Files
buzz-sheet/app/api/deployments/status/route.ts
T

53 lines
1.7 KiB
TypeScript

import { createHash, timingSafeEqual } from "node:crypto";
import { publishDeploymentStatus } from "@/lib/deployment/repository";
import {
isDeploymentId,
isDeploymentStatus,
} from "@/lib/deployment/status";
import {
errorResponse,
HttpError,
readJson,
} from "@/lib/security/http";
function authorized(request: Request): boolean {
const secret = process.env.DEPLOYMENT_WEBHOOK_SECRET;
const authorization = request.headers.get("authorization");
if (!secret) throw new HttpError(503, "deployment-webhook-not-configured");
if (!authorization?.startsWith("Bearer ")) return false;
const supplied = authorization.slice("Bearer ".length);
const expectedDigest = createHash("sha256").update(secret).digest();
const suppliedDigest = createHash("sha256").update(supplied).digest();
return timingSafeEqual(expectedDigest, suppliedDigest);
}
export async function POST(request: Request) {
try {
if (!authorized(request)) throw new HttpError(401, "unauthorized");
const body = await readJson(request, 1_024);
if (
typeof body !== "object" ||
body === null ||
!isDeploymentId((body as Record<string, unknown>).deploymentId) ||
!isDeploymentStatus((body as Record<string, unknown>).status)
) {
throw new HttpError(400, "invalid-deployment-status");
}
const deployment = body as {
deploymentId: string;
status: "deploying" | "ready" | "failed";
};
const result = await publishDeploymentStatus(
deployment.deploymentId,
deployment.status,
);
return Response.json(result, {
status: 202,
headers: { "Cache-Control": "no-store" },
});
} catch (cause) {
return errorResponse(cause);
}
}