diff --git a/src/fgai/dashboard.py b/src/fgai/dashboard.py index 5ca2682..35e5393 100644 --- a/src/fgai/dashboard.py +++ b/src/fgai/dashboard.py @@ -276,9 +276,17 @@ function drawCorrelationGraph(correlations) { }); ctx.textAlign='left'; ctx.fillStyle='#91abc4'; ctx.font='12px Arial'; ctx.fillText('Top correlated entities and streams. Red links/entities include security-event activity; circle size follows event volume.', 8, ch-14); } +function showRefreshError(error) { + const message = error && error.stack ? error.stack : String(error || 'unknown refresh error'); + console.error(error); + document.getElementById('stamp').textContent = 'Dashboard render error'; + document.getElementById('liveStatus').innerHTML = `Dashboard render error:
${esc(message)}`; +} async function refresh() { + try { const openDetails = new Set([...document.querySelectorAll('details[open][data-detail-id]')].map(item => item.dataset.detailId)); const res = await fetch('/api/status', {cache: 'no-store'}); + if (!res.ok) throw new Error(`GET /api/status failed: ${res.status} ${res.statusText}`); const data = await res.json(); const s = data.summary || {}; const a = data.anomaly_summary || {}; @@ -484,6 +492,9 @@ async function refresh() { document.querySelectorAll('details[data-detail-id]').forEach(item => { if (openDetails.has(item.dataset.detailId)) item.open = true; }); document.querySelectorAll('[data-sort-table]').forEach(button => button.addEventListener('click', () => { const current=tableSort[button.dataset.sortTable]; tableSort[button.dataset.sortTable]={key:button.dataset.sortKey,direction:current && current.key===button.dataset.sortKey ? -current.direction : 1}; refresh(); })); document.querySelectorAll('.apply-suggested-profile').forEach(button => button.addEventListener('click', () => applySuggestedProfile(button.dataset.streamId))); + } catch (error) { + showRefreshError(error); + } } async function loadSettings() { const config = await (await fetch('/api/config', {cache: 'no-store'})).json();