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%
4.5 KB · 101 lines tsx
Raw Blame History
1import type { Metadata } from 'next';2import Link from 'next/link';3import { BgpLive } from '@/components/bgp/BgpLive';4import { BgpSeriesChart } from '@/components/bgp/BgpSeriesChart';5import { Section, StatusDot } from '@/components/ui/primitives';6import { apiGet } from '@/lib/api';7import { fmt, fmtInt } from '@/lib/format';8import { Time } from '@/lib/time';9import type { BgpStats } from '@/lib/types';1011export const dynamic = 'force-dynamic';12export const metadata: Metadata = { title: 'BGP', description: 'Live BGP updates and withdrawals per second versus baseline, collector health, one-hour series and top origin ASNs.' };1314export default async function BgpPage() {15  const b = await apiGet<BgpStats>('/api/v1/bgp/stats');16  return (17    <div className="pb-8">18      <header className="pt-6 pb-4">19        <p className="label">Routing component</p>20        <h1 className="mt-1 text-[26px] font-medium tracking-tight sm:text-[32px]">BGP</h1>21        <p className="mt-1 max-w-[760px] text-[13px] text-ink-2">Announcements and withdrawals from RIPE RIS Live collectors, normalised and compared with a rolling baseline. Withdrawal spikes, origin-ASN changes and collector disagreement feed the Routing component.</p>22      </header>23      <Section label="Live rates" right={<span>updated every 5 s from real counters</span>}>24        <BgpLive initial={b} />25      </Section>26      <Section label="Last hour" right={<span>per minute</span>}>27        <BgpSeriesChart series={b.series_1h} />28      </Section>29      <div className="grid grid-cols-[minmax(0,1fr)] gap-x-10 lg:grid-cols-2">30        <Section label="Collectors" right={<span>{fmtInt(b.collectors.length)} · {fmtInt(b.peers)} peers</span>} className="min-w-0">31          <div className="scroll-x -mx-3 px-3">32            <table className="tbl">33              <thead>34                <tr>35                  <th>Collector</th>36                  <th className="hidden sm:table-cell">Location</th>37                  <th className="r">Ann./s</th>38                  <th className="r">Wd./s</th>39                  <th className="r hidden sm:table-cell">Peers</th>40                  <th className="r">Last msg</th>41                  <th>Fresh</th>42                </tr>43              </thead>44              <tbody>45                {b.collectors.map((c) => (46                  <tr key={c.id}>47                    <td className="num text-ink">{c.id}</td>48                    <td className="hidden text-ink-2 sm:table-cell">{c.location}</td>49                    <td className="num r">{fmt(c.announcements_per_s)}</td>50                    <td className="num r">{fmt(c.withdrawals_per_s)}</td>51                    <td className="num r hidden text-ink-2 sm:table-cell">{fmtInt(c.peers)}</td>52                    <td className="num r text-ink-2">53                      <Time ts={c.last_message} style="time" />54                    </td>55                    <td>56                      <StatusDot status={c.fresh ? 'online' : 'stale'} />57                    </td>58                  </tr>59                ))}60              </tbody>61            </table>62          </div>63        </Section>64        <Section label="Top origins · 1 h" className="min-w-0">65          <div className="scroll-x -mx-3 px-3">66          <table className="tbl">67            <thead>68              <tr>69                <th>Origin ASN</th>70                <th className="r">Announcements</th>71                <th className="r">Withdrawals</th>72                <th className="r hidden sm:table-cell">Wd. share</th>73              </tr>74            </thead>75            <tbody>76              {b.top_origins_1h.map((o) => {77                const share = o.announcements + o.withdrawals ? o.withdrawals / (o.announcements + o.withdrawals) : 0;78                return (79                  <tr key={o.asn}>80                    <td>81                      <Link href={`/asn/${o.asn}`} className="text-ink hover:text-accent">82                        <span className="num">AS{o.asn}</span> {o.name}83                      </Link>84                    </td>85                    <td className="num r">{fmtInt(o.announcements)}</td>86                    <td className="num r" style={{ color: share > 0.3 ? 'var(--p-high)' : undefined }}>87                      {fmtInt(o.withdrawals)}88                    </td>89                    <td className="num r hidden text-ink-2 sm:table-cell">{Math.round(share * 100)} %</td>90                  </tr>91                );92              })}93            </tbody>94          </table>95          </div>96        </Section>97      </div>98    </div>99  );100}101