import { ZodError, type ZodSchema } from "zod"; import { config } from "./config"; import { HttpError } from "./errors"; const allowedOrigin = (request: Request): string | null => { const origin = request.headers.get("origin"); if (!origin) { return null; } return config.appOrigins.includes(origin.toLowerCase()) ? origin : null; }; const securityHeaders = (): Record => ({ "X-Content-Type-Options": "nosniff", "X-Frame-Options": "DENY", "Referrer-Policy": "no-referrer" }); const mergeHeaders = (request: Request, headers?: HeadersInit): Headers => { const result = new Headers(headers); const origin = allowedOrigin(request); for (const [key, value] of Object.entries(securityHeaders())) { result.set(key, value); } result.set("Vary", "Origin"); if (origin) { result.set("Access-Control-Allow-Origin", origin); result.set("Access-Control-Allow-Credentials", "true"); result.set("Access-Control-Allow-Headers", "Content-Type"); result.set("Access-Control-Allow-Methods", "GET,POST,PATCH,DELETE,OPTIONS"); } return result; }; export const assertAllowedOrigin = (request: Request): void => { const origin = request.headers.get("origin"); if (!origin) { return; } console.log(config.appOrigins, origin) if (!config.appOrigins.includes(origin.toLowerCase())) { throw new HttpError(403, "Origin is not allowed."); } }; export const jsonResponse = ( request: Request, status: number, payload: unknown, headers?: HeadersInit ): Response => new Response(JSON.stringify(payload), { status, headers: mergeHeaders(request, { "content-type": "application/json; charset=utf-8", ...headers }) }); export const emptyResponse = (request: Request, status = 204, headers?: HeadersInit): Response => new Response(null, { status, headers: mergeHeaders(request, headers) }); export const optionsResponse = (request: Request): Response => emptyResponse(request, 204); export const parseJson = async (request: Request, schema: ZodSchema): Promise => { let body: unknown; try { body = await request.json(); } catch { throw new HttpError(400, "Request body must be valid JSON."); } try { return schema.parse(body); } catch (error) { if (error instanceof ZodError) { throw new HttpError(400, error.issues[0]?.message ?? "Invalid request body."); } throw error; } }; export const errorResponse = (request: Request, error: unknown): Response => { if (error instanceof HttpError) { return jsonResponse(request, error.status, { error: { message: error.message } }, error.headers); } if (error instanceof ZodError) { return jsonResponse(request, 400, { error: { message: error.issues[0]?.message ?? "Invalid request." } }); } console.error(error); return jsonResponse(request, 500, { error: { message: "Unexpected server error." } }); };