import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify"; import { getPrincipal } from "../auth/auth.js"; import type { AppConfig } from "../config.js"; import { PiManagementError, type PiManagementService } from "../pi/management.js"; export function piManagementRoutes( app: FastifyInstance, deps: { config: AppConfig; service: PiManagementService }, ): void { app.get("/pi-management/status", async (request, reply) => run(request, reply, deps, () => deps.service.status())); app.get("/pi-management/options", async (request, reply) => run(request, reply, deps, () => deps.service.options())); app.put("/pi-management/config", async (request, reply) => run( request, reply, deps, () => deps.service.configure((request.body ?? {}) as Record), )); app.post("/pi-management/test", async (request, reply) => run(request, reply, deps, () => deps.service.test())); app.get("/pi-management/logs", async (request, reply) => run(request, reply, deps, () => deps.service.logs())); } async function run( request: FastifyRequest, reply: FastifyReply, deps: { config: AppConfig; service: PiManagementService }, action: () => Promise, ): Promise { const principal = getPrincipal(request); if (!managementAllowed(deps.config, principal.isAdmin)) { return reply.code(403).send({ code: "pi_management_forbidden", error: "Pi management is not permitted" }); } try { return await action(); } catch (error) { if (error instanceof PiManagementError) { const statusCode = error.code === "pi_management_invalid_config" ? 400 : 503; return reply.code(statusCode).send({ code: error.code, error: error.message }); } return reply.code(503).send({ code: "pi_management_unavailable", error: "Pi management is unavailable" }); } } function managementAllowed(config: AppConfig, isAdmin: boolean): boolean { return (config.authMode === "none" && !config.publicExposure) || (config.authMode === "upstream" && isAdmin); }