spb/internetpressure
Public
TypeScript 36.3%
Python 31.8%
Go 18%
JavaScript 9.8%
Shell 1.9%
SQL 1.4%
CSS 0.5%
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