<?php ?> <script nonce="<?= $cspNonce ?> "> (function(){ var panel = document.getElementById('panel-wd-live'); if(!panel) return; panel.innerHTML = '<div class="wd-toolbar">' + '<button id="wd-live-toggle" class="btn btn-sm btn-primary">Pause</button>' + '<span id="wd-live-count" class="wd-stat">0 events</span>' + '<span id="wd-live-visitors" class="wd-stat">0 active</span>' + '</div>' + '<div id="wd-feed" class="wd-feed"></div>'; var paused = false; var feed = document.getElementById('wd-feed'); var countEl = document.getElementById('wd-live-count'); var visEl = document.getElementById('wd-live-visitors'); var total = 0; document.getElementById('wd-live-toggle').addEventListener('click', function(){ paused = !paused; this.textContent = paused ? 'Resume' : 'Pause'; this.classList.toggle('btn-warning', paused); }); var sevColors = {info:'#4a9',low:'#6bf',medium:'#fa3',high:'#f63',critical:'#f00'}; function addEvent(ev){ if(paused) return; total++; countEl.textContent = total + ' events'; var row = document.createElement('div'); row.className = 'wd-row'; var sev = ev.severity || 'info'; row.innerHTML = '<span class="wd-time">' + new Date(ev.ts||Date.now()).toLocaleTimeString() + '</span>' + '<span class="wd-sev" style="color:'+sevColors[sev]+'">'+sev+'</span>' + '<span class="wd-type">' + (ev.type||'') + '</span>' + '<span class="wd-ip">' + (ev.ip||'') + '</span>' + '<span class="wd-detail">' + (ev.detail||'') + '</span>'; feed.insertBefore(row, feed.firstChild); if(feed.children.length > 200) feed.removeChild(feed.lastChild); } function poll(){ fetch('api/system/pulse.php?scope=watchdog&_t='+Date.now(), {credentials:'same-origin'}) .then(function(r){return r.json()}) .then(function(d){ if(d.events) d.events.forEach(addEvent); if(d.active_visitors !== undefined) visEl.textContent = d.active_visitors + ' active'; }).catch(function(){}); } setInterval(poll, 3000); poll(); })(); </script>