SPB Git forge
15commits 1branches 0releases
29.7 MBsize
maindefault branch
10 days agolast push
TypeScript 36.3% Python 31.8% Go 18% JavaScript 9.8% Shell 1.9% SQL 1.4% CSS 0.5%
8.7 KB · 180 lines tsx
Raw Blame History
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