// HuggingFlow — status dashboard + reverse proxy to nginx (DeerFlow) // Public port 7860 → nginx:7861 "use strict"; const http = require("http"); const net = require("net"); const fs = require("fs"); const PUBLIC_PORT = parseInt(process.env.PORT || "7860", 10); const NGINX_PORT = parseInt(process.env.NGINX_PORT || "7861", 10); const NGINX_HOST = "127.0.0.1"; const BACKEND_PORT = parseInt(process.env.BACKEND_PORT || "8001", 10); const FRONTEND_PORT = parseInt(process.env.FRONTEND_PORT || "3000", 10); const startTime = Date.now(); // ── Env-derived display values ───────────────────────────────────────────── const LLM_MODEL = process.env.LLM_MODEL || "Not configured"; const HF_TOKEN = !!process.env.HF_TOKEN; const SYNC_INTERVAL = process.env.SYNC_INTERVAL || "600"; const SERPER_KEY = !!process.env.SERPER_API_KEY; const TAVILY_KEY = !!process.env.TAVILY_API_KEY; const SEARCH_LABEL = SERPER_KEY ? "Serper (Google)" : TAVILY_KEY ? "Tavily" : "DuckDuckGo"; const SYNC_STATUS_FILE = "/tmp/huggingflow-sync-status.json"; const KEEPALIVE_STATUS_FILE = "/tmp/huggingflow-cloudflare-keepalive-status.json"; // ── Helpers ──────────────────────────────────────────────────────────────── function parseUrl(raw) { try { return new URL(raw, "http://localhost"); } catch { return new URL("http://localhost/"); } } function escapeHtml(v) { return String(v) .replace(/&/g,"&").replace(//g,">").replace(/"/g,"""); } function formatUptime(ms) { const t = Math.floor(ms / 1000); const d = Math.floor(t / 86400), h = Math.floor((t % 86400) / 3600), m = Math.floor((t % 3600) / 60); if (d) return `${d}d ${h}h ${m}m`; if (h) return `${h}h ${m}m`; return `${m}m`; } function readJson(path) { try { return JSON.parse(fs.readFileSync(path, "utf8")); } catch { return null; } } function getSyncStatus() { const s = readJson(SYNC_STATUS_FILE); if (s) return s; if (HF_TOKEN) return { status: "configured", message: `Backup enabled. First sync in ${SYNC_INTERVAL}s.` }; return { status: "disabled", message: "HF_TOKEN not set." }; } function getKeepaliveStatus() { return readJson(KEEPALIVE_STATUS_FILE); } function probe(host, port, path, timeout = 1500) { return new Promise(resolve => { const req = http.get({ hostname: host, port, path, timeout }, res => { res.resume(); resolve(res.statusCode >= 200 && res.statusCode < 400); }); req.on("timeout", () => { req.destroy(); resolve(false); }); req.on("error", () => resolve(false)); }); } // TCP-only liveness check — confirms port is open without triggering any HTTP/SSR. // Used for the Next.js frontend probe so we don't hit auth rate-limited endpoints. function tcpProbe(host, port, timeout = 1500) { return new Promise(resolve => { const sock = net.connect({ host, port }, () => { sock.destroy(); resolve(true); }); sock.setTimeout(timeout); sock.on("timeout", () => { sock.destroy(); resolve(false); }); sock.on("error", () => resolve(false)); }); } // ── Dashboard HTML renderer ──────────────────────────────────────────────── function badge(label, tone = "neutral") { return `${escapeHtml(label)}`; } function tile({ title, value, detail = "", tone = "neutral", meta = "" }) { return `
${escapeHtml(title)}
${value}
${detail ? `
${escapeHtml(detail)}
` : ""} ${meta ? `
${meta}
` : ""}
`; } function renderDashboard({ backendUp, frontendUp, uptimeHuman, sync, keepalive }) { const appOnline = backendUp && frontendUp; const syncStatus = String(sync?.status || "unknown"); const syncTone = ["success","restored","synced","configured"].includes(syncStatus) ? "ok" : syncStatus === "error" ? "off" : syncStatus === "disabled" ? "warn" : "neutral"; const kaOk = keepalive?.configured === true; const kaTone = kaOk ? "ok" : process.env.CLOUDFLARE_WORKERS_TOKEN ? "warn" : "neutral"; const kaLabel = kaOk ? "CF Cron" : (process.env.CLOUDFLARE_WORKERS_TOKEN ? "Pending" : "Off"); const tiles = [ tile({ title: "DeerFlow App", value: badge(appOnline ? "Online" : "Starting…", appOnline ? "ok" : "warn"), detail: appOnline ? `Backend :8001 · Frontend :3000` : "Services initializing…", tone: appOnline ? "ok" : "warn", }), tile({ title: "Model", value: `${escapeHtml(LLM_MODEL)}`, detail: "Configured LLM", tone: "neutral", }), tile({ title: "Runtime", value: escapeHtml(uptimeHuman), detail: `Port ${PUBLIC_PORT} · nginx :${NGINX_PORT}`, tone: "neutral", }), tile({ title: "Search", value: badge(SEARCH_LABEL, SERPER_KEY || TAVILY_KEY ? "ok" : "neutral"), detail: SERPER_KEY ? "SERPER_API_KEY set" : TAVILY_KEY ? "TAVILY_API_KEY set" : "No API key — using DuckDuckGo", tone: SERPER_KEY || TAVILY_KEY ? "ok" : "neutral", }), tile({ title: "Backup", value: badge(syncStatus.toUpperCase(), syncTone), detail: sync?.message || "No status yet", tone: syncTone, meta: sync?.timestamp ? `` : "", }), tile({ title: "Keep Awake", value: badge(kaLabel, kaTone), detail: kaOk ? `Pings ${escapeHtml(keepalive.targetUrl || "/health")} · ${escapeHtml(keepalive.cron || "*/10 * * * *")}` : "Set CLOUDFLARE_WORKERS_TOKEN to enable", tone: kaTone, }), ].join("\n"); return ` HuggingFlow Dashboard

