Files
TSSBOT-web/server.cjs
T
2026-05-14 15:43:32 +01:00

99 lines
2.7 KiB
JavaScript

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}`)
})