spb/internetpressure
Public
TypeScript 36.3%
Python 31.8%
Go 18%
JavaScript 9.8%
Shell 1.9%
SQL 1.4%
CSS 0.5%
1'use client';23import { Stat, StatusDot } from '@/components/ui/primitives';4import { fmt, fmtBytes, fmtInt, fmtPct } from '@/lib/format';5import { Time } from '@/lib/time';6import type { AdminOverview } from '@/lib/types';7import { AdminPage, ErrorNote, Ok, Panel, useAdmin } from './shared';89export function Overview() {10 const { data: o, err, loading, reload } = useAdmin<AdminOverview>('/overview');11 return (12 <AdminPage13 title="Pipeline health"14 desc="Probes, ingest, stores, BGP collectors, engine timings and corroboration connectors. Everything here is about US, not about the Internet."15 right={16 <button type="button" onClick={reload} className="h-8 rounded-[4px] border border-line px-3 text-[12px] text-ink-2 hover:text-ink" disabled={loading}>17 {loading ? 'refreshing…' : 'refresh'}18 </button>19 }20 >21 <ErrorNote err={err} />22 {o && (23 <div className="grid grid-cols-[minmax(0,1fr)] gap-3 lg:grid-cols-2">24 <Panel title="Engine" right={<StatusDot status={o.engine.internal_status} />} className="lg:col-span-2">25 <div className="grid grid-cols-3 gap-4 sm:grid-cols-6">26 <Stat label="last run" value={<Time ts={o.engine.last_run} style="time" />} />27 <Stat label="cycle p50" value={`${fmtInt(o.engine.cycle_ms_p50)} ms`} />28 <Stat label="cycle max" value={<span style={{ color: o.engine.cycle_ms_max > 5000 ? 'var(--warn)' : undefined }}>{`${fmtInt(o.engine.cycle_ms_max)} ms`}</span>} />29 <Stat label="runs 1h" value={fmtInt(o.engine.runs_1h)} />30 <Stat label="errors 1h" value={<span style={{ color: o.engine.errors_1h ? 'var(--bad)' : undefined }}>{fmtInt(o.engine.errors_1h)}</span>} />31 <Stat label="excluded probes" value={o.engine.excluded_probes.length ? o.engine.excluded_probes.join(', ') : 'none'} />32 </div>33 </Panel>3435 <Panel title="Probes" right={<span>{o.probes.filter((p) => p.status === 'online').length}/{o.probes.length} online</span>} className="lg:col-span-2">36 <div className="scroll-x">37 <table className="tbl">38 <thead>39 <tr>40 <th>Probe</th>41 <th>Status</th>42 <th className="r">Uptime 24h</th>43 <th className="r">Clock</th>44 <th className="r">Missing 1h</th>45 <th className="r">Error rate 1h</th>46 <th className="r">Buffered</th>47 <th className="r">Spool</th>48 <th>Version</th>49 <th className="r">Last health</th>50 </tr>51 </thead>52 <tbody>53 {o.probes.map((p) => (54 <tr key={p.probe_id}>55 <td className="num text-ink">56 {p.probe_id} <span className="text-ink-3">{p.region}</span>57 </td>58 <td>59 <StatusDot status={p.status} />60 </td>61 <td className="num r" style={{ color: (p.health?.uptime_24h ?? 1) < 0.99 ? 'var(--warn)' : undefined }}>62 {fmtPct(p.health?.uptime_24h, 2)}63 </td>64 <td className="num r" style={{ color: Math.abs(p.health?.clock_offset_ms ?? 0) > 50 ? 'var(--warn)' : undefined }}>65 {p.health ? `${p.health.clock_offset_ms > 0 ? '+' : ''}${p.health.clock_offset_ms} ms` : '—'}66 </td>67 <td className="num r">{fmtPct(p.health?.missing_ratio_1h, 2)}</td>68 <td className="num r" style={{ color: (p.health?.error_rate_1h ?? 0) > 0.05 ? 'var(--warn)' : undefined }}>69 {fmtPct(p.health?.error_rate_1h, 2)}70 </td>71 <td className="num r" style={{ color: p.health?.buffered ? 'var(--warn)' : undefined }}>72 {fmtInt(p.health?.buffered)}73 </td>74 <td className="num r text-ink-2">{fmtBytes(p.health?.spool_bytes)}</td>75 <td className="num text-ink-2">{p.health?.version ?? p.version}</td>76 <td className="num r text-ink-2">77 <Time ts={p.health?.last_health ?? p.last_seen} style="time" />78 </td>79 </tr>80 ))}81 </tbody>82 </table>83 </div>84 </Panel>8586 <Panel title="Ingest">87 <div className="grid grid-cols-2 gap-4 sm:grid-cols-4">88 <Stat label="batches / min" value={fmtInt(o.ingest.batches_per_min)} />89 <Stat label="measurements / min" value={fmtInt(o.ingest.measurements_per_min)} />90 <Stat label="rejected / min" value={<span style={{ color: o.ingest.rejected_per_min ? 'var(--bad)' : undefined }}>{fmtInt(o.ingest.rejected_per_min)}</span>} />91 <Stat label="last batch" value={<Time ts={o.ingest.last_batch} style="time" />} />92 </div>93 </Panel>9495 <Panel title="BGP" right={<Ok ok={o.bgp.fresh} label={o.bgp.fresh ? 'fresh' : 'stale'} />}>96 <div className="grid grid-cols-3 gap-4">97 <Stat label="messages / s" value={fmt(o.bgp.messages_per_s)} />98 <Stat label="collectors fresh" value={`${o.bgp.collectors.filter((c) => c.fresh).length}/${o.bgp.collectors.length}`} />99 <Stat label="reconnects 24h" value={fmtInt(o.bgp.reconnects_24h)} />100 </div>101 <ul className="mt-3 flex flex-wrap gap-1.5">102 {o.bgp.collectors.map((c) => (103 <li key={c.id} className="num rounded-[3px] border border-line px-1.5 py-0.5 text-[11px]" style={{ color: c.fresh ? 'var(--ink)' : 'var(--warn)' }} title={`${c.location} · ${c.peers} peers`}>104 {c.id}105 </li>106 ))}107 </ul>108 </Panel>109110 <Panel title="Stores" className="lg:col-span-2">111 <div className="grid grid-cols-3 gap-4">112 <Stat label="ClickHouse" value={<Ok ok={o.stores.clickhouse.ok} />} sub={`${fmt(o.stores.clickhouse.inserts_per_s)} inserts/s`} />113 <Stat label="Postgres" value={<Ok ok={o.stores.postgres.ok} />} sub={fmtBytes(o.stores.postgres.size_bytes)} />114 <Stat label="Redis" value={<Ok ok={o.stores.redis.ok} />} sub={`${fmtBytes(o.stores.redis.used_memory_bytes)} · ${fmtInt(o.stores.redis.keys)} keys`} />115 </div>116 <div className="scroll-x mt-3"><table className="tbl">117 <thead>118 <tr>119 <th>ClickHouse table</th>120 <th className="r">Rows</th>121 <th className="r">Bytes</th>122 <th className="r">Oldest</th>123 <th className="r">Newest</th>124 </tr>125 </thead>126 <tbody>127 {o.stores.clickhouse.tables.map((t) => (128 <tr key={t.name}>129 <td className="num text-ink">{t.name}</td>130 <td className="num r">{fmtInt(t.rows)}</td>131 <td className="num r text-ink-2">{fmtBytes(t.bytes)}</td>132 <td className="num r text-ink-2">133 <Time ts={t.oldest} style="date" />134 </td>135 <td className="num r text-ink-2">136 <Time ts={t.newest} style="time" />137 </td>138 </tr>139 ))}140 </tbody>141 </table>142 </div>143 </Panel>144145 <Panel title="Corroboration connectors" className="lg:col-span-2">146 <div className="scroll-x">147 <table className="tbl">148 <thead>149 <tr>150 <th>Connector</th>151 <th>State</th>152 <th className="r">Last fetch</th>153 <th className="r">Incidents declared</th>154 </tr>155 </thead>156 <tbody>157 {o.corroboration.map((c) => (158 <tr key={c.id}>159 <td className="text-ink">160 {c.name} <span className="num text-ink-3">{c.id}</span>161 </td>162 <td>163 <Ok ok={c.ok} />164 </td>165 <td className="num r text-ink-2">166 <Time ts={c.last_fetch} style="time" />167 </td>168 <td className="num r">{fmtInt(c.incidents)}</td>169 </tr>170 ))}171 </tbody>172 </table>173 </div>174 </Panel>175 </div>176 )}177 </AdminPage>178 );179}180