attempt fix: cont
This commit is contained in:
parent
8791cdc509
commit
3dc0757b4a
|
|
@ -3,7 +3,7 @@ import { Eta } from "eta";
|
||||||
import { resolve } from "path";
|
import { resolve } from "path";
|
||||||
import { resolveKey } from "./auth";
|
import { resolveKey } from "./auth";
|
||||||
import sql from "../db";
|
import sql from "../db";
|
||||||
import { sparkline, sparklineFromPings } from "../utils/sparkline";
|
import { sparkline, sparklineFromPings, pickBestRegion } from "../utils/sparkline";
|
||||||
import { createHash } from "crypto";
|
import { createHash } from "crypto";
|
||||||
|
|
||||||
// Generate a cache-buster hash from the CSS file content at startup
|
// 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<string, unknown> = {}) {
|
export function html(template: string, data: Record<string, unknown> = {}) {
|
||||||
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" },
|
headers: { "content-type": "text/html; charset=utf-8" },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,33 +12,48 @@ export function sparkline(values: number[], width = 120, height = 32, color = '#
|
||||||
return `<svg width="${width}" height="${height}" class="inline-block" data-vals="${values.join(',')}" data-region="${region}"><polyline points="${points}" fill="none" stroke="${color}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
|
return `<svg width="${width}" height="${height}" class="inline-block" data-vals="${values.join(',')}" data-region="${region}"><polyline points="${points}" fill="none" stroke="${color}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Given pings with region+latency, pick the region with the lowest avg latency
|
const REGION_COLORS: Record<string, string> = {
|
||||||
// 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<string, string> = {
|
|
||||||
'eu-central': '#3b82f6',
|
'eu-central': '#3b82f6',
|
||||||
'us-west': '#f59e0b',
|
'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<string, number[]> = {};
|
const byRegion: Record<string, number[]> = {};
|
||||||
for (const p of pings) {
|
for (const p of withLatency) {
|
||||||
if (p.latency_ms == null) continue;
|
|
||||||
const key = p.region || '__none__';
|
const key = p.region || '__none__';
|
||||||
if (!byRegion[key]) byRegion[key] = [];
|
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 bestRegion = '__none__';
|
||||||
let bestAvg = Infinity;
|
let bestAvg = Infinity;
|
||||||
for (const [region, vals] of Object.entries(byRegion)) {
|
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; }
|
if (avg < bestAvg) { bestAvg = avg; bestRegion = region; }
|
||||||
}
|
}
|
||||||
|
|
||||||
const color = COLORS[bestRegion] || '#60a5fa';
|
const values = byRegion[bestRegion] || [];
|
||||||
return sparkline(byRegion[bestRegion], width, height, color, 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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,22 +28,9 @@
|
||||||
</div>
|
</div>
|
||||||
<% } else { %>
|
<% } else { %>
|
||||||
<% it.monitors.forEach(function(m) {
|
<% it.monitors.forEach(function(m) {
|
||||||
const latencies = (m.pings || []).filter(p => p.latency_ms != null);
|
const pingsForSparkline = (m.pings || []).filter(p => p.latency_ms != null);
|
||||||
// Pick the region with the lowest avg latency (same logic as sparklineFromPings)
|
const best = it.pickBestRegion(pingsForSparkline);
|
||||||
// Pick best region from the 3 most recent pings overall (not per region)
|
const avgLatency = best.latest;
|
||||||
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;
|
|
||||||
%>
|
%>
|
||||||
<a href="/dashboard/monitors/<%= m.id %>" data-monitor-id="<%= m.id %>" class="block monitor-card rounded-xl p-4 group">
|
<a href="/dashboard/monitors/<%= m.id %>" data-monitor-id="<%= m.id %>" class="block monitor-card rounded-xl p-4 group">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
|
|
@ -55,7 +42,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-6 shrink-0 ml-4">
|
<div class="flex items-center gap-6 shrink-0 ml-4">
|
||||||
<div class="hidden sm:block stat-sparkline" style="width:120px;height:32px"><%~ it.sparklineSSR(latencies) %></div>
|
<div class="hidden sm:block stat-sparkline" style="width:120px;height:32px"><%~ it.sparklineSSR(pingsForSparkline) %></div>
|
||||||
<div class="text-right" style="width:72px">
|
<div class="text-right" style="width:72px">
|
||||||
<div class="text-sm text-gray-300 stat-latency"><%= avgLatency != null ? avgLatency + 'ms' : '—' %></div>
|
<div class="text-sm text-gray-300 stat-latency"><%= avgLatency != null ? avgLatency + 'ms' : '—' %></div>
|
||||||
<div class="text-xs text-gray-500 stat-last"><%~ m.last_ping ? it.timeAgoSSR(m.last_ping.checked_at) : 'no pings' %></div>
|
<div class="text-xs text-gray-500 stat-last"><%~ m.last_ping ? it.timeAgoSSR(m.last_ping.checked_at) : 'no pings' %></div>
|
||||||
|
|
@ -113,24 +100,26 @@
|
||||||
|
|
||||||
function getBestRegion(monitorId) {
|
function getBestRegion(monitorId) {
|
||||||
const regions = sparkData[monitorId] || {};
|
const regions = sparkData[monitorId] || {};
|
||||||
// Collect all pings with timestamps to find the 3 most recent overall
|
// Only consider regions that appear in the 3 most recent pings overall
|
||||||
const all = [];
|
// sparkData stores vals per region in chronological order; build a merged timeline
|
||||||
|
const timeline = [];
|
||||||
for (const [region, vals] of Object.entries(regions)) {
|
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)
|
if (!timeline.length) return { region: '__none__', latest: null };
|
||||||
const recent = all.slice(-3);
|
|
||||||
|
const recentRegions = new Set(timeline.slice(-3).map(p => p.region));
|
||||||
|
|
||||||
let bestRegion = '__none__', bestAvg = Infinity;
|
let bestRegion = '__none__', bestAvg = Infinity;
|
||||||
const byRegion = {};
|
for (const [region, vals] of Object.entries(regions)) {
|
||||||
for (const p of recent) {
|
if (!recentRegions.has(region) || !vals.length) continue;
|
||||||
if (!byRegion[p.region]) byRegion[p.region] = [];
|
const recent = vals.slice(-3);
|
||||||
byRegion[p.region].push(p.val);
|
const avg = recent.reduce((a, b) => a + b, 0) / recent.length;
|
||||||
}
|
|
||||||
for (const [region, vals] of Object.entries(byRegion)) {
|
|
||||||
const avg = vals.reduce((a, b) => a + b, 0) / vals.length;
|
|
||||||
if (avg < bestAvg) { bestAvg = avg; bestRegion = region; }
|
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 };
|
return { region: bestRegion, latest };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue