Spaces:
Running
Running
feat: enhance dashboard UI with dynamic status badges and server-side initial data rendering
Browse files- health-server.js +56 -12
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";
|
|
@@ -112,7 +113,33 @@ 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 +491,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 |
|
|
@@ -532,9 +559,14 @@ function renderDashboard() {
|
|
| 532 |
</div>
|
| 533 |
|
| 534 |
<script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 535 |
async function updateStats() {
|
| 536 |
try {
|
| 537 |
-
const res = await fetch('
|
| 538 |
const data = await res.json();
|
| 539 |
|
| 540 |
document.getElementById('model-id').textContent = data.model;
|
|
@@ -624,7 +656,7 @@ function renderDashboard() {
|
|
| 624 |
result.textContent = '';
|
| 625 |
|
| 626 |
try {
|
| 627 |
-
const res = await fetch('
|
| 628 |
method: 'POST',
|
| 629 |
headers: { 'Content-Type': 'application/json' },
|
| 630 |
body: JSON.stringify({ apiKey })
|
|
@@ -944,8 +976,20 @@ const server = http.createServer((req, res) => {
|
|
| 944 |
}
|
| 945 |
|
| 946 |
if (isDashboardRoute(pathname)) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 947 |
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
| 948 |
-
res.end(renderDashboard());
|
| 949 |
return;
|
| 950 |
}
|
| 951 |
|
|
|
|
| 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";
|
|
|
|
| 113 |
return { configured: true, connected: false, pairing: false };
|
| 114 |
}
|
| 115 |
|
| 116 |
+
function renderChannelBadge(channel, configuredLabel) {
|
| 117 |
+
if (channel && channel.connected) {
|
| 118 |
+
return '<div class="status-badge status-online"><div class="pulse"></div>Active</div>';
|
| 119 |
+
}
|
| 120 |
+
if (channel && channel.configured) {
|
| 121 |
+
return `<div class="status-badge status-syncing">${configuredLabel}</div>`;
|
| 122 |
+
}
|
| 123 |
+
return '<div class="status-badge status-offline">Disabled</div>';
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
function renderSyncBadge(syncData) {
|
| 127 |
+
let badgeClass = "status-offline";
|
| 128 |
+
let pulseHtml = "";
|
| 129 |
+
|
| 130 |
+
if (syncData.status === "success") {
|
| 131 |
+
badgeClass = "status-online";
|
| 132 |
+
pulseHtml = '<div class="pulse"></div>';
|
| 133 |
+
} else if (syncData.status === "syncing") {
|
| 134 |
+
badgeClass = "status-syncing";
|
| 135 |
+
pulseHtml = '<div class="pulse" style="background:#3b82f6"></div>';
|
| 136 |
+
}
|
| 137 |
+
|
| 138 |
+
return `<div class="status-badge ${badgeClass}">${pulseHtml}${String(syncData.status || "unknown").toUpperCase()}</div>`;
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
function renderDashboard(initialData) {
|
| 142 |
+
const controlUiHref = SPACE_HOST ? `https://${SPACE_HOST}` : `${DASHBOARD_APP_BASE}/`;
|
| 143 |
return `
|
| 144 |
<!DOCTYPE html>
|
| 145 |
<html lang="en">
|
|
|
|
| 491 |
<div class="stats-grid">
|
| 492 |
<div class="stat-card">
|
| 493 |
<span class="stat-label">Model</span>
|
| 494 |
+
<span class="stat-value" id="model-id">${initialData.model}</span>
|
| 495 |
</div>
|
| 496 |
<div class="stat-card">
|
| 497 |
<span class="stat-label">Uptime</span>
|
| 498 |
+
<span class="stat-value" id="uptime">${initialData.uptime}</span>
|
| 499 |
</div>
|
| 500 |
<div class="stat-card">
|
| 501 |
<span class="stat-label">WhatsApp</span>
|
| 502 |
+
<span id="wa-status">${renderChannelBadge(initialData.whatsapp, 'Ready to pair')}</span>
|
| 503 |
</div>
|
| 504 |
<div class="stat-card">
|
| 505 |
<span class="stat-label">Telegram</span>
|
| 506 |
+
<span id="tg-status">${renderChannelBadge(initialData.telegram, 'Configured')}</span>
|
| 507 |
</div>
|
| 508 |
+
<a href="${controlUiHref}" id="control-ui-link" class="stat-btn">Open Control UI</a>
|
| 509 |
</div>
|
| 510 |
|
| 511 |
<div class="stat-card" style="width: 100%;">
|
| 512 |
<span class="stat-label">Workspace Sync Status</span>
|
| 513 |
+
<div id="sync-badge-container">${renderSyncBadge(initialData.sync)}</div>
|
| 514 |
<div class="sync-info">
|
| 515 |
+
Last Sync Activity: <span id="sync-time">${initialData.sync.timestamp || "Never"}</span>
|
| 516 |
+
<span id="sync-msg">${initialData.sync.message || "Waiting for first sync..."}</span>
|
| 517 |
</div>
|
| 518 |
</div>
|
| 519 |
|
|
|
|
| 559 |
</div>
|
| 560 |
|
| 561 |
<script>
|
| 562 |
+
function getDashboardBase() {
|
| 563 |
+
const pathname = window.location.pathname || '${DASHBOARD_BASE}';
|
| 564 |
+
return pathname.endsWith('/') ? pathname.slice(0, -1) : pathname;
|
| 565 |
+
}
|
| 566 |
+
|
| 567 |
async function updateStats() {
|
| 568 |
try {
|
| 569 |
+
const res = await fetch(getDashboardBase() + '/status');
|
| 570 |
const data = await res.json();
|
| 571 |
|
| 572 |
document.getElementById('model-id').textContent = data.model;
|
|
|
|
| 656 |
result.textContent = '';
|
| 657 |
|
| 658 |
try {
|
| 659 |
+
const res = await fetch(getDashboardBase() + '/uptimerobot/setup', {
|
| 660 |
method: 'POST',
|
| 661 |
headers: { 'Content-Type': 'application/json' },
|
| 662 |
body: JSON.stringify({ apiKey })
|
|
|
|
| 976 |
}
|
| 977 |
|
| 978 |
if (isDashboardRoute(pathname)) {
|
| 979 |
+
const guardianStatus = readGuardianStatus();
|
| 980 |
+
const initialData = {
|
| 981 |
+
model: LLM_MODEL,
|
| 982 |
+
whatsapp: {
|
| 983 |
+
configured: guardianStatus.configured,
|
| 984 |
+
connected: guardianStatus.connected,
|
| 985 |
+
pairing: guardianStatus.pairing,
|
| 986 |
+
},
|
| 987 |
+
telegram: normalizeChannelStatus(null, TELEGRAM_ENABLED),
|
| 988 |
+
sync: readSyncStatus(),
|
| 989 |
+
uptime: uptimeHuman,
|
| 990 |
+
};
|
| 991 |
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
| 992 |
+
res.end(renderDashboard(initialData));
|
| 993 |
return;
|
| 994 |
}
|
| 995 |
|