import { randomBytes, randomUUID } from "node:crypto"; import { readFileSync } from "node:fs"; import { resolve } from "node:path"; import { BootstrapResponseSchema, CorrelationIdSchema, ErrorDetailsSchema, ErrorEnvelopeSchema, GenerationErrorCategorySchema, ModelConfigSseEventSchema, ModelRuntimeSseEventSchema, SseEventSchema, StableEngineeringErrorCodeSchema, StateSseEventSchema, createErrorEnvelope, isCorrelationId, type BootstrapResponse, } from "@dada/shared-contracts"; import swagger from "@fastify/swagger"; import Fastify, { type FastifyReply } from "fastify"; import { BrowserSupportRequestSchema, BrowserSupportSuccessSchema, BrowserUnsupportedReasonSchema, browserSupportCookieMaxAgeSeconds, browserSupportCookieName, checkBrowserSupport, createBrowserSupportCookie, supportedBrowserSummary, verifyBrowserSupportCookie, type BrowserSupportRelease, type BrowserUnsupportedReason, } from "./browser-support.js"; import { EventHub } from "./event-hub.js"; import type { PublicAssetResolver } from "./local-data-root.js"; import { isAllowedNetworkRequest, type NetworkBoundaryOptions } from "./network-boundary.js"; const defaultBootstrap: BootstrapResponse = { app_version: "0.0.0", dependencies: [], model_summary: { config_set_version: null, configured_default_model_id: null, recommended_model_id: null, runtime_availability_version: null, }, public_features: [], }; export interface CreateAppOptions { bootstrap?: () => BootstrapResponse | Promise; browserGate?: boolean; browserSupportRelease?: BrowserSupportRelease; browserSupportSecret?: Buffer; eventHub?: EventHub; networkBoundary?: NetworkBoundaryOptions; publicAssets?: PublicAssetResolver; } const supportGateDirectory = resolve("apps/web/support-gate"); const supportGateHtml = readFileSync(resolve(supportGateDirectory, "index.html"), "utf8"); const supportGateCss = readFileSync(resolve(supportGateDirectory, "support-gate.css"), "utf8"); const supportGateJavaScript = readFileSync(resolve(supportGateDirectory, "support-gate.js"), "utf8"); const clientHints = "Sec-CH-UA, Sec-CH-UA-Full-Version-List, Sec-CH-UA-Platform"; const contentSecurityPolicy = [ "default-src 'self'", "script-src 'self'", "style-src 'self'", "img-src 'self' blob:", "font-src 'self' blob:", "connect-src 'self'", "object-src 'none'", "base-uri 'none'", "frame-ancestors 'none'", ].join("; "); function requestCorrelationId(headers: Record) { const header = headers["x-correlation-id"]; const candidate = Array.isArray(header) ? header[0] : header; return isCorrelationId(candidate) ? candidate : randomUUID(); } function headerValue(value: string | string[] | undefined) { return Array.isArray(value) ? value[0] : value; } function isSupportGateRequest(method: string, path: string) { if (method === "POST" && path === "/api/v1/support/check") return true; if (method !== "GET" && method !== "HEAD") return false; return ( path === "/" || path === "/app" || path.startsWith("/app/") || path === "/admin" || path.startsWith("/admin/") || path === "/support-gate.css" || path === "/support-gate.js" || path === "/RELEASE.json" || path === "/healthz" ); } function sendBrowserUnsupported( reply: FastifyReply, correlationId: string, reason: BrowserUnsupportedReason, release: BrowserSupportRelease | undefined, ) { return reply.code(426).send( createErrorEnvelope({ code: "BROWSER_UNSUPPORTED", correlationId, details: { reason, supported_browsers: supportedBrowserSummary(release), }, }), ); } export async function createApp(options: CreateAppOptions = {}) { const eventHub = options.eventHub ?? new EventHub(); const bootstrap = options.bootstrap ?? (() => defaultBootstrap); const browserGate = options.browserGate ?? true; const browserSupportSecret = options.browserSupportSecret ?? randomBytes(32); const browserSupportRelease = options.browserSupportRelease; const app = Fastify({ genReqId: (request) => requestCorrelationId(request.headers), logger: false, }); await app.register(swagger, { openapi: { info: { title: "Dada P0-A", version: "0.0.0", }, openapi: "3.1.0", }, refResolver: { buildLocalReference: (schema, _baseUri, _fragment, index) => typeof schema.$id === "string" ? schema.$id : `schema-${index}`, }, }); for (const schema of [ CorrelationIdSchema, GenerationErrorCategorySchema, StableEngineeringErrorCodeSchema, ErrorDetailsSchema, ErrorEnvelopeSchema, BrowserUnsupportedReasonSchema, BrowserSupportRequestSchema, BrowserSupportSuccessSchema, BootstrapResponseSchema, StateSseEventSchema, ModelConfigSseEventSchema, ModelRuntimeSseEventSchema, SseEventSchema, ]) { app.addSchema(schema); } app.addHook("onRequest", async (request, reply) => { reply.header("X-Correlation-Id", request.id); reply.header("Accept-CH", clientHints); reply.header("Cache-Control", "no-store"); reply.header("Content-Security-Policy", contentSecurityPolicy); reply.header("X-Content-Type-Options", "nosniff"); const host = headerValue(request.headers.host); const origin = headerValue(request.headers.origin); if (!isAllowedNetworkRequest({ host, method: request.method, origin }, options.networkBoundary)) { return sendBrowserUnsupported(reply, request.id, "identity_unavailable", browserSupportRelease); } const path = request.url.split("?", 1)[0] ?? "/"; if (!browserGate || isSupportGateRequest(request.method, path)) return; const verified = verifyBrowserSupportCookie({ cookieHeader: headerValue(request.headers.cookie), release: browserSupportRelease, secChUa: headerValue(request.headers["sec-ch-ua"]), secret: browserSupportSecret, }); if (!verified.supported) { return sendBrowserUnsupported(reply, request.id, verified.reason, browserSupportRelease); } }); for (const route of ["/", "/app", "/app/*", "/admin", "/admin/*"]) { app.get(route, { schema: { hide: true } }, async (_request, reply) => { reply.type("text/html; charset=utf-8"); return supportGateHtml; }); } app.get("/support-gate.css", { schema: { hide: true } }, async (_request, reply) => { reply.type("text/css; charset=utf-8"); return supportGateCss; }); app.get("/support-gate.js", { schema: { hide: true } }, async (_request, reply) => { reply.type("text/javascript; charset=utf-8"); return supportGateJavaScript; }); app.get("/RELEASE.json", { schema: { hide: true } }, async () => ({ app_version: browserSupportRelease?.appVersion ?? null, browsers: supportedBrowserSummary(browserSupportRelease), })); app.get("/healthz", { schema: { hide: true } }, async () => ({ bind_scope: "loopback", port: 43121, status: "ready", })); app.get( "/api/v1/assets/public/:resourceVersion/:assetId", { schema: { hide: true } }, async (request, reply) => { const { assetId, resourceVersion } = request.params as { assetId?: string; resourceVersion?: string }; const resource = assetId && resourceVersion ? options.publicAssets?.read(resourceVersion, assetId) : undefined; if (!resource) return reply.code(404).send(); reply.type(resource.mimeType); reply.header("Cache-Control", "public, max-age=31536000, immutable"); reply.header("Content-Disposition", "inline"); reply.header("ETag", `"sha256-${resource.sha256}"`); return resource.bytes; }, ); app.post( "/api/v1/support/check", { attachValidation: true, schema: { body: BrowserSupportRequestSchema, operationId: "checkBrowserSupport", response: { 200: BrowserSupportSuccessSchema, 426: ErrorEnvelopeSchema, }, tags: ["Browser support"], }, }, async (request, reply) => { const checked = checkBrowserSupport( request.validationError ? undefined : request.body, { secChUa: headerValue(request.headers["sec-ch-ua"]), secChUaFullVersionList: headerValue(request.headers["sec-ch-ua-full-version-list"]), secChUaPlatform: headerValue(request.headers["sec-ch-ua-platform"]), }, browserSupportRelease, ); if (!checked.supported || !browserSupportRelease) { return sendBrowserUnsupported( reply, request.id, checked.supported ? "version_unsupported" : checked.reason, browserSupportRelease, ); } const cookie = createBrowserSupportCookie( browserSupportSecret, browserSupportRelease, checked.identity, ); reply.header( "Set-Cookie", `${browserSupportCookieName}=${cookie}; Max-Age=${browserSupportCookieMaxAgeSeconds}; Path=/; HttpOnly; SameSite=Strict`, ); return { app_version: browserSupportRelease.appVersion, browser: checked.identity, status: "supported" as const, supported_browsers: supportedBrowserSummary(browserSupportRelease), }; }, ); app.get( "/api/v1/bootstrap", { schema: { operationId: "getBootstrap", response: { 200: BootstrapResponseSchema, 426: ErrorEnvelopeSchema, }, tags: ["Bootstrap"], }, }, async () => bootstrap(), ); app.get( "/api/v1/events", { schema: { operationId: "getEvents", response: { 200: { content: { "text/event-stream": { schema: SseEventSchema, }, }, description: "Non-sensitive state change hints. REST remains authoritative.", }, 426: ErrorEnvelopeSchema, }, tags: ["State events"], }, }, (request, reply) => { reply.hijack(); reply.raw.setHeader("Cache-Control", "no-store"); reply.raw.setHeader("Connection", "keep-alive"); reply.raw.setHeader("Content-Type", "text/event-stream; charset=utf-8"); reply.raw.setHeader("X-Correlation-Id", request.id); reply.raw.writeHead(200); reply.raw.write(": connected\n\n"); const unsubscribe = eventHub.connect( (event) => { reply.raw.write(`id: ${event.event_id}\n`); reply.raw.write(`data: ${JSON.stringify(event)}\n\n`); }, () => reply.raw.end(), ); request.raw.once("close", unsubscribe); }, ); return app; }