#!/usr/bin/env node /** * Minimal server for e2e runs: serves the built SPA (apps/web/dist) with * SPA fallback and proxies /api/* to the api process — mirroring what * nginx + Caddy do in production, without a dev server that may die * under CI memory pressure. Node builtins only. * * PORT=5173 API_TARGET=http://127.0.0.1:3001 node scripts/e2e-static-server.mjs */ import { readFile } from 'node:fs/promises'; import { createServer, request as httpRequest } from 'node:http'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; const DIST = path.join(path.dirname(fileURLToPath(import.meta.url)), '../apps/web/dist'); const PORT = Number(process.env.PORT ?? 5173); const API_TARGET = new URL(process.env.API_TARGET ?? 'http://127.0.0.1:3001'); const MIME = { '.html': 'text/html; charset=utf-8', '.js': 'application/javascript', '.css': 'text/css', '.json': 'application/json', '.svg': 'image/svg+xml', '.png': 'image/png', '.ico': 'image/x-icon', '.woff2': 'font/woff2', }; function proxyApi(req, res) { const upstream = httpRequest( { hostname: API_TARGET.hostname, port: API_TARGET.port, path: req.url, method: req.method, headers: { ...req.headers, host: `${API_TARGET.hostname}:${API_TARGET.port}` }, }, (upstreamRes) => { res.writeHead(upstreamRes.statusCode ?? 502, upstreamRes.headers); upstreamRes.pipe(res); }, ); upstream.on('error', () => { res.writeHead(502).end('api unreachable'); }); req.pipe(upstream); } async function serveStatic(req, res) { const urlPath = new URL(req.url, 'http://x').pathname; // Path traversal guard: resolve inside dist or fall back to the shell. const filePath = path.normalize(path.join(DIST, urlPath)); const target = filePath.startsWith(DIST) && path.extname(filePath) ? filePath : path.join(DIST, 'index.html'); try { const body = await readFile(target); res.writeHead(200, { 'Content-Type': MIME[path.extname(target)] ?? 'application/octet-stream', }); res.end(body); } catch { res.writeHead(404).end('not found'); } } createServer((req, res) => { if (req.url.startsWith('/api/')) proxyApi(req, res); else void serveStatic(req, res); }).listen(PORT, () => console.log(`e2e static server on :${PORT} → ${API_TARGET.href}`));