From 3dc0757b4a4352d163f98080ce5bb53e2908ee0b Mon Sep 17 00:00:00 2001 From: nate Date: Sat, 28 Mar 2026 16:17:15 +0400 Subject: [PATCH] attempt fix: cont --- apps/web/src/routes/dashboard.ts | 4 +-- apps/web/src/utils/sparkline.ts | 47 +++++++++++++++++++----------- apps/web/src/views/home.ejs | 49 +++++++++++++------------------- 3 files changed, 52 insertions(+), 48 deletions(-) diff --git a/apps/web/src/routes/dashboard.ts b/apps/web/src/routes/dashboard.ts index 8ffc0ce..2402ecb 100644 --- a/apps/web/src/routes/dashboard.ts +++ b/apps/web/src/routes/dashboard.ts @@ -3,7 +3,7 @@ import { Eta } from "eta"; import { resolve } from "path"; import { resolveKey } from "./auth"; import sql from "../db"; -import { sparkline, sparklineFromPings } from "../utils/sparkline"; +import { sparkline, sparklineFromPings, pickBestRegion } from "../utils/sparkline"; import { createHash } from "crypto"; // Generate a cache-buster hash from the CSS file content at startup @@ -138,7 +138,7 @@ function escapeHtmlSSR(str: string): string { } export function html(template: string, data: Record = {}) { - return new Response(eta.render(template, { ...data, timeAgoSSR, sparklineSSR, latencyChartSSR, escapeHtmlSSR, cssHash, jsHash }), { + return new Response(eta.render(template, { ...data, timeAgoSSR, sparklineSSR, pickBestRegion, latencyChartSSR, escapeHtmlSSR, cssHash, jsHash }), { headers: { "content-type": "text/html; charset=utf-8" }, }); } diff --git a/apps/web/src/utils/sparkline.ts b/apps/web/src/utils/sparkline.ts index ed9f182..4f6b3d4 100644 --- a/apps/web/src/utils/sparkline.ts +++ b/apps/web/src/utils/sparkline.ts @@ -12,33 +12,48 @@ export function sparkline(values: number[], width = 120, height = 32, color = '# return ``; } -// Given pings with region+latency, pick the region with the lowest avg latency -// and return its sparkline in that region's color. -export function sparklineFromPings(pings: Array<{latency_ms?: number|null, region?: string|null}>, width = 120, height = 32): string { - const COLORS: Record = { - 'eu-central': '#3b82f6', - 'us-west': '#f59e0b', - }; +const REGION_COLORS: Record = { + 'eu-central': '#3b82f6', + 'us-west': '#f59e0b', +}; - // Group by region +// Pick the best region: the one with the lowest avg latency across its last 3 pings. +// Only considers regions that have at least one ping in the most recent 3 pings overall, +// so stale regions that haven't reported recently are excluded. +export function pickBestRegion(pings: Array<{latency_ms?: number|null, region?: string|null}>): { region: string, values: number[], latest: number | null } { + const withLatency = pings.filter(p => p.latency_ms != null); + if (!withLatency.length) return { region: '__none__', values: [], latest: null }; + + // Group all pings by region const byRegion: Record = {}; - for (const p of pings) { - if (p.latency_ms == null) continue; + for (const p of withLatency) { const key = p.region || '__none__'; if (!byRegion[key]) byRegion[key] = []; - byRegion[key].push(p.latency_ms); + byRegion[key].push(p.latency_ms!); } - if (!Object.keys(byRegion).length) return ''; + // Only consider regions that appear in the 3 most recent pings + const recentRegions = new Set( + withLatency.slice(-3).map(p => p.region || '__none__') + ); - // Pick region with lowest average latency let bestRegion = '__none__'; let bestAvg = Infinity; for (const [region, vals] of Object.entries(byRegion)) { - const avg = vals.reduce((a, b) => a + b, 0) / vals.length; + if (!recentRegions.has(region)) continue; + const recent = vals.slice(-3); + const avg = recent.reduce((a, b) => a + b, 0) / recent.length; if (avg < bestAvg) { bestAvg = avg; bestRegion = region; } } - const color = COLORS[bestRegion] || '#60a5fa'; - return sparkline(byRegion[bestRegion], width, height, color, bestRegion); + const values = byRegion[bestRegion] || []; + return { region: bestRegion, values, latest: values.length ? values[values.length - 1] : null }; +} + +// Given pings with region+latency, pick the best region and render its sparkline. +export function sparklineFromPings(pings: Array<{latency_ms?: number|null, region?: string|null}>, width = 120, height = 32): string { + const { region, values } = pickBestRegion(pings); + if (!values.length) return ''; + const color = REGION_COLORS[region] || '#60a5fa'; + return sparkline(values, width, height, color, region); } diff --git a/apps/web/src/views/home.ejs b/apps/web/src/views/home.ejs index 61ec28c..5dd7e53 100644 --- a/apps/web/src/views/home.ejs +++ b/apps/web/src/views/home.ejs @@ -28,22 +28,9 @@ <% } else { %> <% it.monitors.forEach(function(m) { - const latencies = (m.pings || []).filter(p => p.latency_ms != null); - // Pick the region with the lowest avg latency (same logic as sparklineFromPings) - // Pick best region from the 3 most recent pings overall (not per region) - const recentPings = latencies.slice(-3); - const recentRegions = {}; - for (const p of recentPings) { - const key = p.region || '__none__'; - if (!recentRegions[key]) recentRegions[key] = []; - recentRegions[key].push(p.latency_ms); - } - let bestRegion = '__none__', bestAvg = Infinity; - for (const [region, vals] of Object.entries(recentRegions)) { - const avg = vals.reduce((a, b) => a + b, 0) / vals.length; - if (avg < bestAvg) { bestAvg = avg; bestRegion = region; } - } - const avgLatency = latencies.length ? latencies[latencies.length - 1].latency_ms : null; + const pingsForSparkline = (m.pings || []).filter(p => p.latency_ms != null); + const best = it.pickBestRegion(pingsForSparkline); + const avgLatency = best.latest; %>
@@ -55,7 +42,7 @@
- +
<%= avgLatency != null ? avgLatency + 'ms' : '—' %>
<%~ m.last_ping ? it.timeAgoSSR(m.last_ping.checked_at) : 'no pings' %>
@@ -113,24 +100,26 @@ function getBestRegion(monitorId) { const regions = sparkData[monitorId] || {}; - // Collect all pings with timestamps to find the 3 most recent overall - const all = []; + // Only consider regions that appear in the 3 most recent pings overall + // sparkData stores vals per region in chronological order; build a merged timeline + const timeline = []; for (const [region, vals] of Object.entries(regions)) { - for (let i = 0; i < vals.length; i++) all.push({ region, val: vals[i], idx: i }); + for (const val of vals) timeline.push({ region, val }); } - // Use the last 3 entries (vals are appended in order) - const recent = all.slice(-3); + if (!timeline.length) return { region: '__none__', latest: null }; + + const recentRegions = new Set(timeline.slice(-3).map(p => p.region)); + let bestRegion = '__none__', bestAvg = Infinity; - const byRegion = {}; - for (const p of recent) { - if (!byRegion[p.region]) byRegion[p.region] = []; - byRegion[p.region].push(p.val); - } - for (const [region, vals] of Object.entries(byRegion)) { - const avg = vals.reduce((a, b) => a + b, 0) / vals.length; + for (const [region, vals] of Object.entries(regions)) { + if (!recentRegions.has(region) || !vals.length) continue; + const recent = vals.slice(-3); + const avg = recent.reduce((a, b) => a + b, 0) / recent.length; if (avg < bestAvg) { bestAvg = avg; bestRegion = region; } } - const latest = all.length ? all[all.length - 1].val : null; + + const vals = regions[bestRegion] || []; + const latest = vals.length ? vals[vals.length - 1] : null; return { region: bestRegion, latest }; }