68 lines
2.5 KiB
JavaScript
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}`));
|