()
| 7714 | const sev = (document.getElementById('watchtower-minsev') || {}).value || ''; |
| 7715 | const q = '/api/net/watchtower?limit=100' + (sev ? '&min_severity=' + encodeURIComponent(sev) : ''); |
| 7716 | try { renderWatchtower(await fetchAPI(q)); } catch (e) { /* offline — leave as-is */ } |
| 7717 | refreshIncidents('watchtower-incidents'); |
| 7718 | } |
| 7719 | async function watchtowerClear() { |
| 7720 | if (!confirm('Clear the Watchtower alerts shown here?\n\nThe watchers keep their place in each log, so only genuinely new findings will re-appear.')) return; |
| 7721 | try { |
| 7722 | await postAPI('/api/net/watchtower/clear', {}); |
| 7723 | addConsoleMessage('Watchtower alerts cleared', 'info'); |
| 7724 | } catch (err) { |
| 7725 | addConsoleMessage('Failed to clear Watchtower: ' + err.message, 'error'); |
| 7726 | } |
| 7727 | // Refresh both views — the button lives on the Diagnostics pane and the |
| 7728 | // Dashboard summary card. Each refresher re-pulls its own incident rows too, |
| 7729 | // so the fused view empties alongside the raw alerts. |
| 7730 | watchtowerRefresh(); |
| 7731 | if (typeof refreshDashWatchtower === 'function') refreshDashWatchtower(); |
| 7732 | } |
| 7733 | async function syncWatchtowerFromServer() { |
| 7734 | const cb = document.getElementById('watchtower-enabled'); |
| 7735 | try { |
| 7736 | const d = await fetchAPI('/api/net/watchtower?limit=100&min_severity=high'); |
| 7737 | if (cb) cb.checked = !!d.enabled; |
| 7738 | renderWatchtower(d); |
| 7739 | } catch (e) { /* offline */ } |
| 7740 | refreshIncidents('watchtower-incidents'); |
| 7741 | } |
| 7742 | function onWatchtowerToggled(cb) { |
| 7743 | postAPI('/api/config', { watchtower_enabled: cb.checked }) |
| 7744 | .then(() => { |
| 7745 | addConsoleMessage('Watchtower unified alerts ' + (cb.checked ? 'ON' : 'OFF'), 'info'); |
| 7746 | if (cb.checked) setTimeout(watchtowerRefresh, 300); else renderWatchtower({ success: true, enabled: false }); |
| 7747 | }) |
| 7748 | .catch(err => { |
| 7749 | addConsoleMessage('Failed to toggle Watchtower: ' + err.message, 'error'); |
| 7750 | cb.checked = !cb.checked; |
| 7751 | }); |
| 7752 | } |
| 7753 | |
| 7754 | // ---- Watchtower: Dashboard summary card ----------------------------------- |
| 7755 | // Compact mirror of the Diagnostics pane: severity chips + the newest findings, |
| 7756 | // so an active attack is visible on the landing tab without digging. |
| 7757 | function renderDashWatchtower(d) { |
| 7758 | const chipsEl = document.getElementById('dash-watchtower-chips'); |
| 7759 | const listEl = document.getElementById('dash-watchtower-list'); |
| 7760 | const srcEl = document.getElementById('dash-watchtower-sources'); |
| 7761 | if (!chipsEl || !listEl) return; |
| 7762 | if (!d || d.success === false) return; |
| 7763 | if (!d.enabled) { |
| 7764 | chipsEl.innerHTML = '<span class="text-xs text-gray-500">Watchtower is off — enable it in Diagnostics to aggregate the watcher daemons here.</span>'; |
nothing calls this directly
no test coverage detected