refactor: SSE just refreshes sparkline/chart from server, no DOM stat patching
This commit is contained in:
parent
749c6f391e
commit
55f9f6d8ed
|
|
@ -265,68 +265,9 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Track running stats for SSE incremental updates
|
// SSE: on each ping, refresh the chart (debounced — at most once per 5s)
|
||||||
let _totalPings = <%= pings.length %>, _upPings = <%= upPings.length %>;
|
|
||||||
let _latencySum = <%= latencies.reduce((a, b) => a + b, 0) %>, _latencyCount = <%= latencies.length %>;
|
|
||||||
|
|
||||||
// SSE: live ping updates (minimal DOM patches only — no chart re-render)
|
|
||||||
watchMonitor(monitorId, (ping) => {
|
|
||||||
// Stats
|
|
||||||
_totalPings++;
|
|
||||||
if (ping.up) _upPings++;
|
|
||||||
if (ping.latency_ms != null) {
|
|
||||||
_latencySum += ping.latency_ms;
|
|
||||||
_latencyCount++;
|
|
||||||
const avg = Math.round(_latencySum / _latencyCount);
|
|
||||||
document.getElementById('stat-latency').textContent = avg + 'ms';
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById('stat-last').innerHTML = timeAgo(ping.checked_at);
|
|
||||||
document.getElementById('stat-status').innerHTML = ping.up
|
|
||||||
? '<span class="text-green-400">Up</span>'
|
|
||||||
: '<span class="text-red-400">Down</span>';
|
|
||||||
document.getElementById('status-dot').innerHTML = ping.up
|
|
||||||
? '<span class="inline-block w-2.5 h-2.5 rounded-full bg-green-400 mr-2" title="Up"></span>'
|
|
||||||
: '<span class="inline-block w-2.5 h-2.5 rounded-full bg-red-400 mr-2" title="Down"></span>';
|
|
||||||
|
|
||||||
if (_totalPings > 0) {
|
|
||||||
const pct = Math.round((_upPings / _totalPings) * 100);
|
|
||||||
document.getElementById('stat-uptime').textContent = pct + '%';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Status history bar — append new segment
|
|
||||||
const bar = document.getElementById('status-bar');
|
|
||||||
if (bar) {
|
|
||||||
const seg = document.createElement('div');
|
|
||||||
seg.className = `flex-1 ${ping.up ? 'bg-green-500/70' : 'bg-red-500/70'}`;
|
|
||||||
seg.title = `${new Date(ping.checked_at).toLocaleString()} — ${ping.up ? 'Up' : 'Down'}${ping.latency_ms ? ' ' + ping.latency_ms + 'ms' : ''}`;
|
|
||||||
bar.prepend(seg);
|
|
||||||
while (bar.children.length > 60) bar.removeChild(bar.lastChild);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh latency chart (debounced)
|
|
||||||
scheduleChartRefresh();
|
|
||||||
|
|
||||||
// Ping table — prepend plain HTML row
|
|
||||||
const tbody = document.getElementById('pings-table');
|
|
||||||
if (tbody) {
|
|
||||||
const tr = document.createElement('tr');
|
|
||||||
tr.className = 'hover:bg-gray-800/50';
|
|
||||||
tr.innerHTML = `
|
|
||||||
<td class="px-4 py-2">${ping.up ? '<span class="text-green-400">Up</span>' : '<span class="text-red-400">Down</span>'}</td>
|
|
||||||
<td class="px-4 py-2 text-gray-300">${ping.status_code ?? '—'}</td>
|
|
||||||
<td class="px-4 py-2 text-gray-300">${ping.latency_ms ? ping.latency_ms + 'ms' : '—'}</td>
|
|
||||||
<td class="px-4 py-2 text-gray-500">${timeAgo(ping.checked_at)}</td>
|
|
||||||
<td class="px-4 py-2 text-red-400/70 text-xs truncate max-w-[200px]">${ping.error ? escapeHtml(ping.error) : ''}</td>
|
|
||||||
`;
|
|
||||||
tbody.prepend(tr);
|
|
||||||
while (tbody.children.length > 100) tbody.removeChild(tbody.lastChild);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Refresh latency chart from server (debounced — at most once per 5s)
|
|
||||||
let _chartRefreshTimer = null;
|
let _chartRefreshTimer = null;
|
||||||
function scheduleChartRefresh() {
|
watchMonitor(monitorId, () => {
|
||||||
if (_chartRefreshTimer) return;
|
if (_chartRefreshTimer) return;
|
||||||
_chartRefreshTimer = setTimeout(async () => {
|
_chartRefreshTimer = setTimeout(async () => {
|
||||||
_chartRefreshTimer = null;
|
_chartRefreshTimer = null;
|
||||||
|
|
@ -335,7 +276,7 @@
|
||||||
if (res.ok) document.getElementById('latency-chart').innerHTML = await res.text();
|
if (res.ok) document.getElementById('latency-chart').innerHTML = await res.text();
|
||||||
} catch {}
|
} catch {}
|
||||||
}, 5000);
|
}, 5000);
|
||||||
}
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<%~ include('./partials/foot') %>
|
<%~ include('./partials/foot') %>
|
||||||
|
|
|
||||||
|
|
@ -88,41 +88,14 @@
|
||||||
}, 5000);
|
}, 5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// SSE: subscribe to all monitors for live updates
|
// SSE: subscribe to all monitors, refresh sparkline on each ping
|
||||||
const sseConnections = [];
|
document.querySelectorAll('[data-monitor-id]').forEach(card => {
|
||||||
function subscribeAll() {
|
const mid = card.dataset.monitorId;
|
||||||
const monitorCards = document.querySelectorAll('[data-monitor-id]');
|
watchMonitor(mid, () => {
|
||||||
monitorCards.forEach(card => {
|
const sparkEl = card.querySelector('.stat-sparkline');
|
||||||
const mid = card.dataset.monitorId;
|
if (sparkEl) scheduleSparklineRefresh(mid, sparkEl);
|
||||||
const es = watchMonitor(mid, (ping) => {
|
|
||||||
// Status dot
|
|
||||||
const statusDot = card.querySelector('.status-dot');
|
|
||||||
if (statusDot) {
|
|
||||||
const wasUp = statusDot.classList.contains('bg-green-500');
|
|
||||||
statusDot.className = `status-dot w-2.5 h-2.5 rounded-full ${ping.up ? 'bg-green-500' : 'bg-red-500'}`;
|
|
||||||
if (wasUp !== ping.up) {
|
|
||||||
const dots = document.querySelectorAll('.status-dot');
|
|
||||||
const upNow = [...dots].filter(d => d.classList.contains('bg-green-500')).length;
|
|
||||||
const downNow = [...dots].filter(d => d.classList.contains('bg-red-500')).length;
|
|
||||||
const summary = document.getElementById('summary');
|
|
||||||
if (summary) summary.innerHTML = `<span class="text-green-400">${upNow} up</span> · <span class="${downNow > 0 ? 'text-red-400' : 'text-gray-500'}">${downNow} down</span> · ${dots.length} total`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Latency text
|
|
||||||
if (ping.latency_ms) card.querySelector('.stat-latency').textContent = `${ping.latency_ms}ms`;
|
|
||||||
|
|
||||||
// Timestamp
|
|
||||||
card.querySelector('.stat-last').innerHTML = timeAgo(ping.checked_at);
|
|
||||||
|
|
||||||
// Sparkline — debounced fetch from server
|
|
||||||
const sparkEl = card.querySelector('.stat-sparkline');
|
|
||||||
if (sparkEl) scheduleSparklineRefresh(mid, sparkEl);
|
|
||||||
});
|
|
||||||
if (es) sseConnections.push(es);
|
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
subscribeAll();
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<%~ include('./partials/foot') %>
|
<%~ include('./partials/foot') %>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue