48 lines
1.6 KiB
JavaScript
48 lines
1.6 KiB
JavaScript
import { createReadStream } from "node:fs";
|
|
import { access, stat } from "node:fs/promises";
|
|
import { createServer } from "node:http";
|
|
import path from "node:path";
|
|
|
|
const root = path.resolve(process.argv[2] ?? "artifacts/storybook/static");
|
|
const port = Number(process.argv[3] ?? 6006);
|
|
const contentTypes = /** @type {Readonly<Record<string, string>>} */ ({
|
|
".css": "text/css; charset=utf-8",
|
|
".html": "text/html; charset=utf-8",
|
|
".js": "text/javascript; charset=utf-8",
|
|
".json": "application/json; charset=utf-8",
|
|
".svg": "image/svg+xml",
|
|
".png": "image/png",
|
|
});
|
|
|
|
await access(root);
|
|
const server = createServer(async (request, response) => {
|
|
try {
|
|
const url = new URL(request.url ?? "/", `http://127.0.0.1:${port}`);
|
|
const decoded = decodeURIComponent(url.pathname);
|
|
const requested = path.resolve(root, `.${decoded}`);
|
|
if (requested !== root && !requested.startsWith(`${root}${path.sep}`)) {
|
|
response.writeHead(403).end();
|
|
return;
|
|
}
|
|
const details = await stat(requested).catch(() => null);
|
|
const file = details?.isDirectory()
|
|
? path.join(requested, "index.html")
|
|
: requested;
|
|
await access(file);
|
|
response.writeHead(200, {
|
|
"Content-Type":
|
|
contentTypes[path.extname(file)] ?? "application/octet-stream",
|
|
"Cache-Control": "no-store",
|
|
});
|
|
createReadStream(file).pipe(response);
|
|
} catch {
|
|
response.writeHead(404).end();
|
|
}
|
|
});
|
|
server.listen(port, "127.0.0.1", () => {
|
|
process.stdout.write(`Static evidence server: ${root} on ${port}\n`);
|
|
});
|
|
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
process.on(signal, () => server.close(() => process.exit(0)));
|
|
}
|