spb/datacenterindex
Public
HTML 53.9%
TypeScript 44.5%
JavaScript 0.6%
SQL 0.5%
1/**2 * Admin API (/api/admin): header `x-dci-admin-token` must equal DCI_ADMIN_TOKEN (constant-time compare).3 * 60 req/min per IP. Responses use the same envelope `{ data, meta }`; errors `{ error, statusCode }`.4 */5import { createHash, timingSafeEqual } from "node:crypto";6import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";7import { getEnv } from "../../env.js";8import { HttpError } from "../../lib/http.js";9import { connectorAdminRoutes } from "./connectors.js";10import { documentAdminRoutes } from "./documents.js";11import { matchAdminRoutes } from "./matches.js";12import { curationAdminRoutes } from "./curation.js";13import { devtoolAdminRoutes } from "./devtool.js";14import { opsAdminRoutes } from "./ops.js";15import { qualityAdminRoutes } from "./quality.js";16import { proxyAdminRoutes } from "./proxy.js";17import { claimAdminRoutes } from "./claims.js";18import { projectAdminRoutes } from "./projects.js";1920export function tokenMatches(provided: string | undefined, expected: string | null): boolean {21 if (!expected || !provided) return false;22 const a = createHash("sha256").update(provided).digest();23 const b = createHash("sha256").update(expected).digest();24 return timingSafeEqual(a, b);25}2627export function adminAuth(req: FastifyRequest, _reply: FastifyReply, done: (err?: Error) => void): void {28 const expected = getEnv().adminToken;29 if (!expected) return done(new HttpError(503, "admin API disabled: DCI_ADMIN_TOKEN is not configured (missing, empty or the \"change-me\" placeholder)"));30 const header = req.headers["x-dci-admin-token"];31 const provided = Array.isArray(header) ? header[0] : header;32 if (!tokenMatches(provided, expected)) return done(new HttpError(401, "unauthorized"));33 done();34}3536export async function adminRoutes(app: FastifyInstance): Promise<void> {37 app.addHook("onRequest", adminAuth);38 app.addHook("onSend", async (_req, reply) => { reply.header("cache-control", "no-store"); });39 // route-level rate limit override for the admin scope40 app.addHook("onRoute", (route) => {41 route.config = { ...(route.config as Record<string, unknown> | undefined), rateLimit: { max: getEnv().adminRateLimit, timeWindow: "1 minute" } };42 route.schema = { ...(route.schema ?? {}), tags: ["admin"], security: [{ adminToken: [] }] };43 });44 await app.register(connectorAdminRoutes);45 await app.register(documentAdminRoutes);46 await app.register(matchAdminRoutes);47 await app.register(curationAdminRoutes);48 await app.register(devtoolAdminRoutes);49 await app.register(opsAdminRoutes);50 await app.register(qualityAdminRoutes);51 await app.register(proxyAdminRoutes);52 await app.register(claimAdminRoutes);53 await app.register(projectAdminRoutes);54}55