spb/voyage2026
Public
HTML 95.5%
JavaScript 4.5%
1// Zero-dep static server for the voyage2026 itinerary.2// Usage: node server.mjs [port] — serves this directory, / -> index, /healthz -> ok3import { createServer } from "node:http";4import { readFile } from "node:fs/promises";5import { join, extname, normalize } from "node:path";6import { fileURLToPath } from "node:url";78const ROOT = fileURLToPath(new URL(".", import.meta.url));9const PORT = Number(process.argv[2] || process.env.PORT || 8060);10const INDEX = "itineraire-toronto-2026.html";1112const MIME = {13 ".html": "text/html; charset=utf-8",14 ".css": "text/css; charset=utf-8",15 ".js": "text/javascript; charset=utf-8",16 ".json": "application/json",17 ".png": "image/png",18 ".jpg": "image/jpeg",19 ".jpeg": "image/jpeg",20 ".webp": "image/webp",21 ".svg": "image/svg+xml",22 ".ico": "image/x-icon",23 ".pdf": "application/pdf",24};2526createServer(async (req, res) => {27 const url = new URL(req.url, "http://x");28 if (url.pathname === "/healthz") {29 res.writeHead(200, { "content-type": "text/plain" });30 return res.end("ok");31 }32 let path = decodeURIComponent(url.pathname);33 if (path === "/" || path === "/index.html") path = "/" + INDEX;34 const file = normalize(join(ROOT, path));35 if (!file.startsWith(ROOT)) {36 res.writeHead(403);37 return res.end("forbidden");38 }39 try {40 const body = await readFile(file);41 res.writeHead(200, {42 "content-type": MIME[extname(file).toLowerCase()] || "application/octet-stream",43 "cache-control": "public, max-age=300",44 });45 res.end(body);46 } catch {47 res.writeHead(404, { "content-type": "text/plain; charset=utf-8" });48 res.end("404 — page introuvable");49 }50}).listen(PORT, "0.0.0.0", () => {51 console.log(`voyage2026 static server on http://0.0.0.0:${PORT} (root: ${ROOT})`);52});53