Files

68 lines
2.5 KiB
JavaScript

import fs from "fs";
import path from "path";
import https from "https";
import httpProxy from "http-proxy";
import { fileURLToPath } from "url";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const PORT = Number(process.env.HTTPS_PORT || 8443);
const BACKEND = process.env.BACKEND_TARGET || "http://127.0.0.1:8000";
const STATIC_DIR = path.resolve(__dirname, "../web");
const TLS_KEY_FILE = process.env.TLS_KEY_FILE || path.resolve(__dirname, "../certs/dev.key");
const TLS_CERT_FILE = process.env.TLS_CERT_FILE || path.resolve(__dirname, "../certs/dev.crt");
const tls = {
key: fs.readFileSync(TLS_KEY_FILE),
cert: fs.readFileSync(TLS_CERT_FILE),
};
const proxy = httpProxy.createProxyServer({ target: BACKEND, changeOrigin: true, ws: true, xfwd: true });
proxy.on("error", (err, req, res) => {
console.error("[proxy]", req?.url, err.message);
if (res && typeof res.writeHead === "function" && !res.headersSent) {
res.writeHead(502, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: "bad gateway" }));
}
});
const MIME = {
".html": "text/html",
".js": "application/javascript",
".css": "text/css",
".json": "application/json",
".png": "image/png",
".svg": "image/svg+xml",
".ico": "image/x-icon",
};
const ADMIN_PUBLIC = process.env.ADMIN_PUBLIC === "true";
const server = https.createServer(tls, (req, res) => {
const url = req.url || "/";
// Block /admin on public gateway unless explicitly allowed
if ((url === "/admin" || url.startsWith("/admin/")) && !ADMIN_PUBLIC) {
res.writeHead(404, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: "not found" }));
return;
}
if (url.startsWith("/healthz") || url.startsWith("/api/") || url.startsWith("/ws/") || url === "/admin" || url.startsWith("/admin/")) {
proxy.web(req, res);
return;
}
let fp = path.join(STATIC_DIR, url === "/" ? "index.html" : url);
if (!fs.existsSync(fp)) fp = path.join(STATIC_DIR, "index.html");
const ext = path.extname(fp);
fs.readFile(fp, (err, data) => {
if (err) { res.writeHead(404); res.end("Not Found"); return; }
res.writeHead(200, { "Content-Type": MIME[ext] || "application/octet-stream", "Cache-Control": "no-cache" });
res.end(data);
});
});
server.on("upgrade", (req, socket, head) => {
if ((req.url || "").startsWith("/ws/")) proxy.ws(req, socket, head);
else socket.destroy();
});
server.listen(PORT, "0.0.0.0", () => console.log(`[gateway] https://0.0.0.0:${PORT} -> ${BACKEND}`));