diff --git a/apps/status/src/data.ts b/apps/status/src/data.ts index f8a27f0..10cfb9c 100644 --- a/apps/status/src/data.ts +++ b/apps/status/src/data.ts @@ -125,15 +125,14 @@ export async function loadMonitors(pageId: string, window: Window): Promise = {}; + // Index actual rollup data by (monitor_id, isoBucketStart) so we can fill in + // the missing slots below. + const indexed: Record> = {}; const latencyByMonitor: Record = {}; for (const r of rollupRows) { - if (!bucketsByMonitor[r.monitor_id]) bucketsByMonitor[r.monitor_id] = []; - bucketsByMonitor[r.monitor_id]!.push({ - start: r.bucket_start instanceof Date ? r.bucket_start.toISOString() : String(r.bucket_start), - total: r.total, - up: r.up_count, - }); + const startIso = r.bucket_start instanceof Date ? r.bucket_start.toISOString() : String(r.bucket_start); + if (!indexed[r.monitor_id]) indexed[r.monitor_id] = {}; + indexed[r.monitor_id]![startIso] = { total: r.total, up: r.up_count, avg_latency: r.avg_latency ?? null }; if (r.avg_latency != null) { const acc = latencyByMonitor[r.monitor_id] ?? { sum: 0, n: 0 }; acc.sum += r.avg_latency * r.total; @@ -142,6 +141,35 @@ export async function loadMonitors(pageId: string, window: Window): Promise { + const t = new Date(d); + if (bucket === "hourly") { t.setUTCMinutes(0, 0, 0); } + else { t.setUTCHours(0, 0, 0, 0); } + if (bucket === "weekly") { + // ISO week starts Monday. + const day = (t.getUTCDay() + 6) % 7; + t.setUTCDate(t.getUTCDate() - day); + } + return t; + }; + const nowTrunc = truncate(new Date()).getTime(); + const slotIsos: string[] = []; + for (let i = count - 1; i >= 0; i--) { + slotIsos.push(new Date(nowTrunc - i * bucketMs).toISOString()); + } + const bucketsByMonitor: Record = {}; + for (const id of ids) { + const slotMap = indexed[id] ?? {}; + bucketsByMonitor[id] = slotIsos.map((iso) => { + const hit = slotMap[iso]; + return hit ? { start: iso, total: hit.total, up: hit.up } : { start: iso, total: 0, up: 0 }; + }); + } + // Step 4: tiny recent latency history for the sparkline (last 30 hourly buckets). const latRows = await sql` SELECT monitor_id, region, bucket_start, avg_latency diff --git a/apps/status/src/views/page.ejs b/apps/status/src/views/page.ejs index 52c128d..359d5fc 100644 --- a/apps/status/src/views/page.ejs +++ b/apps/status/src/views/page.ejs @@ -24,15 +24,15 @@ return 'Unknown'; } function statusColor(s) { - if (s === 'up') return '#10b981'; - if (s === 'down') return '#ef4444'; - return '#9ca3af'; + if (s === 'up') return 'var(--bar-up)'; + if (s === 'down') return 'var(--bar-down)'; + return 'var(--muted)'; } function bucketColor(b) { - if (b.total === 0) return '#374151'; - if (b.up === b.total) return '#10b981'; - if (b.up === 0) return '#ef4444'; - return '#f59e0b'; + if (!b || b.total === 0) return 'var(--bar-empty)'; + if (b.up === b.total) return 'var(--bar-up)'; + if (b.up === 0) return 'var(--bar-down)'; + return 'var(--bar-partial)'; } // Overall status: down if any monitor is down, degraded if any partial, else up. @@ -45,9 +45,9 @@ const overallText = overall === 'up' ? 'All systems operational' : overall === 'degraded' ? 'Some systems degraded' : 'Major outage in progress'; - const overallColor = overall === 'up' ? '#10b981' - : overall === 'degraded' ? '#f59e0b' - : '#ef4444'; + const overallColor = overall === 'up' ? 'var(--bar-up)' + : overall === 'degraded' ? 'var(--bar-partial)' + : 'var(--bar-down)'; %> @@ -63,27 +63,36 @@