home-server/services/observer-effect/devserver.mjs

68 lines
2.3 KiB
JavaScript
Raw Normal View History

2026-06-15 21:22:41 -07:00
/* 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}`);
});