import type { Metadata } from 'next'; import { RouteExplorer } from '@/components/routes/RouteExplorer'; import { apiTry } from '@/lib/api'; import type { Probe, RoutePair, RouteResponse, Target } from '@/lib/types'; export const dynamic = 'force-dynamic'; export const metadata: Metadata = { title: 'Route Explorer', description: 'Probe → ISP → transit → destination: baseline route versus current route, hop by hop, with ASN changes and latency shifts.' }; export default async function RoutesPage({ searchParams }: { searchParams: Promise<{ probe?: string; target?: string }> }) { const sp = await searchParams; const [pairs, probes, targets] = await Promise.all([apiTry<{ pairs: RoutePair[] }>('/api/v1/routes/pairs'), apiTry<{ probes: Probe[] }>('/api/v1/probes'), apiTry<{ targets: Target[] }>('/api/v1/targets?limit=3000')]); const list = pairs?.pairs ?? []; // pre-select: URL → a changed pair → first pair const initialPair = list.find((p) => p.probe_id === sp.probe && p.target_id === sp.target) ?? [...list].sort((a, b) => b.changed_24h - a.changed_24h)[0] ?? null; const initialRoute = initialPair ? await apiTry(`/api/v1/routes?probe=${encodeURIComponent(initialPair.probe_id)}&target=${encodeURIComponent(initialPair.target_id)}`) : null; return (

Path component

Route Explorer

Sampled traceroutes are hashed into route fingerprints. The 7-day dominant fingerprint is the baseline; the current route is compared hop by hop — added or removed hops, ASN path changes and the latency shift they carry.

); }