import Link from 'next/link'; import { requireAdmin } from '@/lib/admin/auth'; import { connectorsOverview } from '@/lib/admin/queries'; import { connectorAction } from '@/lib/admin/actions'; import { AdminShell, ActionButton, StatusPill, fmtTs, n } from '@/components/admin/shell'; import { Table, th, td, tdNum } from '@/components/ui/primitives'; function pct(v: unknown): string { const x = Number(v); return Number.isFinite(x) ? `${Math.round(x * 100)}%` : '—'; } export default async function ConnectorsPage() { await requireAdmin(); const rows = await connectorsOverview(); return (
{rows.length === 0 ? : null} {rows.map((r) => { const health = (r.health ?? {}) as Record; const anomalies = Array.isArray(r.last_anomalies) ? (r.last_anomalies as string[]) : []; const drift = anomalies.some((a) => /schema|selector|missing_field|redesign/.test(a)); return ( ); })}
Connector Health Status Pages/min Errors Last crawl Records 24h Dup. FC rate SF rate Parser conf. Schema Actions
No connectors in the registry yet. Add meta.json files under connectors/ and run pnpm db:seed.
{String(r.display_name)}
{String(r.id)} · {(r.engine_priority as string[]).join('→')} · {String(r.priority)} · every {n(r.refresh_frequency_minutes)} min
{r.health_at ?
{fmtTs(r.health_at)}
: null}
{r.last_run_status ?
run: {String(r.last_run_status)}
: null}
{n(r.pages_per_min, 1)} {r.last_error ? '1' : '0'}{anomalies.length ?
{anomalies.length} anomal.
: null}
{fmtTs(r.last_started_at)}
ok: {fmtTs(r.last_success_at)}
{n(r.records_24h)}
{n(r.normalized_24h)} norm.
{n(r.duplicates_24h)} {pct(health.firecrawl_success_rate)} {pct(health.scrapfly_fallback_rate)} {pct(r.parser_confidence)} v{String(r.schema_version)}{drift ?
drift?
: null}
{(['run', 'probe', 'recrawl', 'retry'] as const).map((a) => (
))}
inspect

run = incremental crawl · probe = 25-record smoke test · recrawl = backfill mode · retry = re-run from the last cursor. FC = Firecrawl success rate, SF = share of pages that needed Scrapfly (from the 24 h health snapshot).

); }