const fs = require('node:fs') const http = require('node:http') const path = require('node:path') const PORT = Number(process.env.PORT || 3001) const API_UPSTREAM = process.env.API_UPSTREAM || 'http://127.0.0.1:6000' const DIST_DIR = path.join(__dirname, 'dist') 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', '.map': 'application/json; charset=utf-8', '.png': 'image/png', '.svg': 'image/svg+xml', '.webp': 'image/webp', } function send(res, status, body, headers = {}) { res.writeHead(status, headers) res.end(body) } function proxyRequest(req, res) { const target = new URL(req.url, API_UPSTREAM) const proxy = http.request( target, { method: req.method, headers: { ...req.headers, host: target.host, }, }, (proxyRes) => { res.writeHead(proxyRes.statusCode || 502, proxyRes.headers) proxyRes.pipe(res) }, ) proxy.on('error', (error) => { send( res, 502, JSON.stringify({ error: 'API proxy failed', detail: error.message }), { 'content-type': 'application/json; charset=utf-8' }, ) }) req.pipe(proxy) } function serveStatic(req, res) { const requestPath = decodeURIComponent(new URL(req.url, `http://localhost:${PORT}`).pathname) const relativePath = requestPath === '/' ? '/index.html' : requestPath const filePath = path.normalize(path.join(DIST_DIR, relativePath)) if (!filePath.startsWith(DIST_DIR)) { return send(res, 403, 'Forbidden', { 'content-type': 'text/plain; charset=utf-8' }) } fs.readFile(filePath, (error, data) => { if (error) { fs.readFile(path.join(DIST_DIR, 'index.html'), (indexError, indexData) => { if (indexError) { return send(res, 404, 'Build not found. Run npm run build first.', { 'content-type': 'text/plain; charset=utf-8', }) } send(res, 200, indexData, { 'content-type': mimeTypes['.html'] }) }) return } const ext = path.extname(filePath) send(res, 200, data, { 'content-type': mimeTypes[ext] || 'application/octet-stream', 'cache-control': ext === '.html' ? 'no-cache' : 'public, max-age=31536000, immutable', }) }) } http .createServer((req, res) => { if (req.url === '/health' || req.url.startsWith('/api/')) { proxyRequest(req, res) return } serveStatic(req, res) }) .listen(PORT, '0.0.0.0', () => { console.log(`tssbot-web serving http://localhost:${PORT}`) console.log(`proxying API requests to ${API_UPSTREAM}`) })