47 lines
2.0 KiB
TypeScript
47 lines
2.0 KiB
TypeScript
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<string, unknown>),
|
|
));
|
|
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<T>(
|
|
request: FastifyRequest,
|
|
reply: FastifyReply,
|
|
deps: { config: AppConfig; service: PiManagementService },
|
|
action: () => Promise<T>,
|
|
): Promise<T | FastifyReply> {
|
|
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);
|
|
}
|