import http from 'node:http'; import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const distDir = path.join(__dirname, 'dist'); const port = Number(process.env.PORT || 3000); const host = process.env.HOST || '127.0.0.1'; const mimeTypes = { '.html': 'text/html; charset=utf-8', '.js': 'application/javascript; charset=utf-8', '.mjs': 'application/javascript; charset=utf-8', '.css': 'text/css; charset=utf-8', '.svg': 'image/svg+xml', '.json': 'application/json; charset=utf-8', '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.webp': 'image/webp', '.ico': 'image/x-icon', '.woff': 'font/woff', '.woff2': 'font/woff2', '.pdf': 'application/pdf', }; const sendFile = (res, filePath) => { fs.readFile(filePath, (error, content) => { if (error) { res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' }); res.end('Not found'); return; } const ext = path.extname(filePath).toLowerCase(); res.writeHead(200, { 'Content-Type': mimeTypes[ext] || 'application/octet-stream', 'Cache-Control': ext === '.html' ? 'no-cache' : 'public, max-age=31536000, immutable', }); res.end(content); }); }; const server = http.createServer((req, res) => { const urlPath = decodeURIComponent((req.url || '/').split('?')[0]); const safePath = path.normalize(urlPath).replace(/^(\.\.[/\\])+/, ''); let filePath = path.join(distDir, safePath === '/' ? 'index.html' : safePath); if (!filePath.startsWith(distDir)) { res.writeHead(403, { 'Content-Type': 'text/plain; charset=utf-8' }); res.end('Forbidden'); return; } fs.stat(filePath, (err, stats) => { if (!err && stats.isDirectory()) { filePath = path.join(filePath, 'index.html'); } fs.access(filePath, fs.constants.F_OK, (accessError) => { if (accessError) { sendFile(res, path.join(distDir, 'index.html')); return; } sendFile(res, filePath); }); }); }); server.listen(port, host, () => { console.log(`LinkedSign frontend running on http://${host}:${port}`); });