import type { Metadata } from 'next'; import Link from 'next/link'; import { ConnectorsTable, FreshnessLegend } from '@/components/meta/connectors-table'; import { FreshnessBadge } from '@/components/ui/badges'; import { Container, PageHeader, Section, Stat } from '@/components/ui/section'; import { Unavailable } from '@/components/ui/unavailable'; import { api, safe } from '@/lib/api'; import { fmt1, fmtAgo, fmtDateTime, fmtInt, num } from '@/lib/format'; import { SITE_URL, routes } from '@/lib/site'; export const metadata: Metadata = { title: 'Status — platform health and data freshness', description: 'Live health of the SatelliteIndex API, database, cache and orbit service, and the freshness of every data connector.', alternates: { canonical: `${SITE_URL}/status` }, openGraph: { title: 'Status | SatelliteIndex', description: 'Platform health and data freshness, live.', url: `${SITE_URL}/status` }, twitter: { card: 'summary', title: 'Status | SatelliteIndex', description: 'Platform health and data freshness, live.' }, robots: { index: true, follow: true }, }; export const dynamic = 'force-dynamic'; function ComponentPill({ status }: { status: string }) { const ok = status === 'ok' || status === 'fresh'; const warn = status === 'aging' || status === 'degraded'; const color = ok ? 'var(--active)' : warn ? 'var(--warn)' : 'var(--danger)'; return ( {status} ); } export default async function StatusPage() { const [health, status] = await Promise.all([safe(api.health()), safe(api.sourcesStatus())]); const now = Date.now(); const comps = health?.components ?? {}; const overall = health?.status ?? 'unavailable'; const overallColor = overall === 'ok' ? 'var(--active)' : overall === 'degraded' ? 'var(--warn)' : 'var(--danger)'; return (

Rendered {fmtDateTime(new Date(now).toISOString())} · Refresh Data-only view →

{health === null ? ( ) : ( <>

{overall === 'ok' ? 'Operational' : overall}

version {health.version} · server time {fmtDateTime(health.time)}

Database

{comps.database?.satellites !== undefined ? `${fmtInt(comps.database.satellites as number)} satellites` : 'count unavailable'}

Redis cache

response cache, locks, job queue

Orbit service

{comps.orbit_service?.objects !== undefined ? `${fmtInt(comps.orbit_service.objects as number)} objects in the propagator` : 'object count unavailable'}

Data

worst connector freshness

)}
{status === null ? ( ) : ( <>
{fmtAgo(status.data.orbit.latest_epoch, now)}} hint={fmtDateTime(status.data.orbit.latest_epoch)} /> c.freshness === 'fresh').length)} / ${fmtInt(status.data.connectors.filter((c) => c.enabled).length)}`} hint={ {status.data.connectors.filter((c) => c.enabled && c.freshness !== 'fresh').map((c) => ( ))} {status.data.connectors.every((c) => !c.enabled || c.freshness === 'fresh') && 'all enabled connectors are fresh'} } />
Freshness thresholds
c.enabled)} />

generated {fmtDateTime(status.meta.generated_at)} · request {status.meta.request_id} {num(status.data.orbit.median_element_age_hours) !== null && (num(status.data.orbit.median_element_age_hours) ?? 0) > 48 && Element sets are older than 48 h on median — live positions carry more uncertainty than usual.}

)}
); }