/** * Hooks Dashboard * Real-time monitoring of framework enforcement hooks */ const API_BASE = window.location.hostname === 'localhost' ? 'http://localhost:9000/api' : '/api'; // Load metrics on page load document.addEventListener('DOMContentLoaded', () => { loadMetrics(); // Auto-refresh every 30 seconds setInterval(loadMetrics, 30000); // Manual refresh button document.getElementById('refresh-btn')?.addEventListener('click', loadMetrics); }); /** * Load hook metrics */ async function loadMetrics() { try { const response = await fetch(`${API_BASE}/admin/hooks/metrics`, { headers: { 'Authorization': `Bearer ${localStorage.getItem('admin_token')}` } }); if (!response.ok) { throw new Error('Failed to load metrics'); } const data = await response.json(); displayMetrics(data); } catch (error) { console.error('Error loading metrics:', error); showError('Failed to load hook metrics'); } } /** * Display metrics in UI */ function displayMetrics(data) { const metrics = data.metrics || {}; const executions = metrics.hook_executions || []; const blocks = metrics.blocks || []; const stats = metrics.session_stats || {}; // Calculate totals const totalExecutions = executions.length; const totalBlocks = blocks.length; const blockRate = totalExecutions > 0 ? ((totalBlocks / totalExecutions) * 100).toFixed(1) + '%' : '0%'; // Update quick stats document.getElementById('stat-total-executions').textContent = totalExecutions.toLocaleString(); document.getElementById('stat-total-blocks').textContent = totalBlocks.toLocaleString(); document.getElementById('stat-block-rate').textContent = blockRate; // Last updated const lastUpdated = stats.last_updated ? formatRelativeTime(new Date(stats.last_updated)) : 'Never'; document.getElementById('stat-last-updated').textContent = lastUpdated; // Hook breakdown const editExecutions = executions.filter(e => e.hook === 'validate-file-edit').length; const editBlocks = blocks.filter(b => b.hook === 'validate-file-edit').length; const editSuccessRate = editExecutions > 0 ? (((editExecutions - editBlocks) / editExecutions) * 100).toFixed(1) + '%' : '100%'; document.getElementById('edit-executions').textContent = editExecutions.toLocaleString(); document.getElementById('edit-blocks').textContent = editBlocks.toLocaleString(); document.getElementById('edit-success-rate').textContent = editSuccessRate; const writeExecutions = executions.filter(e => e.hook === 'validate-file-write').length; const writeBlocks = blocks.filter(b => b.hook === 'validate-file-write').length; const writeSuccessRate = writeExecutions > 0 ? (((writeExecutions - writeBlocks) / writeExecutions) * 100).toFixed(1) + '%' : '100%'; document.getElementById('write-executions').textContent = writeExecutions.toLocaleString(); document.getElementById('write-blocks').textContent = writeBlocks.toLocaleString(); document.getElementById('write-success-rate').textContent = writeSuccessRate; // Recent blocks displayRecentBlocks(blocks.slice(-10).reverse()); // Recent activity displayRecentActivity(executions.slice(-20).reverse()); } /** * Display recent blocked operations */ function displayRecentBlocks(blocks) { const container = document.getElementById('recent-blocks'); if (blocks.length === 0) { container.innerHTML = '
No blocked operations
'; return; } const html = blocks.map(block => `
${block.hook.replace('validate-file-', '')} ${formatRelativeTime(new Date(block.timestamp))}

${escapeHtml(block.file)}

${escapeHtml(block.reason)}

`).join(''); container.innerHTML = html; } /** * Display recent hook executions */ function displayRecentActivity(executions) { const container = document.getElementById('recent-activity'); if (executions.length === 0) { container.innerHTML = '
No recent activity
'; return; } const html = executions.map(exec => `
${exec.result === 'passed' ? ` ` : ` `}
${exec.hook.replace('validate-file-', '')} ${formatRelativeTime(new Date(exec.timestamp))}

${escapeHtml(exec.file)}

${exec.reason ? `

${escapeHtml(exec.reason)}

` : ''}
`).join(''); container.innerHTML = html; } /** * Format relative time */ function formatRelativeTime(date) { const now = new Date(); const diffMs = now - date; const diffSec = Math.floor(diffMs / 1000); const diffMin = Math.floor(diffSec / 60); const diffHour = Math.floor(diffMin / 60); const diffDay = Math.floor(diffHour / 24); if (diffSec < 60) { return 'Just now'; } else if (diffMin < 60) { return `${diffMin}m ago`; } else if (diffHour < 24) { return `${diffHour}h ago`; } else if (diffDay < 7) { return `${diffDay}d ago`; } else { return date.toLocaleDateString(); } } /** * Escape HTML to prevent XSS */ function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } /** * Show error message */ function showError(message) { const container = document.getElementById('recent-activity'); container.innerHTML = `

${escapeHtml(message)}

`; // Add event listener to retry button document.getElementById('retry-load-btn')?.addEventListener('click', loadMetrics); }