feat(ops): /operations dashboard — everything running in the background
A single live page for all the background work that downloads/analyses, so the chair can see what's running instead of guessing. - court_fetch_service: GET /pm2 (unauthenticated, host-only) → trimmed pm2 jlist for the legal-* services (status, restarts, mem, cron schedule). - FastAPI GET /api/operations: aggregates the DB-backed pipelines (court_fetch jobs, metadata + halacha extraction queues, halacha review gate, missing_precedents, digests, recent court ingests) and proxies the host /pm2 over the docker bridge (graceful if the host service is down). - web-ui /operations page (+ src/lib/api/operations.ts hook, nav entry under admin): services grid (with Hebrew labels + schedules) + pipeline cards + recent-fetch / recent-ingest lists. Auto-refreshes every 5s. tsc --noEmit clean; pm2 status carries nothing sensitive and the bind (10.0.1.1) is host/container-only, so /pm2 needs no secret. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
53
web-ui/src/lib/api/operations.ts
Normal file
53
web-ui/src/lib/api/operations.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { apiRequest } from "./client";
|
||||
|
||||
export type OpsService = {
|
||||
name: string;
|
||||
status: string;
|
||||
restarts: number;
|
||||
uptime_ms: number;
|
||||
cpu: number;
|
||||
memory_bytes: number;
|
||||
cron: string;
|
||||
autorestart: boolean;
|
||||
};
|
||||
|
||||
export type CourtFetchJob = {
|
||||
case_number_norm: string;
|
||||
citation_raw: string;
|
||||
tier: string;
|
||||
status: string;
|
||||
error: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
export type IngestedRow = {
|
||||
case_number: string;
|
||||
court: string;
|
||||
source_url: string;
|
||||
created_at: string;
|
||||
};
|
||||
|
||||
export type OperationsSnapshot = {
|
||||
services: OpsService[];
|
||||
services_error: string | null;
|
||||
pipelines: {
|
||||
court_fetch: { by_status: Record<string, number>; recent: CourtFetchJob[] };
|
||||
metadata_extraction: { by_status: Record<string, number>; queued: number };
|
||||
halacha_extraction: { by_status: Record<string, number>; queued: number };
|
||||
halacha_review: { by_status: Record<string, number> };
|
||||
missing_precedents: { by_status: Record<string, number> };
|
||||
digests: { total: number; linked: number };
|
||||
ingested_recent: IngestedRow[];
|
||||
};
|
||||
};
|
||||
|
||||
export function useOperations() {
|
||||
return useQuery({
|
||||
queryKey: ["operations"],
|
||||
queryFn: ({ signal }) =>
|
||||
apiRequest<OperationsSnapshot>("/api/operations", { signal }),
|
||||
refetchInterval: 5000, // live view of background work
|
||||
staleTime: 3000,
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user