fix(mesh): probe upstream synchronously in route diagnostic (F-11) (#1100)

GET /api/mesh/aliases/:alias/diagnostic returned a cached state derived
from the last latency/error maps. Those maps only mutated when someone
called POST .../test or when a cross-node connect logged an event, so
once an upstream stopped the diagnostic kept reporting "healthy" until
the 60 s alias-cache refresh pruned the alias entirely.

The GET now calls testUpstream synchronously after the alias-resolved,
opted-in, tunnel-up short-circuits. Probe failures land in routeErrorMap
via logActivity (cross-node already did this; same-node timeout/error
paths now log probe.fail in the same shape), so the state computation
flips to "unreachable" on the same request that exposed the stopped
upstream. A new routeProbeAtMap stamps freshness and surfaces in the
response as lastProbeAt; the route detail sheet renders "Last probe
<age> · <ms>" using the existing formatTimeAgo helper.
This commit is contained in:
Anso
2026-05-18 16:48:09 -04:00
committed by GitHub
parent 7d2b8bee7a
commit c460bb87a8
5 changed files with 241 additions and 5 deletions
@@ -1,5 +1,6 @@
import { useEffect, useState } from 'react';
import { apiFetch } from '@/lib/api';
import { formatTimeAgo } from '@/lib/relativeTime';
import { SystemSheet, SheetSection } from '@/components/ui/system-sheet';
import { Badge } from '@/components/ui/badge';
import { Loader2, Activity, Hash } from 'lucide-react';
@@ -71,7 +72,11 @@ export function MeshRouteDetailSheet({ open, onOpenChange, alias }: Props) {
const footerContext = probe
? (probe.ok ? `Last probe ok · ${probe.latencyMs}ms` : `Last probe failed · ${probe.where ?? 'unknown'}`)
: (diag?.lastProbeMs != null ? `Last probe ${diag.lastProbeMs}ms` : 'No probe run yet');
: diag?.lastProbeMs != null
? (diag.lastProbeAt != null
? `Last probe ${formatTimeAgo(diag.lastProbeAt)} · ${diag.lastProbeMs}ms`
: `Last probe ${diag.lastProbeMs}ms`)
: 'No probe run yet';
return (
<SystemSheet