/* Local dev server: serves ./site and proxies /ws to the relay, so the pages run same-origin exactly like they do behind nginx in production. Usage: OBSERVER_TOKEN=test cargo run # in relay/, terminal 1 node devserver.mjs # terminal 2 -> http://localhost:8080 Env: PORT (default 8080), RELAY (default 127.0.0.1:8770). */ import { createServer } from "node:http"; import { connect } from "node:net"; import { readFile } from "node:fs/promises"; import { extname, join, normalize } from "node:path"; import { fileURLToPath } from "node:url"; const PORT = Number(process.env.PORT || 8080); const [RELAY_HOST, RELAY_PORT] = (process.env.RELAY || "127.0.0.1:8770").split(":"); const SITE = join(fileURLToPath(new URL(".", import.meta.url)), "site"); const MIME = { ".html": "text/html; charset=utf-8", ".css": "text/css; charset=utf-8", ".js": "text/javascript; charset=utf-8", ".mp3": "audio/mpeg", ".wav": "audio/wav", }; const server = createServer(async (req, res) => { let path = decodeURIComponent(req.url.split("?")[0]); if (path === "/") path = "/clock.html"; if (path === "/control") path = "/control.html"; // keep it inside SITE const file = join(SITE, normalize(path)); if (!file.startsWith(SITE)) { res.writeHead(403).end("forbidden"); return; } try { const body = await readFile(file); res.writeHead(200, { "content-type": MIME[extname(file)] || "application/octet-stream" }); res.end(body); } catch { res.writeHead(404).end("not found"); } }); // Proxy the WebSocket upgrade straight through to the relay. server.on("upgrade", (req, socket, head) => { const upstream = connect(Number(RELAY_PORT), RELAY_HOST, () => { const lines = [`${req.method} ${req.url} HTTP/1.1`]; for (let i = 0; i < req.rawHeaders.length; i += 2) { lines.push(`${req.rawHeaders[i]}: ${req.rawHeaders[i + 1]}`); } upstream.write(lines.join("\r\n") + "\r\n\r\n"); if (head && head.length) upstream.write(head); socket.pipe(upstream); upstream.pipe(socket); }); const bail = () => socket.destroy(); upstream.on("error", bail); socket.on("error", () => upstream.destroy()); }); server.listen(PORT, () => { console.log(`observer-effect dev server: http://localhost:${PORT}/ (control: /control)`); console.log(`proxying /ws -> ${RELAY_HOST}:${RELAY_PORT}`); });