/** * Custom Next.js standalone server. * Proxies /api/* requests to BACKEND_URL (resolved at RUNTIME, not build time). * This is needed because next.config.ts rewrites are baked in at build time. */ const { createServer } = require("http"); const { parse } = require("url"); const next = require("next"); const port = parseInt(process.env.PORT || "3000", 10); const hostname = process.env.HOSTNAME || "0.0.0.0"; const dev = process.env.NODE_ENV !== "production"; const backendUrl = (process.env.BACKEND_URL || "http://127.0.0.1:8000").replace(/\/$/, ""); const app = next({ dev, hostname, port }); const handle = app.getRequestHandler(); app.prepare().then(() => { createServer(async (req, res) => { try { const parsedUrl = parse(req.url, true); const { pathname } = parsedUrl; // Proxy /api/* to backend if (pathname.startsWith("/api/")) { const http = require("http"); const targetUrl = new URL(req.url, backendUrl); const proxyReq = http.request( targetUrl, { method: req.method, headers: { ...req.headers, host: targetUrl.host, }, }, (proxyRes) => { res.writeHead(proxyRes.statusCode, proxyRes.headers); proxyRes.pipe(res, { end: true }); } ); proxyReq.on("error", (err) => { console.error(`Proxy error for ${req.url}:`, err.message); res.writeHead(502); res.end("Bad Gateway"); }); req.pipe(proxyReq, { end: true }); return; } // All other requests -> Next.js await handle(req, res, parsedUrl); } catch (err) { console.error("Error occurred handling", req.url, err); res.writeHead(500); res.end("internal server error"); } }).listen(port, hostname, () => { console.log(`> Ready on http://${hostname}:${port}`); console.log(`> API proxy: ${backendUrl}`); }); });