import { createReadStream, existsSync, statSync } from 'node:fs'; import { extname, isAbsolute, join, normalize } from 'node:path'; import { createServer } from 'node:http'; import { connect as connectNet } from 'node:net'; import { Readable } from 'node:stream'; import { connect as connectTls } from 'node:tls'; const port = Number(process.env.PORT ?? 5173); const distDirName = process.env.APP_DIST_DIR ?? 'app-dist'; const rootDir = normalize(isAbsolute(distDirName) ? distDirName : join(process.cwd(), distDirName)); const workServerUrl = new URL(process.env.WORK_SERVER_URL ?? 'http://127.0.0.1:3100'); const proxyPrefixes = ['/api', '/.codex_chat', '/public/.codex_chat', '/ws/chat']; const mimeTypes = { '.css': 'text/css; charset=utf-8', '.html': 'text/html; charset=utf-8', '.ico': 'image/x-icon', '.js': 'text/javascript; charset=utf-8', '.json': 'application/json; charset=utf-8', '.mjs': 'text/javascript; charset=utf-8', '.png': 'image/png', '.svg': 'image/svg+xml', '.txt': 'text/plain; charset=utf-8', '.webmanifest': 'application/manifest+json; charset=utf-8', '.woff2': 'font/woff2', }; function canListenOnPort(candidatePort, host = '0.0.0.0') { return new Promise((resolve, reject) => { const probeServer = createServer(); probeServer.once('error', (error) => { probeServer.close(() => { if (error && typeof error === 'object' && 'code' in error && error.code === 'EADDRINUSE') { resolve(false); return; } reject(error); }); }); probeServer.once('listening', () => { probeServer.close((closeError) => { if (closeError) { reject(closeError); return; } resolve(true); }); }); probeServer.listen(candidatePort, host); }); } async function findAvailablePort(initialPort, host = '0.0.0.0', maxAttempts = 20) { for (let offset = 0; offset < maxAttempts; offset += 1) { const candidatePort = initialPort + offset; const available = await canListenOnPort(candidatePort, host); if (available) { return candidatePort; } if (offset === 0) { console.warn(`Port ${initialPort} is in use, trying another one...`); } } throw new Error(`No available port found from ${initialPort} to ${initialPort + maxAttempts - 1}.`); } function resolveCacheControl(resolvedPath, extension) { const normalizedPath = resolvedPath.replace(/\\/g, '/'); if (extension === '.html') { return 'no-cache'; } if (normalizedPath.endsWith('/sw.js') || extension === '.webmanifest') { return 'no-store, no-cache, max-age=0, must-revalidate'; } return 'public, max-age=31536000, immutable'; } function looksLikeStaticAsset(requestedPath) { const normalizedPath = requestedPath.split('?')[0] ?? requestedPath; const extension = extname(normalizedPath); return extension.length > 0; } function resolvePath(urlPath) { const decodedPath = decodeURIComponent(urlPath.split('?')[0] || '/'); const requestedPath = decodedPath === '/' ? '/index.html' : decodedPath; const absolutePath = normalize(join(rootDir, requestedPath)); if (!absolutePath.startsWith(rootDir)) { return null; } if (existsSync(absolutePath) && statSync(absolutePath).isFile()) { return absolutePath; } if (looksLikeStaticAsset(requestedPath)) { return null; } return join(rootDir, 'index.html'); } function shouldProxyRequest(urlPath = '/') { const normalizedPath = urlPath.split('?')[0] ?? urlPath; return proxyPrefixes.some((prefix) => normalizedPath === prefix || normalizedPath.startsWith(`${prefix}/`)); } function readRequestBody(request) { return new Promise((resolve, reject) => { const chunks = []; request.on('data', (chunk) => { chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)); }); request.on('end', () => { resolve(chunks.length > 0 ? Buffer.concat(chunks) : null); }); request.on('error', reject); }); } async function proxyRequest(request, response) { const targetUrl = new URL(request.url ?? '/', workServerUrl); const headers = new Headers(); Object.entries(request.headers).forEach(([key, value]) => { if (value == null || key.toLowerCase() === 'host' || key.toLowerCase() === 'connection') { return; } if (Array.isArray(value)) { value.forEach((item) => headers.append(key, item)); return; } headers.set(key, value); }); const method = request.method ?? 'GET'; const body = method === 'GET' || method === 'HEAD' ? undefined : await readRequestBody(request); try { const upstreamResponse = await fetch(targetUrl, { method, headers, body, }); response.writeHead( upstreamResponse.status, Object.fromEntries(upstreamResponse.headers.entries()), ); if (!upstreamResponse.body) { response.end(); return; } Readable.fromWeb(upstreamResponse.body).pipe(response); } catch (error) { response.writeHead(502, { 'Content-Type': 'application/json; charset=utf-8' }); response.end( JSON.stringify({ message: error instanceof Error ? error.message : 'Failed to proxy request to work server.', }), ); } } const server = createServer(async (request, response) => { if (shouldProxyRequest(request.url ?? '/')) { await proxyRequest(request, response); return; } const resolvedPath = resolvePath(request.url ?? '/'); if (!resolvedPath || !existsSync(resolvedPath)) { response.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' }); response.end('Not Found'); return; } const extension = extname(resolvedPath); const contentType = mimeTypes[extension] ?? 'application/octet-stream'; response.writeHead(200, { 'Content-Type': contentType, 'Cache-Control': resolveCacheControl(resolvedPath, extension), }); createReadStream(resolvedPath).pipe(response); }); server.on('upgrade', (request, socket, head) => { if (!shouldProxyRequest(request.url ?? '/')) { socket.destroy(); return; } const upstreamPort = Number( workServerUrl.port || (workServerUrl.protocol === 'https:' ? '443' : '80'), ); const upstreamSocket = workServerUrl.protocol === 'https:' ? connectTls(upstreamPort, workServerUrl.hostname, { servername: workServerUrl.hostname }) : connectNet(upstreamPort, workServerUrl.hostname); upstreamSocket.on('connect', () => { const headerLines = Object.entries(request.headers) .flatMap(([key, value]) => { if (value == null || key.toLowerCase() === 'host') { return []; } return Array.isArray(value) ? value.map((item) => `${key}: ${item}\r\n`) : [`${key}: ${value}\r\n`]; }) .join(''); const requestLine = `${request.method ?? 'GET'} ${request.url ?? '/'} HTTP/${request.httpVersion}\r\n`; upstreamSocket.write(`${requestLine}host: ${workServerUrl.host}\r\n${headerLines}\r\n`); if (head?.length) { upstreamSocket.write(head); } }); upstreamSocket.on('data', (chunk) => { socket.write(chunk); }); upstreamSocket.on('end', () => { socket.end(); }); upstreamSocket.on('error', () => { socket.destroy(); }); socket.on('data', (chunk) => { upstreamSocket.write(chunk); }); socket.on('end', () => { upstreamSocket.end(); }); socket.on('error', () => { upstreamSocket.destroy(); }); }); const host = '0.0.0.0'; const resolvedPort = await findAvailablePort(port, host); server.listen(resolvedPort, host, () => { console.log(`${distDirName} server listening on http://${host}:${resolvedPort}`); });