import { NextResponse, type NextRequest } from "next/server"; import { lotteryApiOrigin } from "./src/lib/lottery-api-base"; import { generateCSP, nonCspSecurityHeaders } from "./src/lib/csp-config"; type RuntimeOriginsEnvelope = { code?: number; data?: { iframe_allowed_origins?: unknown; }; }; const RUNTIME_ORIGINS_TTL_MS = 60_000; let cachedRuntimeOrigins: string[] | null = null; let cachedAt = 0; async function loadRuntimeOrigins(): Promise { if (cachedRuntimeOrigins !== null && Date.now() - cachedAt < RUNTIME_ORIGINS_TTL_MS) { return cachedRuntimeOrigins; } try { const url = `${lotteryApiOrigin()}/api/v1/integration/runtime-origins`; const response = await fetch(url, { headers: { Accept: "application/json" }, cache: "no-store", }); if (!response.ok) return []; const payload = (await response.json()) as RuntimeOriginsEnvelope; const origins = payload.data?.iframe_allowed_origins; if (!Array.isArray(origins)) return []; cachedRuntimeOrigins = origins.filter((origin): origin is string => typeof origin === "string"); cachedAt = Date.now(); return cachedRuntimeOrigins; } catch { return cachedRuntimeOrigins ?? []; } } export async function middleware(_request: NextRequest): Promise { const response = NextResponse.next(); const runtimeOrigins = await loadRuntimeOrigins(); response.headers.set("Content-Security-Policy", generateCSP(runtimeOrigins)); for (const header of nonCspSecurityHeaders) { response.headers.set(header.key, header.value); } return response; } export const config = { matcher: ["/((?!api|_next/static|_next/image|favicon.ico|.*\\..*).*)"], };