Spaces:
Running
Running
feat: implement server-side dashboard rendering with dynamic status badges and configurable control UI link
Browse files- health-server.js +59 -13
health-server.js
CHANGED
|
@@ -9,6 +9,7 @@ const GATEWAY_PORT = 7860;
|
|
| 9 |
const GATEWAY_HOST = "127.0.0.1";
|
| 10 |
const startTime = Date.now();
|
| 11 |
const LLM_MODEL = process.env.LLM_MODEL || "Not Set";
|
|
|
|
| 12 |
const TELEGRAM_ENABLED = !!process.env.TELEGRAM_BOT_TOKEN;
|
| 13 |
const WHATSAPP_ENABLED = /^true$/i.test(process.env.WHATSAPP_ENABLED || "");
|
| 14 |
const WHATSAPP_STATUS_FILE = "/tmp/huggingclaw-wa-status.json";
|
|
@@ -35,7 +36,10 @@ function isDashboardScopedPath(pathname, suffix) {
|
|
| 35 |
}
|
| 36 |
|
| 37 |
function isDashboardAppRoute(pathname) {
|
| 38 |
-
return
|
|
|
|
|
|
|
|
|
|
| 39 |
}
|
| 40 |
|
| 41 |
function isLocalRoute(pathname) {
|
|
@@ -112,7 +116,35 @@ function readGuardianStatus() {
|
|
| 112 |
return { configured: true, connected: false, pairing: false };
|
| 113 |
}
|
| 114 |
|
| 115 |
-
function
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
return `
|
| 117 |
<!DOCTYPE html>
|
| 118 |
<html lang="en">
|
|
@@ -464,29 +496,29 @@ function renderDashboard() {
|
|
| 464 |
<div class="stats-grid">
|
| 465 |
<div class="stat-card">
|
| 466 |
<span class="stat-label">Model</span>
|
| 467 |
-
<span class="stat-value" id="model-id">
|
| 468 |
</div>
|
| 469 |
<div class="stat-card">
|
| 470 |
<span class="stat-label">Uptime</span>
|
| 471 |
-
<span class="stat-value" id="uptime">
|
| 472 |
</div>
|
| 473 |
<div class="stat-card">
|
| 474 |
<span class="stat-label">WhatsApp</span>
|
| 475 |
-
<span id="wa-status">
|
| 476 |
</div>
|
| 477 |
<div class="stat-card">
|
| 478 |
<span class="stat-label">Telegram</span>
|
| 479 |
-
<span id="tg-status">
|
| 480 |
</div>
|
| 481 |
-
<a href="
|
| 482 |
</div>
|
| 483 |
|
| 484 |
<div class="stat-card" style="width: 100%;">
|
| 485 |
<span class="stat-label">Workspace Sync Status</span>
|
| 486 |
-
<div id="sync-badge-container"></div>
|
| 487 |
<div class="sync-info">
|
| 488 |
-
Last Sync Activity: <span id="sync-time">Never</span>
|
| 489 |
-
<span id="sync-msg">
|
| 490 |
</div>
|
| 491 |
</div>
|
| 492 |
|
|
@@ -660,7 +692,6 @@ function renderDashboard() {
|
|
| 660 |
updateStats();
|
| 661 |
setInterval(updateStats, 10000);
|
| 662 |
restoreMonitorUiState();
|
| 663 |
-
document.getElementById('control-ui-link').setAttribute('href', getDashboardBase() + '/app/');
|
| 664 |
document.getElementById('uptimerobot-btn').addEventListener('click', setupUptimeRobot);
|
| 665 |
document.getElementById('uptimerobot-toggle').addEventListener('click', toggleMonitorSetup);
|
| 666 |
</script>
|
|
@@ -908,7 +939,10 @@ const server = http.createServer((req, res) => {
|
|
| 908 |
return;
|
| 909 |
}
|
| 910 |
|
| 911 |
-
if (
|
|
|
|
|
|
|
|
|
|
| 912 |
if (req.method !== "POST") {
|
| 913 |
res.writeHead(405, { "Content-Type": "application/json" });
|
| 914 |
res.end(JSON.stringify({ message: "Method not allowed" }));
|
|
@@ -950,8 +984,20 @@ const server = http.createServer((req, res) => {
|
|
| 950 |
}
|
| 951 |
|
| 952 |
if (isDashboardRoute(pathname)) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 953 |
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
| 954 |
-
res.end(renderDashboard());
|
| 955 |
return;
|
| 956 |
}
|
| 957 |
|
|
|
|
| 9 |
const GATEWAY_HOST = "127.0.0.1";
|
| 10 |
const startTime = Date.now();
|
| 11 |
const LLM_MODEL = process.env.LLM_MODEL || "Not Set";
|
| 12 |
+
const SPACE_HOST = process.env.SPACE_HOST || "";
|
| 13 |
const TELEGRAM_ENABLED = !!process.env.TELEGRAM_BOT_TOKEN;
|
| 14 |
const WHATSAPP_ENABLED = /^true$/i.test(process.env.WHATSAPP_ENABLED || "");
|
| 15 |
const WHATSAPP_STATUS_FILE = "/tmp/huggingclaw-wa-status.json";
|
|
|
|
| 36 |
}
|
| 37 |
|
| 38 |
function isDashboardAppRoute(pathname) {
|
| 39 |
+
return (
|
| 40 |
+
pathname === DASHBOARD_APP_BASE ||
|
| 41 |
+
pathname.startsWith(`${DASHBOARD_APP_BASE}/`)
|
| 42 |
+
);
|
| 43 |
}
|
| 44 |
|
| 45 |
function isLocalRoute(pathname) {
|
|
|
|
| 116 |
return { configured: true, connected: false, pairing: false };
|
| 117 |
}
|
| 118 |
|
| 119 |
+
function renderChannelBadge(channel, configuredLabel) {
|
| 120 |
+
if (channel && channel.connected) {
|
| 121 |
+
return '<div class="status-badge status-online"><div class="pulse"></div>Active</div>';
|
| 122 |
+
}
|
| 123 |
+
if (channel && channel.configured) {
|
| 124 |
+
return `<div class="status-badge status-syncing">${configuredLabel}</div>`;
|
| 125 |
+
}
|
| 126 |
+
return '<div class="status-badge status-offline">Disabled</div>';
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
function renderSyncBadge(syncData) {
|
| 130 |
+
let badgeClass = "status-offline";
|
| 131 |
+
let pulseHtml = "";
|
| 132 |
+
|
| 133 |
+
if (syncData.status === "success") {
|
| 134 |
+
badgeClass = "status-online";
|
| 135 |
+
pulseHtml = '<div class="pulse"></div>';
|
| 136 |
+
} else if (syncData.status === "syncing") {
|
| 137 |
+
badgeClass = "status-syncing";
|
| 138 |
+
pulseHtml = '<div class="pulse" style="background:#3b82f6"></div>';
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
return `<div class="status-badge ${badgeClass}">${pulseHtml}${String(syncData.status || "unknown").toUpperCase()}</div>`;
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
function renderDashboard(initialData) {
|
| 145 |
+
const controlUiHref = SPACE_HOST
|
| 146 |
+
? `https://${SPACE_HOST}`
|
| 147 |
+
: `${DASHBOARD_APP_BASE}/`;
|
| 148 |
return `
|
| 149 |
<!DOCTYPE html>
|
| 150 |
<html lang="en">
|
|
|
|
| 496 |
<div class="stats-grid">
|
| 497 |
<div class="stat-card">
|
| 498 |
<span class="stat-label">Model</span>
|
| 499 |
+
<span class="stat-value" id="model-id">${initialData.model}</span>
|
| 500 |
</div>
|
| 501 |
<div class="stat-card">
|
| 502 |
<span class="stat-label">Uptime</span>
|
| 503 |
+
<span class="stat-value" id="uptime">${initialData.uptime}</span>
|
| 504 |
</div>
|
| 505 |
<div class="stat-card">
|
| 506 |
<span class="stat-label">WhatsApp</span>
|
| 507 |
+
<span id="wa-status">${renderChannelBadge(initialData.whatsapp, "Ready to pair")}</span>
|
| 508 |
</div>
|
| 509 |
<div class="stat-card">
|
| 510 |
<span class="stat-label">Telegram</span>
|
| 511 |
+
<span id="tg-status">${renderChannelBadge(initialData.telegram, "Configured")}</span>
|
| 512 |
</div>
|
| 513 |
+
<a href="${controlUiHref}" id="control-ui-link" class="stat-btn">Open Control UI</a>
|
| 514 |
</div>
|
| 515 |
|
| 516 |
<div class="stat-card" style="width: 100%;">
|
| 517 |
<span class="stat-label">Workspace Sync Status</span>
|
| 518 |
+
<div id="sync-badge-container">${renderSyncBadge(initialData.sync)}</div>
|
| 519 |
<div class="sync-info">
|
| 520 |
+
Last Sync Activity: <span id="sync-time">${initialData.sync.timestamp || "Never"}</span>
|
| 521 |
+
<span id="sync-msg">${initialData.sync.message || "Waiting for first sync..."}</span>
|
| 522 |
</div>
|
| 523 |
</div>
|
| 524 |
|
|
|
|
| 692 |
updateStats();
|
| 693 |
setInterval(updateStats, 10000);
|
| 694 |
restoreMonitorUiState();
|
|
|
|
| 695 |
document.getElementById('uptimerobot-btn').addEventListener('click', setupUptimeRobot);
|
| 696 |
document.getElementById('uptimerobot-toggle').addEventListener('click', toggleMonitorSetup);
|
| 697 |
</script>
|
|
|
|
| 939 |
return;
|
| 940 |
}
|
| 941 |
|
| 942 |
+
if (
|
| 943 |
+
pathname === "/uptimerobot/setup" ||
|
| 944 |
+
pathname === DASHBOARD_UPTIMEROBOT_PATH
|
| 945 |
+
) {
|
| 946 |
if (req.method !== "POST") {
|
| 947 |
res.writeHead(405, { "Content-Type": "application/json" });
|
| 948 |
res.end(JSON.stringify({ message: "Method not allowed" }));
|
|
|
|
| 984 |
}
|
| 985 |
|
| 986 |
if (isDashboardRoute(pathname)) {
|
| 987 |
+
const guardianStatus = readGuardianStatus();
|
| 988 |
+
const initialData = {
|
| 989 |
+
model: LLM_MODEL,
|
| 990 |
+
whatsapp: {
|
| 991 |
+
configured: guardianStatus.configured,
|
| 992 |
+
connected: guardianStatus.connected,
|
| 993 |
+
pairing: guardianStatus.pairing,
|
| 994 |
+
},
|
| 995 |
+
telegram: normalizeChannelStatus(null, TELEGRAM_ENABLED),
|
| 996 |
+
sync: readSyncStatus(),
|
| 997 |
+
uptime: uptimeHuman,
|
| 998 |
+
};
|
| 999 |
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
| 1000 |
+
res.end(renderDashboard(initialData));
|
| 1001 |
return;
|
| 1002 |
}
|
| 1003 |
|