generated from Labyricorn/labyricorn-project-template
Post Step 4 Completion
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
import http from 'node:http';
|
||||
import { readFile, realpath, stat } from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const root = fileURLToPath(new URL('../', import.meta.url));
|
||||
const mime = { '.html': 'text/html; charset=utf-8', '.js': 'text/javascript; charset=utf-8', '.css': 'text/css; charset=utf-8',
|
||||
'.json': 'application/json', '.png': 'image/png', '.jpg': 'image/jpeg', '.svg': 'image/svg+xml', '.wav': 'audio/wav', '.mp3': 'audio/mpeg' };
|
||||
export function createServer() {
|
||||
return http.createServer(async (req, res) => {
|
||||
try {
|
||||
if (!['GET', 'HEAD'].includes(req.method)) { res.writeHead(405); res.end('Method not allowed'); return; }
|
||||
const url = new URL(req.url, 'http://localhost');
|
||||
const pathname = decodeURIComponent(url.pathname);
|
||||
// Serve only the product surface and explicitly owned fixture subtree.
|
||||
const route = pathname === '/' ? '/public/index.html' : pathname;
|
||||
const mount = ['/public/', '/src/', '/test-fixtures/'].find(prefix => route.startsWith(prefix));
|
||||
if (!mount || route.includes('\\') || route.includes('\0')) throw new Error('Not found');
|
||||
const base = await realpath(path.join(root, mount));
|
||||
let candidate = await realpath(path.join(root, route));
|
||||
const relative = path.relative(base, candidate);
|
||||
if (relative.startsWith('..') || path.isAbsolute(relative)) throw new Error('Not found');
|
||||
if ((await stat(candidate)).isDirectory()) candidate = await realpath(path.join(candidate, 'index.html'));
|
||||
const finalRelative = path.relative(base, candidate);
|
||||
if (finalRelative.startsWith('..') || path.isAbsolute(finalRelative)) throw new Error('Not found');
|
||||
const content = await readFile(candidate);
|
||||
res.writeHead(200, { 'Content-Type': mime[path.extname(candidate)] ?? 'application/octet-stream',
|
||||
'Cache-Control': 'no-store', 'X-Content-Type-Options': 'nosniff', 'Content-Length': content.length });
|
||||
res.end(req.method === 'HEAD' ? undefined : content);
|
||||
} catch { res.writeHead(404, { 'Content-Type': 'text/plain' }); res.end('Not found'); }
|
||||
});
|
||||
}
|
||||
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
||||
const port = Number(process.env.PORT ?? 4173);
|
||||
createServer().listen(port, '127.0.0.1', () => console.log(`XZBT-NGN: http://127.0.0.1:${port}`));
|
||||
}
|
||||
Reference in New Issue
Block a user