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
|
// Docs
|
||||||
.get("/docs", () => Bun.file(`${dashDir}/docs.html`));
|
.get("/docs", () => Bun.file(`${dashDir}/docs.html`));
|
||||||
|
|
|
||||||
|
|
@ -304,6 +304,9 @@
|
||||||
while (bar.children.length > 60) bar.removeChild(bar.lastChild);
|
while (bar.children.length > 60) bar.removeChild(bar.lastChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Refresh latency chart (debounced)
|
||||||
|
scheduleChartRefresh();
|
||||||
|
|
||||||
// Ping table — prepend plain HTML row
|
// Ping table — prepend plain HTML row
|
||||||
const tbody = document.getElementById('pings-table');
|
const tbody = document.getElementById('pings-table');
|
||||||
if (tbody) {
|
if (tbody) {
|
||||||
|
|
@ -321,16 +324,18 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Poll every 30s to refresh the latency chart from server
|
// Refresh latency chart from server (debounced — at most once per 5s)
|
||||||
setInterval(async () => {
|
let _chartRefreshTimer = null;
|
||||||
|
function scheduleChartRefresh() {
|
||||||
|
if (_chartRefreshTimer) return;
|
||||||
|
_chartRefreshTimer = setTimeout(async () => {
|
||||||
|
_chartRefreshTimer = null;
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/dashboard/monitors/${monitorId}/chart`, { credentials: 'same-origin' });
|
const res = await fetch(`/dashboard/monitors/${monitorId}/chart`, { credentials: 'same-origin' });
|
||||||
if (res.ok) {
|
if (res.ok) document.getElementById('latency-chart').innerHTML = await res.text();
|
||||||
const html = await res.text();
|
|
||||||
document.getElementById('latency-chart').innerHTML = html;
|
|
||||||
}
|
|
||||||
} catch {}
|
} catch {}
|
||||||
}, 30000);
|
}, 5000);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<%~ include('./partials/foot') %>
|
<%~ include('./partials/foot') %>
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,20 @@
|
||||||
} catch {}
|
} catch {}
|
||||||
}, 30000);
|
}, 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 = [];
|
const sseConnections = [];
|
||||||
function subscribeAll() {
|
function subscribeAll() {
|
||||||
const monitorCards = document.querySelectorAll('[data-monitor-id]');
|
const monitorCards = document.querySelectorAll('[data-monitor-id]');
|
||||||
|
|
@ -101,6 +114,10 @@
|
||||||
|
|
||||||
// Timestamp
|
// Timestamp
|
||||||
card.querySelector('.stat-last').innerHTML = timeAgo(ping.checked_at);
|
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);
|
if (es) sseConnections.push(es);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue