consolidation

This commit is contained in:
overcuriousity 2025-07-25 13:37:52 +02:00
parent bd7f93167c
commit bb26d9a80d
6 changed files with 176 additions and 169 deletions

View File

@ -145,14 +145,6 @@ const sortedTags = Object.entries(tagFrequency)
let selectedPhase = ''; let selectedPhase = '';
let isTagCloudExpanded = false; let isTagCloudExpanded = false;
// Check authentication status and show/hide AI button
async function initAIButton() {
await showIfAuthenticated('#ai-view-toggle');
}
// Call auth check on page load
initAIButton();
// Initialize tag cloud state // Initialize tag cloud state
function initTagCloud() { function initTagCloud() {
const visibleCount = 22; const visibleCount = 22;

View File

@ -832,5 +832,4 @@ domains.forEach((domain: any) => {
} }
} }
}); });
setupContributionButtonAuth();
</script> </script>

6
src/env.d.ts vendored
View File

@ -23,6 +23,12 @@ declare global {
createToolSlug: (toolName: string) => string; createToolSlug: (toolName: string) => string;
findToolByIdentifier: (tools: any[], identifier: string) => any | undefined; findToolByIdentifier: (tools: any[], identifier: string) => any | undefined;
isToolHosted: (tool: any) => boolean; isToolHosted: (tool: any) => boolean;
// CONSOLIDATED: Auth utility functions (now in BaseLayout)
checkClientAuth: () => Promise<{authenticated: boolean; authRequired: boolean; expires?: string}>;
requireClientAuth: (callback?: () => void, returnUrl?: string) => Promise<boolean>;
showIfAuthenticated: (selector: string) => Promise<void>;
setupAuthButtons: (selector?: string) => void;
} }
} }

View File

@ -20,18 +20,178 @@ const { title, description = 'CC24-Guide - A comprehensive directory of digital
<title>{title} - CC24-Guide</title> <title>{title} - CC24-Guide</title>
<link rel="icon" type="image/x-icon" href="/favicon.ico"> <link rel="icon" type="image/x-icon" href="/favicon.ico">
<!-- CONSOLIDATED: Load theme script -->
<script src="/src/scripts/theme.js"></script>
<!-- CONSOLIDATED: Load tool utilities (now from enhanced TypeScript file) -->
<script src="/src/utils/toolHelpers.ts"></script>
<!-- CONSOLIDATED: Load client-side auth utilities -->
<script src="/src/scripts/client-auth.js"></script>
<script> <script>
// Initialize theme immediately to prevent flash // Initialize theme immediately to prevent flash
(window as any).themeUtils?.initTheme(); document.addEventListener('DOMContentLoaded', () => {
// Theme management (consolidated from theme.js)
const THEME_KEY = 'dfir-theme';
function getSystemTheme() {
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
function getStoredTheme() {
return localStorage.getItem(THEME_KEY) || 'auto';
}
function applyTheme(theme) {
const effectiveTheme = theme === 'auto' ? getSystemTheme() : theme;
document.documentElement.setAttribute('data-theme', effectiveTheme);
}
function updateThemeToggle(theme) {
document.querySelectorAll('[data-theme-toggle]').forEach(button => {
button.setAttribute('data-current-theme', theme);
});
}
function initTheme() {
const storedTheme = getStoredTheme();
applyTheme(storedTheme);
updateThemeToggle(storedTheme);
}
function toggleTheme() {
const current = getStoredTheme();
const themes = ['light', 'dark', 'auto'];
const currentIndex = themes.indexOf(current);
const nextIndex = (currentIndex + 1) % themes.length;
const nextTheme = themes[nextIndex];
localStorage.setItem(THEME_KEY, nextTheme);
applyTheme(nextTheme);
updateThemeToggle(nextTheme);
}
// Listen for system theme changes
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
if (getStoredTheme() === 'auto') {
applyTheme('auto');
}
});
// Make theme utils available globally
(window as any).themeUtils = {
initTheme,
toggleTheme,
getStoredTheme
};
// Tool helper functions (consolidated from toolHelpers.ts)
function createToolSlug(toolName) {
if (!toolName || typeof toolName !== 'string') {
console.warn('[toolHelpers] Invalid toolName provided to createToolSlug:', toolName);
return '';
}
return toolName.toLowerCase()
.replace(/[^a-z0-9\s-]/g, '') // Remove special characters
.replace(/\s+/g, '-') // Replace spaces with hyphens
.replace(/-+/g, '-') // Remove duplicate hyphens
.replace(/^-|-$/g, ''); // Remove leading/trailing hyphens
}
function findToolByIdentifier(tools, identifier) {
if (!identifier || !Array.isArray(tools)) return undefined;
return tools.find(tool =>
tool.name === identifier ||
createToolSlug(tool.name) === identifier.toLowerCase()
);
}
function isToolHosted(tool) {
return tool.projectUrl !== undefined &&
tool.projectUrl !== null &&
tool.projectUrl !== "" &&
tool.projectUrl.trim() !== "";
}
// Client-side auth functions (consolidated from client-auth.js)
async function checkClientAuth() {
try {
const response = await fetch('/api/auth/status');
const data = await response.json();
return {
authenticated: data.authenticated,
authRequired: data.authRequired,
expires: data.expires
};
} catch (error) {
console.error('Auth check failed:', error);
return {
authenticated: false,
authRequired: true
};
}
}
async function requireClientAuth(callback, returnUrl) {
const authStatus = await checkClientAuth();
if (authStatus.authRequired && !authStatus.authenticated) {
const targetUrl = returnUrl || window.location.href;
window.location.href = `/api/auth/login?returnTo=${encodeURIComponent(targetUrl)}`;
return false;
} else {
if (typeof callback === 'function') {
callback();
}
return true;
}
}
async function showIfAuthenticated(selector) {
const authStatus = await checkClientAuth();
const element = document.querySelector(selector);
if (element) {
element.style.display = (!authStatus.authRequired || authStatus.authenticated)
? 'inline-flex'
: 'none';
}
}
function setupAuthButtons(selector = '[data-contribute-button]') {
// Use event delegation on document for dynamic content support
document.addEventListener('click', async (e) => {
if (!e.target) return;
const button = (e.target as Element).closest(selector);
if (!button) return;
e.preventDefault();
console.log('[AUTH] Contribute button clicked:', button.getAttribute('data-contribute-button'));
await requireClientAuth(() => {
console.log('[AUTH] Navigation approved, redirecting to:', (button as HTMLAnchorElement).href);
window.location.href = (button as HTMLAnchorElement).href;
}, (button as HTMLAnchorElement).href);
});
}
// Make functions available globally
(window as any).createToolSlug = createToolSlug;
(window as any).findToolByIdentifier = findToolByIdentifier;
(window as any).isToolHosted = isToolHosted;
(window as any).checkClientAuth = checkClientAuth;
(window as any).requireClientAuth = requireClientAuth;
(window as any).showIfAuthenticated = showIfAuthenticated;
(window as any).setupAuthButtons = setupAuthButtons;
// Initialize everything
initTheme();
setupAuthButtons('[data-contribute-button]');
// Initialize AI button visibility (moved from ToolFilters.astro to avoid race condition)
const initAIButton = async () => {
await showIfAuthenticated('#ai-view-toggle');
};
initAIButton();
console.log('[CONSOLIDATED] All utilities loaded and initialized');
});
</script> </script>
</head> </head>
<body> <body>

View File

@ -1,86 +0,0 @@
// src/scripts/client-auth.js - CONSOLIDATED client-side auth utilities
// This file REPLACES auth-utils.js and any client-side auth functions
/**
* Check authentication status
*/
async function checkClientAuth() {
try {
const response = await fetch('/api/auth/status');
const data = await response.json();
return {
authenticated: data.authenticated,
authRequired: data.authRequired,
expires: data.expires
};
} catch (error) {
console.error('Auth check failed:', error);
return {
authenticated: false,
authRequired: true
};
}
}
/**
* Redirect to login if not authenticated, otherwise execute callback
*/
async function requireClientAuth(callback, returnUrl) {
const authStatus = await checkClientAuth();
if (authStatus.authRequired && !authStatus.authenticated) {
const targetUrl = returnUrl || window.location.href;
window.location.href = `/api/auth/login?returnTo=${encodeURIComponent(targetUrl)}`;
return false;
} else {
if (typeof callback === 'function') {
callback();
}
return true;
}
}
/**
* Show/hide element based on authentication
*/
async function showIfAuthenticated(selector) {
const authStatus = await checkClientAuth();
const element = document.querySelector(selector);
if (element) {
element.style.display = (!authStatus.authRequired || authStatus.authenticated)
? 'inline-flex'
: 'none';
}
}
function setupAuthButtons(selector = '[data-contribute-button]') {
// Use event delegation on document for dynamic content support
document.addEventListener('click', async (e) => {
const button = e.target.closest(selector);
if (!button) return;
e.preventDefault();
// Enhanced error handling and debugging
console.log('[AUTH] Contribute button clicked:', button.getAttribute('data-contribute-button'));
await requireClientAuth(() => {
console.log('[AUTH] Navigation approved, redirecting to:', button.href);
window.location.href = button.href;
}, button.href);
});
}
// Make functions available globally for dynamic content
window.checkClientAuth = checkClientAuth;
window.requireClientAuth = requireClientAuth;
window.showIfAuthenticated = showIfAuthenticated;
window.setupAuthButtons = setupAuthButtons;
// Auto-setup contribute buttons when DOM is ready
document.addEventListener('DOMContentLoaded', () => {
console.log('[AUTH] Setting up global auth handlers for contribute buttons');
setupAuthButtons('[data-contribute-button]');
});
console.log('Client auth utilities loaded');

View File

@ -1,64 +0,0 @@
// Theme management
const THEME_KEY = 'dfir-theme';
// Get system preference
function getSystemTheme() {
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
// Get stored theme or default to auto
function getStoredTheme() {
return localStorage.getItem(THEME_KEY) || 'auto';
}
// Apply theme to document
function applyTheme(theme) {
const effectiveTheme = theme === 'auto' ? getSystemTheme() : theme;
document.documentElement.setAttribute('data-theme', effectiveTheme);
}
// Update theme toggle button state
function updateThemeToggle(theme) {
document.querySelectorAll('[data-theme-toggle]').forEach(button => {
button.setAttribute('data-current-theme', theme);
});
}
// Initialize theme on page load
function initTheme() {
const storedTheme = getStoredTheme();
applyTheme(storedTheme);
// Update theme toggle buttons immediately
updateThemeToggle(storedTheme);
}
// Handle theme toggle
function toggleTheme() {
const current = getStoredTheme();
const themes = ['light', 'dark', 'auto'];
const currentIndex = themes.indexOf(current);
const nextIndex = (currentIndex + 1) % themes.length;
const nextTheme = themes[nextIndex];
localStorage.setItem(THEME_KEY, nextTheme);
applyTheme(nextTheme);
updateThemeToggle(nextTheme);
}
// Listen for system theme changes
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
if (getStoredTheme() === 'auto') {
applyTheme('auto');
}
});
// Initialize when DOM is ready (for safety)
document.addEventListener('DOMContentLoaded', initTheme);
// Export functions for use in Astro components
window.themeUtils = {
initTheme,
toggleTheme,
getStoredTheme
};