Spaces:
Running
Running
style: format code for better readability and consistency
Browse files- health-server.js +38 -18
health-server.js
CHANGED
|
@@ -15,7 +15,8 @@ const HF_BACKUP_ENABLED = !!process.env.HF_TOKEN;
|
|
| 15 |
const SYNC_INTERVAL = process.env.SYNC_INTERVAL || "180";
|
| 16 |
const APP_BASE = "/app";
|
| 17 |
const SYNC_STATUS_FILE = "/tmp/sync-status.json";
|
| 18 |
-
const CLOUDFLARE_KEEPALIVE_STATUS_FILE =
|
|
|
|
| 19 |
|
| 20 |
function parseRequestUrl(url) {
|
| 21 |
try {
|
|
@@ -60,7 +61,9 @@ function readGuardianStatus() {
|
|
| 60 |
function getKeepaliveStatus() {
|
| 61 |
try {
|
| 62 |
if (fs.existsSync(CLOUDFLARE_KEEPALIVE_STATUS_FILE)) {
|
| 63 |
-
return JSON.parse(
|
|
|
|
|
|
|
| 64 |
}
|
| 65 |
} catch {}
|
| 66 |
return null;
|
|
@@ -110,7 +113,13 @@ function toneBadge(label, tone = "neutral") {
|
|
| 110 |
return `<span class="badge ${tone}">${escapeHtml(label)}</span>`;
|
| 111 |
}
|
| 112 |
|
| 113 |
-
function renderTile({
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
return `<article class="tile ${tone}">
|
| 115 |
<div class="tile-head">
|
| 116 |
<span class="tile-title">${escapeHtml(title)}</span>
|
|
@@ -124,12 +133,16 @@ function renderTile({ title, value, detail = "", tone = "neutral", meta = "" })
|
|
| 124 |
|
| 125 |
function renderDashboard(data) {
|
| 126 |
const syncStatus = String(data.sync?.status || "unknown");
|
| 127 |
-
const syncTone = ["success", "restored", "synced", "configured"].includes(
|
|
|
|
|
|
|
| 128 |
? "ok"
|
| 129 |
: syncStatus === "disabled"
|
| 130 |
? "warn"
|
| 131 |
: "neutral";
|
| 132 |
-
const backupDetail = data.sync?.message
|
|
|
|
|
|
|
| 133 |
|
| 134 |
const keepaliveConfigured = data.keepalive?.configured === true;
|
| 135 |
const keepaliveStatus = String(
|
|
@@ -150,7 +163,10 @@ function renderDashboard(data) {
|
|
| 150 |
const tiles = [
|
| 151 |
renderTile({
|
| 152 |
title: "Gateway",
|
| 153 |
-
value: toneBadge(
|
|
|
|
|
|
|
|
|
|
| 154 |
detail: `Internal Port ${GATEWAY_PORT}`,
|
| 155 |
tone: data.gatewayReady ? "ok" : "off",
|
| 156 |
}),
|
|
@@ -166,15 +182,12 @@ function renderDashboard(data) {
|
|
| 166 |
detail: `Public Port ${PORT}`,
|
| 167 |
tone: "neutral",
|
| 168 |
}),
|
| 169 |
-
renderTile({
|
| 170 |
-
title: "WhatsApp",
|
| 171 |
-
value: toneBadge(data.whatsapp.connected ? "Connected" : (data.whatsapp.configured ? "Ready" : "Disabled"), data.whatsapp.connected ? "ok" : (data.whatsapp.configured ? "warn" : "neutral")),
|
| 172 |
-
detail: data.whatsapp.connected ? "Active Session" : (data.whatsapp.pairing ? "Pairing Requested" : "Channel configured"),
|
| 173 |
-
tone: data.whatsapp.connected ? "ok" : (data.whatsapp.configured ? "warn" : "neutral"),
|
| 174 |
-
}),
|
| 175 |
renderTile({
|
| 176 |
title: "Telegram",
|
| 177 |
-
value: toneBadge(
|
|
|
|
|
|
|
|
|
|
| 178 |
detail: TELEGRAM_ENABLED ? "Bot Channel active" : "Not configured",
|
| 179 |
tone: TELEGRAM_ENABLED ? "ok" : "neutral",
|
| 180 |
}),
|
|
@@ -186,7 +199,10 @@ function renderDashboard(data) {
|
|
| 186 |
}),
|
| 187 |
renderTile({
|
| 188 |
title: "Keep Awake",
|
| 189 |
-
value: toneBadge(
|
|
|
|
|
|
|
|
|
|
| 190 |
detail: keepAliveDetail,
|
| 191 |
tone: keepAliveTone,
|
| 192 |
}),
|
|
@@ -199,14 +215,14 @@ function renderDashboard(data) {
|
|
| 199 |
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
| 200 |
<title>HuggingClaw</title>
|
| 201 |
<style>
|
| 202 |
-
:root { color-scheme: dark; --bg:#08080f; --panel:#12111b; --
|
| 203 |
* { box-sizing:border-box; }
|
| 204 |
body { margin:0; min-height:100vh; font-family:Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background:var(--bg); color:var(--text); font-size:13px; }
|
| 205 |
main { width:min(720px, calc(100% - 32px)); margin:0 auto; padding:36px 0 44px; }
|
| 206 |
header { text-align:center; margin-bottom:22px; }
|
| 207 |
h1 { margin:0; font-size:1.65rem; line-height:1; letter-spacing:0; }
|
| 208 |
.subtitle { margin-top:12px; color:var(--muted); font-size:.72rem; text-transform:uppercase; letter-spacing:.14em; font-weight:800; }
|
| 209 |
-
.hero-action { display:flex; width:100%; min-height:46px; align-items:center; justify-content:center; border-radius:8px; background:
|
| 210 |
.hero-action:hover { opacity: 0.9; }
|
| 211 |
.overview { display:grid; grid-template-columns:repeat(2, minmax(0, 1fr)); gap:10px; margin-bottom:10px; }
|
| 212 |
.tile { border:1px solid var(--line); background:var(--panel); border-radius:11px; padding:18px; min-height:124px; display:flex; flex-direction:column; gap:10px; position:relative; }
|
|
@@ -257,7 +273,9 @@ const server = http.createServer(async (req, res) => {
|
|
| 257 |
// 1. Dashboard Routes
|
| 258 |
if (pathname === "/health") {
|
| 259 |
const gatewayReady = await probeGatewayHealth();
|
| 260 |
-
res.writeHead(gatewayReady ? 200 : 503, {
|
|
|
|
|
|
|
| 261 |
return res.end(
|
| 262 |
JSON.stringify({
|
| 263 |
status: gatewayReady ? "ok" : "degraded",
|
|
@@ -373,5 +391,7 @@ server.on("upgrade", (req, socket, head) => {
|
|
| 373 |
server.timeout = 0;
|
| 374 |
server.keepAliveTimeout = 65000;
|
| 375 |
server.listen(PORT, "0.0.0.0", () =>
|
| 376 |
-
console.log(
|
|
|
|
|
|
|
| 377 |
);
|
|
|
|
| 15 |
const SYNC_INTERVAL = process.env.SYNC_INTERVAL || "180";
|
| 16 |
const APP_BASE = "/app";
|
| 17 |
const SYNC_STATUS_FILE = "/tmp/sync-status.json";
|
| 18 |
+
const CLOUDFLARE_KEEPALIVE_STATUS_FILE =
|
| 19 |
+
"/tmp/huggingclaw-cloudflare-keepalive-status.json";
|
| 20 |
|
| 21 |
function parseRequestUrl(url) {
|
| 22 |
try {
|
|
|
|
| 61 |
function getKeepaliveStatus() {
|
| 62 |
try {
|
| 63 |
if (fs.existsSync(CLOUDFLARE_KEEPALIVE_STATUS_FILE)) {
|
| 64 |
+
return JSON.parse(
|
| 65 |
+
fs.readFileSync(CLOUDFLARE_KEEPALIVE_STATUS_FILE, "utf8"),
|
| 66 |
+
);
|
| 67 |
}
|
| 68 |
} catch {}
|
| 69 |
return null;
|
|
|
|
| 113 |
return `<span class="badge ${tone}">${escapeHtml(label)}</span>`;
|
| 114 |
}
|
| 115 |
|
| 116 |
+
function renderTile({
|
| 117 |
+
title,
|
| 118 |
+
value,
|
| 119 |
+
detail = "",
|
| 120 |
+
tone = "neutral",
|
| 121 |
+
meta = "",
|
| 122 |
+
}) {
|
| 123 |
return `<article class="tile ${tone}">
|
| 124 |
<div class="tile-head">
|
| 125 |
<span class="tile-title">${escapeHtml(title)}</span>
|
|
|
|
| 133 |
|
| 134 |
function renderDashboard(data) {
|
| 135 |
const syncStatus = String(data.sync?.status || "unknown");
|
| 136 |
+
const syncTone = ["success", "restored", "synced", "configured"].includes(
|
| 137 |
+
syncStatus,
|
| 138 |
+
)
|
| 139 |
? "ok"
|
| 140 |
: syncStatus === "disabled"
|
| 141 |
? "warn"
|
| 142 |
: "neutral";
|
| 143 |
+
const backupDetail = data.sync?.message
|
| 144 |
+
? escapeHtml(data.sync.message)
|
| 145 |
+
: "No status yet";
|
| 146 |
|
| 147 |
const keepaliveConfigured = data.keepalive?.configured === true;
|
| 148 |
const keepaliveStatus = String(
|
|
|
|
| 163 |
const tiles = [
|
| 164 |
renderTile({
|
| 165 |
title: "Gateway",
|
| 166 |
+
value: toneBadge(
|
| 167 |
+
data.gatewayReady ? "Online" : "Offline",
|
| 168 |
+
data.gatewayReady ? "ok" : "off",
|
| 169 |
+
),
|
| 170 |
detail: `Internal Port ${GATEWAY_PORT}`,
|
| 171 |
tone: data.gatewayReady ? "ok" : "off",
|
| 172 |
}),
|
|
|
|
| 182 |
detail: `Public Port ${PORT}`,
|
| 183 |
tone: "neutral",
|
| 184 |
}),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 185 |
renderTile({
|
| 186 |
title: "Telegram",
|
| 187 |
+
value: toneBadge(
|
| 188 |
+
TELEGRAM_ENABLED ? "Enabled" : "Disabled",
|
| 189 |
+
TELEGRAM_ENABLED ? "ok" : "neutral",
|
| 190 |
+
),
|
| 191 |
detail: TELEGRAM_ENABLED ? "Bot Channel active" : "Not configured",
|
| 192 |
tone: TELEGRAM_ENABLED ? "ok" : "neutral",
|
| 193 |
}),
|
|
|
|
| 199 |
}),
|
| 200 |
renderTile({
|
| 201 |
title: "Keep Awake",
|
| 202 |
+
value: toneBadge(
|
| 203 |
+
keepaliveConfigured ? "CF Cron" : keepaliveStatus.toUpperCase(),
|
| 204 |
+
keepAliveTone,
|
| 205 |
+
),
|
| 206 |
detail: keepAliveDetail,
|
| 207 |
tone: keepAliveTone,
|
| 208 |
}),
|
|
|
|
| 215 |
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
| 216 |
<title>HuggingClaw</title>
|
| 217 |
<style>
|
| 218 |
+
:root { color-scheme: dark; --bg:#08080f; --panel:#12111b; --line:#26243a; --text:#f6f4ff; --muted:#7f7a9e; --soft:#b8b3d7; --good:#22c55e; --warn:#f5c542; --bad:#fb7185; }
|
| 219 |
* { box-sizing:border-box; }
|
| 220 |
body { margin:0; min-height:100vh; font-family:Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background:var(--bg); color:var(--text); font-size:13px; }
|
| 221 |
main { width:min(720px, calc(100% - 32px)); margin:0 auto; padding:36px 0 44px; }
|
| 222 |
header { text-align:center; margin-bottom:22px; }
|
| 223 |
h1 { margin:0; font-size:1.65rem; line-height:1; letter-spacing:0; }
|
| 224 |
.subtitle { margin-top:12px; color:var(--muted); font-size:.72rem; text-transform:uppercase; letter-spacing:.14em; font-weight:800; }
|
| 225 |
+
.hero-action { display:flex; width:100%; min-height:46px; align-items:center; justify-content:center; border-radius:8px; background:#fff; color:#000; text-decoration:none; font-weight:850; font-size:.98rem; margin:24px 0 20px; transition: opacity 0.15s ease; }
|
| 226 |
.hero-action:hover { opacity: 0.9; }
|
| 227 |
.overview { display:grid; grid-template-columns:repeat(2, minmax(0, 1fr)); gap:10px; margin-bottom:10px; }
|
| 228 |
.tile { border:1px solid var(--line); background:var(--panel); border-radius:11px; padding:18px; min-height:124px; display:flex; flex-direction:column; gap:10px; position:relative; }
|
|
|
|
| 273 |
// 1. Dashboard Routes
|
| 274 |
if (pathname === "/health") {
|
| 275 |
const gatewayReady = await probeGatewayHealth();
|
| 276 |
+
res.writeHead(gatewayReady ? 200 : 503, {
|
| 277 |
+
"Content-Type": "application/json",
|
| 278 |
+
});
|
| 279 |
return res.end(
|
| 280 |
JSON.stringify({
|
| 281 |
status: gatewayReady ? "ok" : "degraded",
|
|
|
|
| 391 |
server.timeout = 0;
|
| 392 |
server.keepAliveTimeout = 65000;
|
| 393 |
server.listen(PORT, "0.0.0.0", () =>
|
| 394 |
+
console.log(
|
| 395 |
+
`🦞 HuggingClaw Dashboard on ${PORT} -> Gateway on ${GATEWAY_PORT}`,
|
| 396 |
+
),
|
| 397 |
);
|