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 { useState } from 'react';4import { adminFetch } from '@/lib/admin-fetch';5import type { AdminTarget } from '@/lib/types';6import { AdminPage, ErrorNote, Field, Panel, Toast, btnPrimary, inputCls, useAdmin } from './shared';78export function Boost() {9 const { data } = useAdmin<{ targets: AdminTarget[] }>('/targets');10 const [q, setQ] = useState('');11 const [sel, setSel] = useState<Set<string>>(new Set());12 const [factor, setFactor] = useState(0.5);13 const [seconds, setSeconds] = useState(900);14 const [msg, setMsg] = useState<string | null>(null);15 const [err, setErr] = useState<string | null>(null);16 const [busy, setBusy] = useState(false);17 const list = (data?.targets ?? []).filter((t) => !q || t.hostname.includes(q) || t.name.toLowerCase().includes(q.toLowerCase()) || t.service_id.includes(q));18 const toggle = (id: string) =>19 setSel((s) => {20 const n = new Set(s);21 if (n.has(id)) n.delete(id);22 else n.add(id);23 return n;24 });25 return (26 <AdminPage title="Sampling boost" desc="Temporarily multiply the check interval of selected targets by a factor < 1 (0.5 = twice as often) for a limited time. Pushed to probes at their next config refresh; never exceeds the ethical traffic budget.">27 <div className="grid grid-cols-[minmax(0,1fr)] gap-3 lg:grid-cols-[minmax(0,1fr)_300px]">28 <Panel title="Targets" right={<input value={q} onChange={(e) => setQ(e.target.value)} placeholder="filter…" className={inputCls} aria-label="Filter" />}>29 <div className="flex gap-2 text-[11px]">30 <button type="button" className="text-ink-2 hover:text-ink" onClick={() => setSel(new Set(list.map((t) => t.target_id)))}>31 select shown ({list.length})32 </button>33 <button type="button" className="text-ink-2 hover:text-ink" onClick={() => setSel(new Set())}>34 clear35 </button>36 </div>37 <ul className="mt-2 max-h-[60vh] overflow-auto divide-y divide-line">38 {list.map((t) => (39 <li key={t.target_id}>40 <label className="flex cursor-pointer items-center gap-3 py-1.5 text-[12.5px]">41 <input type="checkbox" checked={sel.has(t.target_id)} onChange={() => toggle(t.target_id)} className="accent-[var(--accent)]" />42 <span className="text-ink">{t.name}</span>43 <span className="num text-ink-3">{t.hostname}</span>44 <span className="num ml-auto text-ink-3">tier {t.tier}</span>45 </label>46 </li>47 ))}48 </ul>49 </Panel>50 <Panel title="Boost">51 <Field label="factor (interval multiplier)">52 <input type="number" step="0.1" min={0.1} max={1} value={factor} onChange={(e) => setFactor(Number(e.target.value))} className={`${inputCls} num w-full`} />53 </Field>54 <div className="mt-3">55 <Field label="duration (seconds)">56 <input type="number" step="60" min={60} max={7200} value={seconds} onChange={(e) => setSeconds(Number(e.target.value))} className={`${inputCls} num w-full`} />57 </Field>58 </div>59 <p className="num mt-3 text-[11px] text-ink-3">60 {sel.size} targets · ×{factor} for {Math.round(seconds / 60)} min61 </p>62 <button63 type="button"64 className={`${btnPrimary} mt-3 w-full`}65 disabled={!sel.size || busy || factor <= 0 || factor > 1}66 onClick={() => {67 setBusy(true);68 setErr(null);69 adminFetch('/boost', { method: 'POST', body: { targets: [...sel], factor, seconds } })70 .then(() => setMsg(`Boost pushed for ${sel.size} targets`))71 .catch((e: unknown) => setErr(String(e)))72 .finally(() => {73 setBusy(false);74 setTimeout(() => setMsg(null), 4000);75 });76 }}77 >78 Push boost79 </button>80 <Toast msg={msg} />81 <ErrorNote err={err} />82 </Panel>83 </div>84 </AdminPage>85 );86}87