TypeScript 97.5%
SQL 1.4%
Python 0.8%
1import { requireAdmin } from "@/lib/session";2import { internalApi, InternalApiError } from "@/lib/api";3import { getProviderConfigs, getProviderHealthHistory, getProviderStats, rangeStart } from "@/lib/queries/admin";4import { formatBytes, formatMs, formatNumber, formatPercent, formatUsd } from "@/lib/format";5import { PageHeader } from "@/components/ui/page-header";6import { Alert } from "@/components/ui/alert";7import { Badge, StatusBadge } from "@/components/ui/badge";8import { Stat, StatGrid } from "@/components/ui/stat";9import { Table, TableBody, TableCell, TableEmpty, TableHead, TableHeader, TableRow } from "@/components/ui/table";10import { DateCell, NetworkBadge, Panel, ProviderBadge, numCell } from "@/components/admin/primitives";11import { CircuitList, ProbeButton, ProviderConfigForm, ResetAllCircuitsButton } from "@/components/admin/provider-panel";1213export const dynamic = "force-dynamic";1415type ProvidersPayload = Awaited<ReturnType<typeof internalApi.providers>>;1617export default async function AdminProvidersPage() {18 await requireAdmin();19 let live: ProvidersPayload | null = null;20 let apiError: string | null = null;21 try {22 live = await internalApi.providers();23 } catch (e) {24 apiError = e instanceof InternalApiError ? `${e.code}: ${e.message}` : (e as Error).message;25 }26 const [configs, stats, history] = await Promise.all([getProviderConfigs(), getProviderStats(rangeStart("24h")), getProviderHealthHistory(50)]);27 const configById = new Map(configs.map((c) => [c.id, c]));28 const statById = new Map(stats.map((s) => [s.provider, s]));2930 // Providers known to the API, plus any config rows the API does not report (e.g. disabled ones).31 const providerIds = Array.from(new Set([...(live?.providers.map((p) => p.id) ?? []), ...configs.map((c) => c.id)]));3233 return (34 <>35 <PageHeader36 eyebrow="Upstream"37 title="Providers"38 description="Configuration, health, circuit breakers and 24-hour unit economics per upstream provider. This page is the only place provider names appear."39 actions={40 <>41 <ResetAllCircuitsButton />42 <ProbeButton />43 </>44 }45 />4647 {apiError ? (48 <Alert variant="warning" title="API service unreachable" className="mb-4">49 Live provider status could not be loaded ({apiError}). Configuration and database statistics are still shown; saving a config will retry the reload.50 </Alert>51 ) : (52 <p className="mb-4 text-[12.5px] text-fg-muted">53 Available networks right now: {live!.available_networks.length ? live!.available_networks.map((n) => <NetworkBadge key={n} network={n} />) : <span>none</span>}54 </p>55 )}5657 <div className="space-y-6">58 {providerIds.length === 0 ? <Alert variant="info">No providers registered.</Alert> : null}59 {providerIds.map((id) => {60 const p = live?.providers.find((x) => x.id === id) ?? null;61 const cfg = configById.get(id) ?? null;62 const st = statById.get(id) ?? null;63 const label = cfg?.label ?? p?.label ?? id;64 const enabled = cfg?.enabled ?? true;65 return (66 <Panel67 key={id}68 title={69 <span className="flex flex-wrap items-center gap-2">70 <span className="text-[15px]">{label}</span>71 <ProviderBadge id={id} />72 {p ? p.configured ? <Badge variant="success" dot>configured</Badge> : <Badge variant="danger" dot>credentials missing</Badge> : <Badge variant="outline">not reported by API</Badge>}73 {enabled ? <Badge variant="accent">enabled</Badge> : <Badge variant="outline">disabled</Badge>}74 </span>75 }76 description={p ? `Networks: ${p.networks.join(", ") || "none"}` : cfg ? `Networks (config): ${cfg.networks.join(", ")}` : undefined}77 bodyClassName="p-0"78 >79 <div className="border-b border-border">80 <StatGrid cols={6} className="rounded-none border-0 shadow-none">81 <Stat label="Attempts 24h" value={formatNumber(st?.requests ?? 0)} />82 <Stat label="Success" value={formatPercent(st?.successRate ?? null)} />83 <Stat label="Blocked" value={formatPercent(st?.blockedRate ?? null)} />84 <Stat label="Avg latency" value={formatMs(st?.avgLatency ?? null)} />85 <Stat label="Bytes · cost" value={`${formatBytes(st?.bytes ?? 0)} · ${formatUsd(st?.cost ?? 0, true)}`} />86 <Stat label="Cost / success" value={<span className="text-accent">{formatUsd(st?.costPerSuccess ?? null, true)}</span>} hint="key metric" />87 </StatGrid>88 </div>89 <div className="grid gap-6 p-4 lg:grid-cols-2">90 <div>91 <h3 className="mb-2 text-[12px] font-semibold uppercase tracking-wide text-fg-subtle">Configuration</h3>92 <ProviderConfigForm93 id={id}94 label={label}95 enabled={enabled}96 networks={cfg?.networks ?? []}97 pricePerGbUsd={cfg?.pricePerGbUsd ?? {}}98 weight={cfg?.weight ?? 1}99 maxConcurrency={cfg?.maxConcurrency ?? 200}100 notes={cfg?.notes ?? null}101 livePrices={p?.prices ?? {}}102 liveNetworks={p?.networks ?? []}103 hasConfigRow={Boolean(cfg)}104 />105 </div>106 <div className="grid gap-5">107 <div>108 <h3 className="mb-2 text-[12px] font-semibold uppercase tracking-wide text-fg-subtle">Latest health per network</h3>109 {p?.health.length ? (110 <ul className="divide-y divide-border rounded-md border border-border">111 {p.health.map((h) => (112 <li key={h.network} className="flex flex-wrap items-center gap-3 px-3 py-2 text-[12.5px]">113 <NetworkBadge network={h.network} />114 <StatusBadge status={h.status} />115 <span className="tabular font-mono text-fg-muted">{formatMs(h.latency_ms)}</span>116 <span className="text-fg-subtle">117 <DateCell value={h.checked_at} />118 </span>119 {h.detail ? (120 <span className="w-full truncate font-mono text-[11.5px] text-fg-subtle" title={h.detail}>121 {h.detail}122 </span>123 ) : null}124 </li>125 ))}126 </ul>127 ) : (128 <p className="text-[12.5px] text-fg-muted">No health check in the last 24 hours. Run a probe.</p>129 )}130 </div>131 <div>132 <h3 className="mb-2 text-[12px] font-semibold uppercase tracking-wide text-fg-subtle">Circuit breakers</h3>133 <CircuitList circuits={p?.circuits ?? []} />134 </div>135 </div>136 </div>137 </Panel>138 );139 })}140 </div>141142 <Panel title="Health history" description="Last 50 probe results across providers." flush className="mt-6">143 <Table>144 <TableHeader>145 <TableRow>146 <TableHead>Checked</TableHead>147 <TableHead>Provider</TableHead>148 <TableHead>Network</TableHead>149 <TableHead>Status</TableHead>150 <TableHead className="text-right">Latency</TableHead>151 <TableHead className="text-right">Success rate</TableHead>152 <TableHead>Detail</TableHead>153 </TableRow>154 </TableHeader>155 <TableBody>156 {history.length === 0 ? (157 <TableEmpty colSpan={7}>No probes recorded yet.</TableEmpty>158 ) : (159 history.map((h) => (160 <TableRow key={h.id}>161 <TableCell>162 <DateCell value={h.checkedAt} relative={false} />163 </TableCell>164 <TableCell>165 <ProviderBadge id={h.provider} />166 </TableCell>167 <TableCell>168 <NetworkBadge network={h.network} />169 </TableCell>170 <TableCell>171 <StatusBadge status={h.status} />172 </TableCell>173 <TableCell className={numCell}>{formatMs(h.latencyMs)}</TableCell>174 <TableCell className={numCell}>{h.successRate !== null ? formatPercent(h.successRate * (h.successRate <= 1 ? 100 : 1)) : "—"}</TableCell>175 <TableCell className="max-w-[360px] truncate font-mono text-[11.5px] text-fg-muted" title={h.detail ?? undefined}>176 {h.detail ?? "—"}177 </TableCell>178 </TableRow>179 ))180 )}181 </TableBody>182 </Table>183 </Panel>184 </>185 );186}187