🦌 HuggingFlow

DeerFlow Research Agent · Status Dashboard
${appOnline ? "Open DeerFlow Workspace →" : "DeerFlow is starting…"}
${tiles}
`; } // ── Request handler ──────────────────────────────────────────────────────── const server = http.createServer(async (req, res) => { const url = parseUrl(req.url || "/"); const pathname = url.pathname; // Health — JSON for HEALTHCHECK and load balancers if (pathname === "/health") { const [backendUp, frontendUp] = await Promise.all([ probe(NGINX_HOST, BACKEND_PORT, "/health"), tcpProbe(NGINX_HOST, FRONTEND_PORT), ]); const ok = backendUp && frontendUp; res.writeHead(ok ? 200 : 503, { "Content-Type": "application/json" }); return res.end(JSON.stringify({ status: ok ? "ok" : "degraded", backendUp, frontendUp, uptime: formatUptime(Date.now() - startTime), sync: getSyncStatus(), keepalive: getKeepaliveStatus(), })); } // Status — full JSON payload if (pathname === "/status") { const [backendUp, frontendUp] = await Promise.all([ probe(NGINX_HOST, BACKEND_PORT, "/health"), tcpProbe(NGINX_HOST, FRONTEND_PORT), ]); res.writeHead(200, { "Content-Type": "application/json" }); return res.end(JSON.stringify({ model: LLM_MODEL, search: SEARCH_LABEL, uptime: formatUptime(Date.now() - startTime), backendUp, frontendUp, sync: getSyncStatus(), keepalive: getKeepaliveStatus(), })); } // Dashboard — HTML status page (served at / and /dashboard) if (pathname === "/" || pathname === "/dashboard") { const [backendUp, frontendUp] = await Promise.all([ probe(NGINX_HOST, BACKEND_PORT, "/health"), tcpProbe(NGINX_HOST, FRONTEND_PORT), ]); res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", "Cache-Control": "no-store" }); return res.end(renderDashboard({ backendUp, frontendUp, uptimeHuman: formatUptime(Date.now() - startTime), sync: getSyncStatus(), keepalive: getKeepaliveStatus(), })); } // Everything else → proxy to nginx const proxyHeaders = { ...req.headers, host: `${NGINX_HOST}:${NGINX_PORT}`, "x-forwarded-for": req.socket?.remoteAddress || "", "x-forwarded-host": req.headers.host || "", "x-forwarded-proto": "https", }; const proxyReq = http.request( { hostname: NGINX_HOST, port: NGINX_PORT, path: pathname + url.search, method: req.method, headers: proxyHeaders }, proxyRes => { res.writeHead(proxyRes.statusCode, proxyRes.headers); proxyRes.pipe(res); proxyRes.on("error", () => res.end()); }, ); proxyReq.on("error", err => { if (!res.headersSent) { res.writeHead(503, { "Content-Type": "application/json" }); res.end(JSON.stringify({ status: "starting", message: "DeerFlow is initializing…" })); } else { res.end(); } }); req.on("error", () => proxyReq.destroy()); res.on("error", () => proxyReq.destroy()); req.pipe(proxyReq); }); // WebSocket upgrade passthrough server.on("upgrade", (req, socket, head) => { const url = parseUrl(req.url || "/"); const upstream = net.connect(NGINX_PORT, NGINX_HOST, () => { upstream.write( `${req.method} ${url.pathname}${url.search} HTTP/${req.httpVersion}\r\n` + req.rawHeaders.reduce((acc, v, i) => acc + (i % 2 === 0 ? `${v}: ` : `${v}\r\n`), "") + "\r\n", ); if (head?.length) upstream.write(head); upstream.pipe(socket).pipe(upstream); }); upstream.on("error", () => socket.destroy()); socket.on("error", () => upstream.destroy()); }); server.timeout = 0; server.keepAliveTimeout = 65000; server.listen(PUBLIC_PORT, "0.0.0.0", () => console.log(`🦌 HuggingFlow health-server :${PUBLIC_PORT} → nginx :${NGINX_PORT}`) );