72 lines
2.3 KiB
JavaScript
72 lines
2.3 KiB
JavaScript
import http from "node:http";
|
|
import { readFile, stat } from "node:fs/promises";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
const rootDir = path.resolve(scriptDir, "../..");
|
|
const port = Number(process.env.HEROLL_MVP_PORT || 4173);
|
|
const parentPidFlag = process.argv.indexOf("--parent-pid");
|
|
const parentPid = parentPidFlag >= 0 ? Number(process.argv[parentPidFlag + 1]) : 0;
|
|
const mime = new Map([
|
|
[".html", "text/html; charset=utf-8"],
|
|
[".js", "text/javascript; charset=utf-8"],
|
|
[".mjs", "text/javascript; charset=utf-8"],
|
|
[".css", "text/css; charset=utf-8"],
|
|
[".json", "application/json; charset=utf-8"],
|
|
[".csv", "text/csv; charset=utf-8"],
|
|
[".png", "image/png"],
|
|
[".jpg", "image/jpeg"],
|
|
[".jpeg", "image/jpeg"],
|
|
[".svg", "image/svg+xml"],
|
|
[".wav", "audio/wav"],
|
|
[".ogg", "audio/ogg"]
|
|
]);
|
|
|
|
function safePath(urlPath) {
|
|
const decoded = decodeURIComponent(urlPath.split("?")[0] || "/");
|
|
const relative = decoded === "/" ? "mvp/index.html" : decoded.replace(/^\/+/, "");
|
|
const resolved = path.resolve(rootDir, relative);
|
|
if (!resolved.startsWith(rootDir + path.sep) && resolved !== rootDir) return null;
|
|
return resolved;
|
|
}
|
|
|
|
const server = http.createServer(async (req, res) => {
|
|
try {
|
|
let target = safePath(req.url || "/");
|
|
if (!target) {
|
|
res.writeHead(403).end("Forbidden");
|
|
return;
|
|
}
|
|
const info = await stat(target);
|
|
if (info.isDirectory()) target = path.join(target, "index.html");
|
|
const body = await readFile(target);
|
|
res.writeHead(200, {
|
|
"Content-Type": mime.get(path.extname(target).toLowerCase()) || "application/octet-stream",
|
|
"Cache-Control": "no-store",
|
|
"Cross-Origin-Resource-Policy": "same-origin"
|
|
});
|
|
res.end(body);
|
|
} catch (error) {
|
|
res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
|
|
res.end(`Not found\n${String(error)}`);
|
|
}
|
|
});
|
|
|
|
server.listen(port, "127.0.0.1", () => {
|
|
console.log(`HEROLL MVP running at http://127.0.0.1:${port}/mvp/`);
|
|
console.log("Press Ctrl+C to stop.");
|
|
});
|
|
|
|
if (Number.isInteger(parentPid) && parentPid > 0) {
|
|
const parentWatch = setInterval(() => {
|
|
try {
|
|
process.kill(parentPid, 0);
|
|
} catch {
|
|
clearInterval(parentWatch);
|
|
server.close(() => process.exit(0));
|
|
}
|
|
}, 1000);
|
|
parentWatch.unref();
|
|
}
|