import { createReadStream, realpathSync, statSync } from "node:fs"; import { createServer } from "node:http"; import { networkInterfaces } from "node:os"; import path from "node:path"; import { fileURLToPath } from "node:url"; const ROOT = realpathSync(path.dirname(fileURLToPath(import.meta.url))); const LAN_MODE = process.argv.includes("--lan"); const HOST = LAN_MODE ? "0.0.0.0" : process.env.HOST || "127.0.0.1"; const PORT = Number.parseInt(process.env.PORT || "4173", 10); const TYPES = new Map([ [".css", "text/css; charset=utf-8"], [".html", "text/html; charset=utf-8"], [".js", "text/javascript; charset=utf-8"], [".json", "application/json; charset=utf-8"], [".mjs", "text/javascript; charset=utf-8"], [".svg", "image/svg+xml"], ]); const PUBLIC_PATHS = new Set([ "/index.html", "/styles.css", "/js/app.js", "/js/charts.js", "/js/data.js", "/public/data/age_risk_curve.json", "/public/data/cohort_scorecard.json", "/public/data/coverage_quality.json", "/public/data/data_manifest.json", "/public/data/filter_catalog.json", "/public/data/model_diagnostics.json", "/public/data/overview_period_county.json", "/public/data/sha256_manifest.json", ]); function safeFile(requestUrl) { let pathname; try { pathname = decodeURIComponent(new URL(requestUrl, "http://local").pathname); } catch { return { file: null, status: 400 }; } if (pathname === "/") pathname = "/index.html"; if (!PUBLIC_PATHS.has(pathname)) return { file: null, status: 404 }; try { const candidate = realpathSync(path.join(ROOT, pathname.slice(1))); if (!candidate.startsWith(`${ROOT}${path.sep}`) || !statSync(candidate).isFile()) { return { file: null, status: 404 }; } return { file: candidate, status: 200 }; } catch { return { file: null, status: 404 }; } } // Tailscale and similar VPN tunnels show up as extra "LAN" interfaces but are not // useful for an in-room demo, so keep them out of the printed URL list. function isTunnelInterface(interfaceName, address) { if (/^(utun|tailscale|tun|ppp|ipsec)/i.test(interfaceName)) return true; const [first, second] = address.split(".").map(Number); return first === 100 && second >= 64 && second <= 127; // 100.64.0.0/10 (CGNAT/tailnet) } function displayUrls(host, port) { if (host !== "0.0.0.0") { const displayHost = host === "::" ? "::1" : host; const urlHost = displayHost.includes(":") ? `[${displayHost}]` : displayHost; return [{ label: "Open", url: `http://${urlHost}:${port}` }]; } const entries = [{ label: "This computer", url: `http://127.0.0.1:${port}` }]; const seen = new Set(); for (const [interfaceName, addresses] of Object.entries(networkInterfaces())) { for (const address of addresses || []) { if (address.family !== "IPv4" || address.internal || seen.has(address.address)) continue; if (isTunnelInterface(interfaceName, address.address)) continue; seen.add(address.address); entries.push({ label: `Other devices (${interfaceName})`, url: `http://${address.address}:${port}`, }); } } return entries; } const server = createServer((request, response) => { if (!request.url || !["GET", "HEAD"].includes(request.method || "")) { response.writeHead(405, { Allow: "GET, HEAD" }); response.end("Method not allowed"); return; } const resolved = safeFile(request.url); if (!resolved.file) { response.writeHead(resolved.status, { "Content-Type": "text/plain; charset=utf-8" }); response.end(resolved.status === 400 ? "Bad request" : "Not found"); return; } const file = resolved.file; const extension = path.extname(file).toLowerCase(); response.writeHead(200, { "Content-Type": TYPES.get(extension) || "application/octet-stream", "Cache-Control": "no-store", "X-Content-Type-Options": "nosniff", "Referrer-Policy": "no-referrer", }); if (request.method === "HEAD") { response.end(); return; } createReadStream(file).on("error", () => response.destroy()).pipe(response); }); server.on("error", (error) => { if (error.code === "EADDRINUSE") { process.stderr.write( `Dashboard port ${PORT} is already in use. Stop the existing server or set a different PORT.\n`, ); } else { process.stderr.write(`Dashboard server could not start (${error.code || "unknown error"}).\n`); } process.exitCode = 1; }); server.listen(PORT, HOST, () => { const address = server.address(); const actualPort = typeof address === "object" && address ? address.port : PORT; const urls = displayUrls(HOST, actualPort) .map(({ label, url }) => ` ${label}: ${url}`) .join("\n"); process.stdout.write(`Utah Vehicle Health dashboard:\n${urls}\n`); });