56 lines
2.1 KiB
TypeScript
56 lines
2.1 KiB
TypeScript
// Production static-file server for the built SPA, run by Bun (replaces nginx).
|
|
// Serves apps/frontend/dist and reverse-proxies backend routes so the
|
|
// session cookie stays first-party. Dev still uses Vite (see vite.config.ts).
|
|
|
|
const PORT = Number(process.env.PORT ?? 8080);
|
|
const BACKEND_URL = process.env.BACKEND_URL ?? "http://localhost:3001";
|
|
const DIST = "./dist";
|
|
|
|
function isProxied(pathname: string): boolean {
|
|
return (
|
|
pathname === "/health" ||
|
|
pathname.startsWith("/auth") ||
|
|
pathname.startsWith("/api") ||
|
|
pathname.startsWith("/internal")
|
|
);
|
|
}
|
|
|
|
// Forward the request to the backend, preserving method/headers/body/cookies.
|
|
async function proxy(req: Request, url: URL): Promise<Response> {
|
|
const target = new URL(url.pathname + url.search, BACKEND_URL);
|
|
const headers = new Headers(req.headers);
|
|
// Let fetch set Host from the target URL (Cloud Run routes by Host).
|
|
headers.delete("host");
|
|
headers.set("x-forwarded-host", url.host);
|
|
headers.set("x-forwarded-proto", url.protocol.replace(":", ""));
|
|
|
|
const init: RequestInit = { method: req.method, headers, redirect: "manual" };
|
|
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
init.body = await req.arrayBuffer();
|
|
}
|
|
return fetch(target, init);
|
|
}
|
|
|
|
async function serveStatic(url: URL): Promise<Response> {
|
|
const pathname = url.pathname === "/" ? "/index.html" : url.pathname;
|
|
const file = Bun.file(`${DIST}${pathname}`);
|
|
if (await file.exists()) {
|
|
const headers: Record<string, string> =
|
|
pathname.startsWith("/assets/")
|
|
? { "Cache-Control": "public, max-age=31536000, immutable" }
|
|
: { "Cache-Control": "no-cache" };
|
|
return new Response(file, { headers });
|
|
}
|
|
// SPA fallback: serve index.html for client-side routes.
|
|
return new Response(Bun.file(`${DIST}/index.html`), { headers: { "Cache-Control": "no-cache" } });
|
|
}
|
|
|
|
Bun.serve({
|
|
port: PORT,
|
|
fetch(req) {
|
|
const url = new URL(req.url);
|
|
return isProxied(url.pathname) ? proxy(req, url) : serveStatic(url);
|
|
},
|
|
});
|
|
|
|
console.log(`cerebrus-frontend serving on :${PORT} (proxy backend routes -> ${BACKEND_URL})`);
|