somratpro commited on
Commit
bf48a19
·
1 Parent(s): d1ca953

refactor: remove redundant startup CLI logging, update linting instructions, and simplify dashboard UI presentation

Browse files
Files changed (3) hide show
  1. CONTRIBUTING.md +2 -2
  2. health-server.js +111 -42
  3. start.sh +0 -26
CONTRIBUTING.md CHANGED
@@ -7,9 +7,9 @@ Thanks for improving HuggingMess.
7
  Run these before submitting changes:
8
 
9
  ```bash
10
- bash -n start.sh setup-uptimerobot.sh
11
  node --check health-server.js
12
- python3 -m py_compile hermes-sync.py cloudflare-proxy-setup.py
13
  ```
14
 
15
  If Docker is available:
 
7
  Run these before submitting changes:
8
 
9
  ```bash
10
+ bash -n start.sh
11
  node --check health-server.js
12
+ python3 -m py_compile hermes-sync.py cloudflare-proxy-setup.py cloudflare-keepalive-setup.py
13
  ```
14
 
15
  If Docker is available:
health-server.js CHANGED
@@ -17,7 +17,8 @@ const LOGIN_PATH = "/login";
17
  const SESSION_COOKIE = "huggingmess_session";
18
 
19
  const SYNC_STATUS_FILE = "/tmp/huggingmess-sync-status.json";
20
- const CLOUDFLARE_KEEPALIVE_STATUS_FILE = "/tmp/huggingmess-cloudflare-keepalive-status.json";
 
21
 
