diff --git a/frontend/assets/js/app.js b/frontend/assets/js/app.js index db58a00..54e4df1 100644 --- a/frontend/assets/js/app.js +++ b/frontend/assets/js/app.js @@ -39,6 +39,53 @@ let syncInterval = null; let syncHashes = { events: null, documents: null, network: null }; let pendingCanvasCreate = null; +function startSync() { + if (syncInterval) return; + syncInterval = setInterval(async () => { + if (dragType || dragTarget || isPanning) return; + const openModals = document.querySelectorAll('.modal.show'); + if (openModals.length > 0) return; + try { + const [eventsData, docsData, nodesData, linksData, shapesData] = await Promise.all([ + apiFetch('events'), + apiFetch('documents'), + apiFetch('nodes'), + apiFetch('links'), + apiFetch('shapes'), + ]); + + const eventsHash = JSON.stringify(eventsData); + if (eventsHash !== syncHashes.events) { + syncHashes.events = eventsHash; + events = Array.isArray(eventsData) ? eventsData : []; + loadAllTags(); + renderTimeline(); + } + + const docsHash = JSON.stringify(docsData); + if (docsHash !== syncHashes.documents) { + syncHashes.documents = docsHash; + documents = Array.isArray(docsData) ? docsData : []; + renderDocuments(); + } + + const netHash = JSON.stringify({ n: nodesData, l: linksData, s: shapesData }); + if (netHash !== syncHashes.network) { + syncHashes.network = netHash; + nodes = Array.isArray(nodesData) ? nodesData : []; + links = Array.isArray(linksData) ? linksData : []; + shapes = Array.isArray(shapesData) ? shapesData : []; + populateNodeSelects(); + renderNodeList(); + renderShapeList(); + if (shapes.length) nextShapeZ = Math.max(...shapes.map(x => x.z_index)) + 1; + buildCanvasGraph(); + renderNetwork(); + } + } catch (e) {} + }, 5000); +} + const DOC_TYPE_ICONS = { deployment: { icon: 'fa-server', color: '#06b6d4' }, attack: { icon: 'fa-bolt', color: '#ef4444' },