fix: SSE-driven chart/sparkline refresh, debounced server-side partials
This commit is contained in:
parent
2f7273604b
commit
5071e340c7
|
|
@ -196,5 +196,26 @@ export const dashboard = new Elysia()
|
|||
});
|
||||
})
|
||||
|
||||
// Sparkline partial — returns just the SVG for one monitor
|
||||
.get("/dashboard/monitors/:id/sparkline", async ({ cookie, headers, params }) => {
|
||||
const accountId = await getAccountId(cookie, headers);
|
||||
if (!accountId) return new Response("Unauthorized", { status: 401 });
|
||||
|
||||
const [monitor] = await sql`
|
||||
SELECT id FROM monitors WHERE id = ${params.id} AND account_id = ${accountId}
|
||||
`;
|
||||
if (!monitor) return new Response("Not found", { status: 404 });
|
||||
|
||||
const pings = await sql`
|
||||
SELECT latency_ms FROM pings
|
||||
WHERE monitor_id = ${params.id} AND latency_ms IS NOT NULL
|
||||
ORDER BY checked_at DESC LIMIT 20
|
||||
`;
|
||||
const latencies = pings.map((p: any) => p.latency_ms).reverse();
|
||||
return new Response(sparklineSSR(latencies), {
|
||||
headers: { "content-type": "text/html; charset=utf-8" },
|
||||
});
|
||||
})
|
||||
|
||||
// Docs
|
||||
.get("/docs", () => Bun.file(`${dashDir}/docs.html`));
|
||||
|
|
|
|||
|
|
@ -304,6 +304,9 @@
|
|||
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) {
|
||||
|
|
@ -321,16 +324,18 @@
|
|||
}
|
||||
});
|
||||
|
||||
// Poll every 30s to refresh the latency chart from server
|
||||
setInterval(async () => {
|
||||
try {
|
||||
const res = await fetch(`/dashboard/monitors/${monitorId}/chart`, { credentials: 'same-origin' });
|
||||
if (res.ok) {
|
||||
const html = await res.text();
|
||||
document.getElementById('latency-chart').innerHTML = html;
|
||||
}
|
||||
} catch {}
|
||||
}, 30000);
|
||||
// Refresh latency chart from server (debounced — at most once per 5s)
|
||||
let _chartRefreshTimer = null;
|
||||
function scheduleChartRefresh() {
|
||||
if (_chartRefreshTimer) return;
|
||||
_chartRefreshTimer = setTimeout(async () => {
|
||||
_chartRefreshTimer = null;
|
||||
try {
|
||||
const res = await fetch(`/dashboard/monitors/${monitorId}/chart`, { credentials: 'same-origin' });
|
||||
if (res.ok) document.getElementById('latency-chart').innerHTML = await res.text();
|
||||
} catch {}
|
||||
}, 5000);
|
||||
}
|
||||
</script>
|
||||
|
||||
<%~ include('./partials/foot') %>
|
||||
|
|
|
|||
|
|
@ -75,7 +75,20 @@
|
|||
} catch {}
|
||||
}, 30000);
|
||||
|
||||
// SSE: subscribe to all monitors for live updates (minimal DOM patches only)
|
||||
// Sparkline refresh — debounced per monitor (at most once per 5s)
|
||||
const _sparklineTimers = {};
|
||||
function scheduleSparklineRefresh(mid, sparkEl) {
|
||||
if (_sparklineTimers[mid]) return;
|
||||
_sparklineTimers[mid] = setTimeout(async () => {
|
||||
delete _sparklineTimers[mid];
|
||||
try {
|
||||
const res = await fetch(`/dashboard/monitors/${mid}/sparkline`, { credentials: 'same-origin' });
|
||||
if (res.ok) sparkEl.innerHTML = await res.text();
|
||||
} catch {}
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
// SSE: subscribe to all monitors for live updates
|
||||
const sseConnections = [];
|
||||
function subscribeAll() {
|
||||
const monitorCards = document.querySelectorAll('[data-monitor-id]');
|
||||
|
|
@ -101,6 +114,10 @@
|
|||
|
||||
// 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);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue