src/portal-maintenance.ts
34 lines and 3 functions in DrDustinEdwards/capsid, at commit daa875b on 2026-10-24. This file on GitHub.
Functions
| Function | Lines | Complexity | Covered | CRAP | Mutation |
|---|---|---|---|---|---|
| sum | 17 to 17 | 1 | 100% | OkCRAP 1 | No data: mutation testing has not run on this repo yet |
| <anonymous:17> | 17 to 17 | 1 | 100% | OkCRAP 1 | No data: mutation testing has not run on this repo yet |
| handlePortalMaintenance | 20 to 38 | 3 | 32% | WatchCRAP 5.9 | No data: mutation testing has not run on this repo yet |
Imports
It imports src/env.ts, src/maintenance.ts, src/ops-feed.ts, src/ops-types/views.ts, src/portal-auth.ts, src/roster.ts. Imported by src/routes.ts.
Source
import type { Env } from "./env";
import { ROSTER } from "./roster";
import { readMaintenance } from "./maintenance";
import { OPS_RETURN_TO } from "./ops-feed";
import type { PortalMaintenance } from "./ops-types/views";
import { portalGate } from "./portal-auth";
// The Portal's Maintenance list: GET /portal/api/maintenance (job_549550d73d4e). The list
// the daily maintenance pass stored, the same one capsid_run_improve action status serves per namespace, so
// the Portal and chat cannot disagree. It reads KV only and runs nothing: the pass runs on
// the five-minute tick once a day. A route, not a tool, so no grant is checked here
// (one enforcement point, test/route-gates.test.ts): it answers to portalGate, and src/scope.ts lists
// it among the routes gated some other way.
export const PORTAL_MAINTENANCE_PATH = "/portal/api/maintenance";
const sum = (counts: Record<string, number>): number => Object.values(counts).reduce((a, b) => a + b, 0);
/** GET /portal/api/maintenance: the last pass's list and what it read. */
export async function handlePortalMaintenance(request: Request, env: Env, now: Date = new Date()): Promise<Response> {
const gate = await portalGate(request, env, now, OPS_RETURN_TO);
if (!gate.ok) return gate.response;
const list = await readMaintenance(env);
const body: PortalMaintenance = list
? {
generated: list.generated,
items: list.items,
read: {
prs: sum(list.prs_read),
branches: sum(list.branches_read),
repos: Object.keys(list.prs_read).length,
roster: ROSTER.length,
deploys: Object.keys(list.deploys_read).length,
},
}
: { generated: null, items: [], read: { prs: 0, branches: 0, repos: 0, roster: ROSTER.length, deploys: 0 } };
return new Response(JSON.stringify(body), { headers: { "Content-Type": "application/json", "Cache-Control": "no-store" } });
}