22
  function canConnect(port, host = GATEWAY_HOST, timeoutMs = 600) {
23
  return new Promise((resolve) => {
@@ -94,7 +95,10 @@ function isAuthorized(req) {
94
  if (!API_SERVER_KEY) return true;
95
  return (
96
  timingSafeEqualString(getBearerToken(req), API_SERVER_KEY) ||
97
- timingSafeEqualString(parseCookies(req)[SESSION_COOKIE], expectedSessionValue())
 
 
 
98
  );
99
  }
100
 
@@ -110,7 +114,9 @@ function loginUrl(nextPath) {
110
 
111
  function renderLoginPage(nextPath, errorMessage = "") {
112
  const safeNext = sanitizeNext(nextPath);
113
- const errorHtml = errorMessage ? `<div class="error">${escapeHtml(errorMessage)}</div>` : "";
 
 
114
  return `<!doctype html>
115
  <html lang="en">
116
  <head>
@@ -182,7 +188,9 @@ function wantsHtml(req) {
182
  }
183
 
184
  async function handleLogin(req, res, parsed) {
185
- const nextPath = sanitizeNext(parsed.searchParams.get("next") || `${APP_BASE}/`);
 
 
186
 
187
  if (!API_SERVER_KEY) {
188
  redirect(res, nextPath);
@@ -215,7 +223,12 @@ async function handleLogin(req, res, parsed) {
215
  "content-type": "text/html; charset=utf-8",
216
  "cache-control": "no-store",
217
  });
218
- res.end(renderLoginPage(submittedNext, "That token did not match GATEWAY_TOKEN."));
 
 
 
 
 
219
  return;
220
  }
221
 
@@ -234,7 +247,13 @@ async function handleLogin(req, res, parsed) {
234
  }
235
  }
236
 
237
- function proxyRequest(req, res, targetPort, rewritePath = (path) => path, headerOverrides = {}) {
 
 
 
 
 
 
238
  const parsed = new URL(req.url, "http://localhost");
239
  const targetPath = rewritePath(parsed.pathname) + parsed.search;
240
  const headers = {
@@ -286,10 +305,17 @@ async function statusPayload() {
286
  const gateway = await canConnect(GATEWAY_PORT);
287
  const dashboard = await canConnect(DASHBOARD_PORT);
288
  const telegramWebhook =
289
- !!process.env.TELEGRAM_WEBHOOK_URL && (await canConnect(TELEGRAM_WEBHOOK_PORT));
290
- const sync = readJson(SYNC_STATUS_FILE, process.env.HF_TOKEN
291
- ? { status: "configured", message: "Backup is enabled; waiting for the first sync." }
292
- : { status: "disabled", message: "HF_TOKEN is not configured." });
 
 
 
 
 
 
 
293
 
294
  return {
295
  ok: gateway,
@@ -311,8 +337,15 @@ async function statusPayload() {
311
  webhookListening: telegramWebhook,
312
  proxy: process.env.CLOUDFLARE_PROXY_URL || "",
313
  },
314
- model: process.env.MODEL_FOR_CONFIG || process.env.HERMES_MODEL || process.env.LLM_MODEL || "",
315
- provider: process.env.PROVIDER_FOR_CONFIG || process.env.HERMES_INFERENCE_PROVIDER || "auto",
 
 
 
 
 
 
 
316
  backup: sync,
317
  keepalive: readJson(CLOUDFLARE_KEEPALIVE_STATUS_FILE, null),
318
  };
@@ -327,10 +360,18 @@ function toneBadge(label, tone = "neutral") {
327
  }
328
 
329
  function valueOrUnset(value, fallback = "Not set") {
330
- return value ? escapeHtml(value) : `<span class="muted">${escapeHtml(fallback)}</span>`;
 
 
331
  }
332
 
333
- function renderTile({ title, value, detail = "", tone = "neutral", meta = "" }) {
 
 
 
 
 
 
334
  return `<article class="tile ${tone}">
335
  <div class="tile-head">
336
  <span class="tile-title">${escapeHtml(title)}</span>
@@ -344,65 +385,76 @@ function renderTile({ title, value, detail = "", tone = "neutral", meta = "" })
344
 
345
  function renderDashboard(data) {
346
  const syncStatus = String(data.backup?.status || "unknown");
347
- const syncTone = ["success", "restored", "synced", "configured"].includes(syncStatus) ? "ok" : syncStatus === "disabled" ? "warn" : "neutral";
348
- const telegramTone = data.telegram.configured ? (data.telegram.webhookListening || !data.telegram.webhook ? "ok" : "warn") : "warn";
 
 
 
 
 
 
 
 
 
 
349
  const keepaliveConfigured = data.keepalive?.configured === true;
350
- const keepaliveStatus = String(data.keepalive?.status || (process.env.CLOUDFLARE_WORKERS_TOKEN ? "pending" : "not configured"));
351
- const keepAliveTone = keepaliveConfigured ? "ok" : process.env.CLOUDFLARE_WORKERS_TOKEN ? "warn" : "neutral";
 
 
 
 
 
 
 
352
  const telegramDetail = data.telegram.configured
353
- ? `${data.telegram.webhook ? "Webhook mode" : "Polling mode"}${data.telegram.proxy ? ` through Cloudflare proxy` : ""}.`
354
- : "Add TELEGRAM_BOT_TOKEN to enable Telegram.";
355
- const backupDetail = data.backup?.message ? escapeHtml(data.backup.message) : "No backup status has been written yet.";
356
- const backupMeta = data.backup?.timestamp ? `Last update ${escapeHtml(data.backup.timestamp)}` : "";
357
  const keepAliveDetail = keepaliveConfigured
358
- ? `Worker <code>${escapeHtml(data.keepalive.workerName || "keepalive")}</code> pings <code>${escapeHtml(data.keepalive.targetUrl || "/health")}</code>.`
359
  : process.env.CLOUDFLARE_WORKERS_TOKEN
360
- ? "Cloudflare keep-awake Worker is pending or failed; check Space logs."
361
- : "Add CLOUDFLARE_WORKERS_TOKEN to create a scheduled keep-awake Worker.";
362
  const serviceOk = data.gateway && data.dashboard;
363
 
364
  const tiles = [
365
  renderTile({
366
  title: "Gateway",
367
  value: toneBadge(data.gateway ? "Online" : "Offline", data.gateway ? "ok" : "off"),
368
- detail: data.gateway ? `OpenAI-compatible API on port <code>${data.ports.gateway}</code>.` : `Gateway API is not reachable on port <code>${data.ports.gateway}</code>.`,
369
  tone: data.gateway ? "ok" : "off",
370
- meta: data.authConfigured ? `Protected by <code>GATEWAY_TOKEN</code>.` : `Set <code>GATEWAY_TOKEN</code> before sharing.`,
371
  }),
372
  renderTile({
373
  title: "Model",
374
  value: `<code>${valueOrUnset(data.model)}</code>`,
375
- detail: `Provider <code>${valueOrUnset(data.provider || "auto")}</code>.`,
376
  tone: data.model ? "ok" : "warn",
377
- meta: "Gemini example: google/gemini-2.5-flash",
378
  }),
379
  renderTile({
380
  title: "Runtime",
381
  value: escapeHtml(data.uptime),
382
- detail: `Public port <code>${data.ports.public}</code>; health at <code>/health</code>.`,
383
  tone: "neutral",
384
- meta: `Started <code>${escapeHtml(data.startedAt)}</code>.`,
385
  }),
386
  renderTile({
387
  title: "Telegram",
388
  value: toneBadge(data.telegram.configured ? "Configured" : "Disabled", telegramTone),
389
  detail: telegramDetail,
390
  tone: telegramTone,
391
- meta: data.telegram.webhookUrl ? `<code>${escapeHtml(data.telegram.webhookUrl)}</code>` : "",
392
  }),
393
  renderTile({
394
  title: "Backup",
395
  value: toneBadge(syncStatus.toUpperCase(), syncTone),
396
  detail: backupDetail,
397
  tone: syncTone,
398
- meta: backupMeta,
399
  }),
400
  renderTile({
401
  title: "Keep Awake",
402
- value: toneBadge(keepaliveConfigured ? "Cloudflare cron" : keepaliveStatus.toUpperCase(), keepAliveTone),
403
  detail: keepAliveDetail,
404
  tone: keepAliveTone,
405
- meta: keepaliveConfigured ? `Schedule <code>${escapeHtml(data.keepalive.cron || "*/10 * * * *")}</code>.` : "",
406
  }),
407
  ].join("");
408
 
@@ -457,7 +509,7 @@ function renderDashboard(data) {
457
  <main>
458
  <header>
459
  <h1>HuggingMess</h1>
460
- <div class="subtitle">Self-hosted - Hugging Face Spaces - Hermes Agent</div>
461
  </header>
462
  <a class="hero-action" href="${APP_BASE}/" target="_blank" rel="noopener noreferrer">Open Hermes Agent -></a>
463
  <section class="overview">
@@ -481,7 +533,13 @@ const server = http.createServer(async (req, res) => {
481
  if (path === "/health" || path === `${APP_BASE}/health`) {
482
  const data = await statusPayload();
483
  res.writeHead(data.ok ? 200 : 503, { "content-type": "application/json" });
484
- res.end(JSON.stringify({ ok: data.ok, gateway: data.gateway, uptime: data.uptime }));
 
 
 
 
 
 
485
  return;
486
  }
487
 
@@ -511,7 +569,12 @@ const server = http.createServer(async (req, res) => {
511
 
512
  if (path === APP_BASE || path.startsWith(`${APP_BASE}/`)) {
513
  if (!requireAuth(req, res)) return;
514
- proxyRequest(req, res, DASHBOARD_PORT, (p) => p.replace(/^\/app/, "") || "/");
 
 
 
 
 
515
  return;
516
  }
517
 
@@ -557,12 +620,18 @@ const server = http.createServer(async (req, res) => {
557
  "content-type": "application/json",
558
  "cache-control": "no-store",
559
  });
560
- res.end(JSON.stringify({ error: "unauthorized", message: "Use Authorization: Bearer <GATEWAY_TOKEN>." }));
 
 
 
 
 
561
  return;
562
  }
563
- const upstreamHeaders = getBearerToken(req) || !API_SERVER_KEY
564
- ? {}
565
- : { authorization: `Bearer ${API_SERVER_KEY}` };
 
566
  proxyRequest(req, res, GATEWAY_PORT, (p) => p, upstreamHeaders);
567
  return;
568
  }
 
17
  const SESSION_COOKIE = "huggingmess_session";
18
 
19
  const SYNC_STATUS_FILE = "/tmp/huggingmess-sync-status.json";
20
+ const CLOUDFLARE_KEEPALIVE_STATUS_FILE =
21
+ "/tmp/huggingmess-cloudflare-keepalive-status.json";
22
 
23
  function canConnect(port, host = GATEWAY_HOST, timeoutMs = 600) {
24
  return new Promise((resolve) => {
 
95
  if (!API_SERVER_KEY) return true;
96
  return (
97
  timingSafeEqualString(getBearerToken(req), API_SERVER_KEY) ||
98
+ timingSafeEqualString(
99
+ parseCookies(req)[SESSION_COOKIE],
100
+ expectedSessionValue(),
101
+ )
102
  );
103
  }
104
 
 
114
 
115
  function renderLoginPage(nextPath, errorMessage = "") {
116
  const safeNext = sanitizeNext(nextPath);
117
+ const errorHtml = errorMessage
118
+ ? `<div class="error">${escapeHtml(errorMessage)}</div>`
119
+ : "";
120
  return `<!doctype html>
121
  <html lang="en">
122
  <head>
 
188
  }
189
 
190
  async function handleLogin(req, res, parsed) {
191
+ const nextPath = sanitizeNext(
192
+ parsed.searchParams.get("next") || `${APP_BASE}/`,
193
+ );
194
 
195
  if (!API_SERVER_KEY) {
196
  redirect(res, nextPath);
 
223
  "content-type": "text/html; charset=utf-8",
224
  "cache-control": "no-store",
225
  });
226
+ res.end(
227
+ renderLoginPage(
228
+ submittedNext,
229
+ "That token did not match GATEWAY_TOKEN.",
230
+ ),
231
+ );
232
  return;
233
  }
234
 
 
247
  }
248
  }
249
 
250
+ function proxyRequest(
251
+ req,
252
+ res,
253
+ targetPort,
254
+ rewritePath = (path) => path,
255
+ headerOverrides = {},
256
+ ) {
257
  const parsed = new URL(req.url, "http://localhost");
258
  const targetPath = rewritePath(parsed.pathname) + parsed.search;
259
  const headers = {
 
305
  const gateway = await canConnect(GATEWAY_PORT);
306
  const dashboard = await canConnect(DASHBOARD_PORT);
307
  const telegramWebhook =
308
+ !!process.env.TELEGRAM_WEBHOOK_URL &&
309
+ (await canConnect(TELEGRAM_WEBHOOK_PORT));
310
+ const sync = readJson(
311
+ SYNC_STATUS_FILE,
312
+ process.env.HF_TOKEN
313
+ ? {
314
+ status: "configured",
315
+ message: "Backup is enabled; waiting for the first sync.",
316
+ }
317
+ : { status: "disabled", message: "HF_TOKEN is not configured." },
318
+ );
319
 
320
  return {
321
  ok: gateway,
 
337
  webhookListening: telegramWebhook,
338
  proxy: process.env.CLOUDFLARE_PROXY_URL || "",
339
  },
340
+ model:
341
+ process.env.MODEL_FOR_CONFIG ||
342
+ process.env.HERMES_MODEL ||
343
+ process.env.LLM_MODEL ||
344
+ "",
345
+ provider:
346
+ process.env.PROVIDER_FOR_CONFIG ||
347
+ process.env.HERMES_INFERENCE_PROVIDER ||
348
+ "auto",
349
  backup: sync,
350
  keepalive: readJson(CLOUDFLARE_KEEPALIVE_STATUS_FILE, null),
351
  };
 
360
  }
361
 
362
  function valueOrUnset(value, fallback = "Not set") {
363
+ return value
364
+ ? escapeHtml(value)
365
+ : `<span class="muted">${escapeHtml(fallback)}</span>`;
366
  }
367
 
368
+ function renderTile({
369
+ title,
370
+ value,
371
+ detail = "",
372
+ tone = "neutral",
373
+ meta = "",
374
+ }) {
375
  return `<article class="tile ${tone}">
376
  <div class="tile-head">
377
  <span class="tile-title">${escapeHtml(title)}</span>
 
385
 
386
  function renderDashboard(data) {
387
  const syncStatus = String(data.backup?.status || "unknown");
388
+ const syncTone = ["success", "restored", "synced", "configured"].includes(
389
+ syncStatus,
390
+ )
391
+ ? "ok"
392
+ : syncStatus === "disabled"
393
+ ? "warn"
394
+ : "neutral";
395
+ const telegramTone = data.telegram.configured
396
+ ? data.telegram.webhookListening || !data.telegram.webhook
397
+ ? "ok"
398
+ : "warn"
399
+ : "warn";
400
  const keepaliveConfigured = data.keepalive?.configured === true;
401
+ const keepaliveStatus = String(
402
+ data.keepalive?.status ||
403
+ (process.env.CLOUDFLARE_WORKERS_TOKEN ? "pending" : "not configured"),
404
+ );
405
+ const keepAliveTone = keepaliveConfigured
406
+ ? "ok"
407
+ : process.env.CLOUDFLARE_WORKERS_TOKEN
408
+ ? "warn"
409
+ : "neutral";
410
  const telegramDetail = data.telegram.configured
411
+ ? `${data.telegram.webhook ? "Webhook" : "Polling"}${data.telegram.proxy ? " via CF proxy" : ""}`
412
+ : "Not configured";
413
+ const backupDetail = data.backup?.message ? escapeHtml(data.backup.message) : "No status yet";
 
414
  const keepAliveDetail = keepaliveConfigured
415
+ ? `Pinging <code>${escapeHtml(data.keepalive.targetUrl || "/health")}</code>`
416
  : process.env.CLOUDFLARE_WORKERS_TOKEN
417
+ ? "Worker pending or failed"
418
+ : "Not configured";
419
  const serviceOk = data.gateway && data.dashboard;
420
 
421
  const tiles = [
422
  renderTile({
423
  title: "Gateway",
424
  value: toneBadge(data.gateway ? "Online" : "Offline", data.gateway ? "ok" : "off"),
425
+ detail: data.gateway ? `API on port ${data.ports.gateway}` : `Unreachable`,
426
  tone: data.gateway ? "ok" : "off",
427
+ meta: data.authConfigured ? "Protected" : "Unprotected",
428
  }),
429
  renderTile({
430
  title: "Model",
431
  value: `<code>${valueOrUnset(data.model)}</code>`,
432
+ detail: `Provider: ${valueOrUnset(data.provider || "auto")}`,
433
  tone: data.model ? "ok" : "warn",
 
434
  }),
435
  renderTile({
436
  title: "Runtime",
437
  value: escapeHtml(data.uptime),
438
+ detail: `Port ${data.ports.public}`,
439
  tone: "neutral",
 
440
  }),
441
  renderTile({
442
  title: "Telegram",
443
  value: toneBadge(data.telegram.configured ? "Configured" : "Disabled", telegramTone),
444
  detail: telegramDetail,
445
  tone: telegramTone,
 
446
  }),
447
  renderTile({
448
  title: "Backup",
449
  value: toneBadge(syncStatus.toUpperCase(), syncTone),
450
  detail: backupDetail,
451
  tone: syncTone,
 
452
  }),
453
  renderTile({
454
  title: "Keep Awake",
455
+ value: toneBadge(keepaliveConfigured ? "CF Cron" : keepaliveStatus.toUpperCase(), keepAliveTone),
456
  detail: keepAliveDetail,
457
  tone: keepAliveTone,
 
458
  }),
459
  ].join("");
460
 
 
509
  <main>
510
  <header>
511
  <h1>HuggingMess</h1>
512
+ <div class="subtitle">Self-hosted - Hermes Agent</div>
513
  </header>
514
  <a class="hero-action" href="${APP_BASE}/" target="_blank" rel="noopener noreferrer">Open Hermes Agent -></a>
515
  <section class="overview">
 
533
  if (path === "/health" || path === `${APP_BASE}/health`) {
534
  const data = await statusPayload();
535
  res.writeHead(data.ok ? 200 : 503, { "content-type": "application/json" });
536
+ res.end(
537
+ JSON.stringify({
538
+ ok: data.ok,
539
+ gateway: data.gateway,
540
+ uptime: data.uptime,
541
+ }),
542
+ );
543
  return;
544
  }
545
 
 
569
 
570
  if (path === APP_BASE || path.startsWith(`${APP_BASE}/`)) {
571
  if (!requireAuth(req, res)) return;
572
+ proxyRequest(
573
+ req,
574
+ res,
575
+ DASHBOARD_PORT,
576
+ (p) => p.replace(/^\/app/, "") || "/",
577
+ );
578
  return;
579
  }
580
 
 
620
  "content-type": "application/json",
621
  "cache-control": "no-store",
622
  });
623
+ res.end(
624
+ JSON.stringify({
625
+ error: "unauthorized",
626
+ message: "Use Authorization: Bearer <GATEWAY_TOKEN>.",
627
+ }),
628
+ );
629
  return;
630
  }
631
+ const upstreamHeaders =
632
+ getBearerToken(req) || !API_SERVER_KEY
633
+ ? {}
634
+ : { authorization: `Bearer ${API_SERVER_KEY}` };
635
  proxyRequest(req, res, GATEWAY_PORT, (p) => p, upstreamHeaders);
636
  return;
637
  }
start.sh CHANGED
@@ -243,32 +243,6 @@ path.write_text(yaml.safe_dump(config, sort_keys=False), encoding="utf-8")
243
  path.chmod(0o600)
244
  PY
245
 
246
- echo ""
247
- echo "Hermes model : ${MODEL_FOR_CONFIG:-using Hermes default/restored config}"
248
- echo "Provider : ${PROVIDER_FOR_CONFIG:-using Hermes default/restored config}"
249
- echo "Public port : ${PUBLIC_PORT}"
250
- if [ -n "${TELEGRAM_BOT_TOKEN:-}" ]; then
251
- echo "Telegram : enabled"
252
- if [ -n "${TELEGRAM_WEBHOOK_URL:-}" ]; then
253
- echo "Telegram mode: webhook (${TELEGRAM_WEBHOOK_URL})"
254
- else
255
- echo "Telegram mode: polling"
256
- fi
257
- else
258
- echo "Telegram : not configured"
259
- fi
260
- if [ -n "${HF_TOKEN:-}" ]; then
261
- echo "Backup : ${BACKUP_DATASET} every ${SYNC_INTERVAL}s"
262
- else
263
- echo "Backup : disabled"
264
- fi
265
- if [ -n "${CLOUDFLARE_PROXY_URL:-}" ]; then
266
- echo "Proxy : ${CLOUDFLARE_PROXY_URL}"
267
- fi
268
- if [ -n "${SPACE_HOST:-}" ]; then
269
- echo "Space URL : https://${SPACE_HOST}"
270
- fi
271
- echo ""
272
 
273
  graceful_shutdown() {
274
  echo "Shutting down HuggingMess..."
 
243
  path.chmod(0o600)
244
  PY
245
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
246
 
247
  graceful_shutdown() {
248
  echo "Shutting down HuggingMess..."