// 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(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 `