Spaces:
Running
Running
Upload health-server.js with huggingface_hub
#3
by ruang101 - opened
- health-server.js +330 -150
health-server.js
CHANGED
|
@@ -446,115 +446,48 @@ function valueOrUnset(value, fallback = "Not set") {
|
|
| 446 |
}
|
| 447 |
|
| 448 |
function renderTile({
|
| 449 |
-
title,
|
| 450 |
-
value,
|
| 451 |
-
detail = "",
|
| 452 |
-
tone = "neutral",
|
| 453 |
-
meta = "",
|
| 454 |
}) {
|
| 455 |
-
|
| 456 |
-
|
| 457 |
-
|
| 458 |
-
|
|
|
|
|
|
|
| 459 |
</div>
|
| 460 |
-
<div class="
|
| 461 |
-
${detail ? `<div class="
|
| 462 |
-
${meta ? `<div class="
|
| 463 |
</article>`;
|
| 464 |
}
|
| 465 |
|
| 466 |
function renderDashboard(data) {
|
| 467 |
const syncStatus = String(data.backup?.status || "unknown");
|
| 468 |
-
const syncTone = ["success", "restored", "synced", "configured"].includes(
|
| 469 |
-
syncStatus
|
| 470 |
-
)
|
| 471 |
-
? "ok"
|
| 472 |
-
: syncStatus === "disabled"
|
| 473 |
-
? "warn"
|
| 474 |
-
: "neutral";
|
| 475 |
const telegramTone = data.telegram.configured
|
| 476 |
-
? data.telegram.webhookListening || !data.telegram.webhook
|
| 477 |
-
? "ok"
|
| 478 |
-
: "warn"
|
| 479 |
-
: "warn";
|
| 480 |
const keepaliveConfigured = data.keepalive?.configured === true;
|
| 481 |
-
const keepaliveStatus = String(
|
| 482 |
-
|
| 483 |
-
(process.env.CLOUDFLARE_WORKERS_TOKEN ? "pending" : "not configured"),
|
| 484 |
-
);
|
| 485 |
-
const keepAliveTone = keepaliveConfigured
|
| 486 |
-
? "ok"
|
| 487 |
-
: process.env.CLOUDFLARE_WORKERS_TOKEN
|
| 488 |
-
? "warn"
|
| 489 |
-
: "neutral";
|
| 490 |
const telegramDetail = data.telegram.configured
|
| 491 |
-
? `${data.telegram.webhook ? "Webhook" : "Polling"}${data.telegram.proxy ? " via CF proxy" : ""}`
|
| 492 |
: "Not configured";
|
| 493 |
-
const backupDetail = data.backup?.message
|
| 494 |
-
? escapeHtml(data.backup.message)
|
| 495 |
-
: "No status yet";
|
| 496 |
const keepAliveDetail = keepaliveConfigured
|
| 497 |
? `Pinging <code>${escapeHtml(data.keepalive.targetUrl || "/health")}</code>`
|
| 498 |
: keepaliveStatus === "error" && data.keepalive?.message
|
| 499 |
? escapeHtml(data.keepalive.message)
|
| 500 |
-
: process.env.CLOUDFLARE_WORKERS_TOKEN
|
| 501 |
-
? "Worker pending or failed"
|
| 502 |
-
: "Not configured";
|
| 503 |
-
const serviceOk = data.gateway && data.dashboard;
|
| 504 |
|
| 505 |
const tiles = [
|
| 506 |
-
|
| 507 |
-
|
| 508 |
-
|
| 509 |
-
|
| 510 |
-
|
| 511 |
-
|
| 512 |
-
|
| 513 |
-
? `API on port ${data.ports.gateway}`
|
| 514 |
-
: `Unreachable`,
|
| 515 |
-
tone: data.gateway ? "ok" : "off",
|
| 516 |
-
meta: data.authConfigured ? "Protected" : "Unprotected",
|
| 517 |
-
}),
|
| 518 |
-
renderTile({
|
| 519 |
-
title: "Model",
|
| 520 |
-
value: `<code>${valueOrUnset(data.model)}</code>`,
|
| 521 |
-
detail: `Provider: ${valueOrUnset(data.provider || "auto")}`,
|
| 522 |
-
tone: data.model ? "ok" : "warn",
|
| 523 |
-
}),
|
| 524 |
-
renderTile({
|
| 525 |
-
title: "Runtime",
|
| 526 |
-
value: escapeHtml(data.uptime),
|
| 527 |
-
detail: `Port ${data.ports.public}`,
|
| 528 |
-
tone: "neutral",
|
| 529 |
-
}),
|
| 530 |
-
renderTile({
|
| 531 |
-
title: "Telegram",
|
| 532 |
-
value: toneBadge(
|
| 533 |
-
data.telegram.configured ? "Configured" : "Disabled",
|
| 534 |
-
telegramTone,
|
| 535 |
-
),
|
| 536 |
-
detail: telegramDetail,
|
| 537 |
-
tone: telegramTone,
|
| 538 |
-
}),
|
| 539 |
-
renderTile({
|
| 540 |
-
title: "Backup",
|
| 541 |
-
value: toneBadge(syncStatus.toUpperCase(), syncTone),
|
| 542 |
-
detail: backupDetail,
|
| 543 |
-
tone: syncTone,
|
| 544 |
-
meta: data.backup?.timestamp
|
| 545 |
-
? `<span class="local-time" data-iso="${data.backup.timestamp}"></span>`
|
| 546 |
-
: "",
|
| 547 |
-
}),
|
| 548 |
-
renderTile({
|
| 549 |
-
title: "Keep Awake",
|
| 550 |
-
value: toneBadge(
|
| 551 |
-
keepaliveConfigured ? "CF Cron" : keepaliveStatus.toUpperCase(),
|
| 552 |
-
keepAliveTone,
|
| 553 |
-
),
|
| 554 |
-
detail: keepAliveDetail,
|
| 555 |
-
tone: keepAliveTone,
|
| 556 |
-
}),
|
| 557 |
-
].join("");
|
| 558 |
|
| 559 |
return `<!doctype html>
|
| 560 |
<html lang="en">
|
|
@@ -562,72 +495,320 @@ function renderDashboard(data) {
|
|
| 562 |
<meta charset="utf-8" />
|
| 563 |
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
| 564 |
<title>HuggingMes</title>
|
|
|
|
|
|
|
|
|
|
| 565 |
<style>
|
| 566 |
-
:root {
|
| 567 |
-
|
| 568 |
-
|
| 569 |
-
|
| 570 |
-
|
| 571 |
-
|
| 572 |
-
|
| 573 |
-
|
| 574 |
-
|
| 575 |
-
|
| 576 |
-
|
| 577 |
-
|
| 578 |
-
|
| 579 |
-
|
| 580 |
-
|
| 581 |
-
|
| 582 |
-
|
| 583 |
-
|
| 584 |
-
|
| 585 |
-
|
| 586 |
-
|
| 587 |
-
|
| 588 |
-
|
| 589 |
-
|
| 590 |
-
|
| 591 |
-
|
| 592 |
-
|
| 593 |
-
|
| 594 |
-
|
| 595 |
-
|
| 596 |
-
|
| 597 |
-
|
| 598 |
-
|
| 599 |
-
|
| 600 |
-
|
| 601 |
-
|
| 602 |
-
|
| 603 |
-
|
| 604 |
-
|
| 605 |
-
|
| 606 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 607 |
</style>
|
| 608 |
</head>
|
| 609 |
<body>
|
| 610 |
<main>
|
| 611 |
-
<header>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 612 |
<h1>HuggingMes</h1>
|
| 613 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 614 |
</header>
|
| 615 |
-
|
| 616 |
-
|
| 617 |
-
<
|
| 618 |
-
<
|
|
|
|
| 619 |
</div>
|
| 620 |
-
|
| 621 |
-
|
| 622 |
-
|
| 623 |
-
<footer>
|
|
|
|
|
|
|
|
|
|
| 624 |
</main>
|
| 625 |
<script>
|
| 626 |
document.querySelectorAll('.local-time').forEach(el => {
|
| 627 |
-
const
|
| 628 |
-
if (!isNaN(
|
| 629 |
-
el.textContent = 'At ' + date.toLocaleTimeString();
|
| 630 |
-
}
|
| 631 |
});
|
| 632 |
const inEmbeddedApp = (() => { try { return window.top !== window.self; } catch { return true; } })();
|
| 633 |
const isDirectHfSpaceHost = /\.hf\.space$/i.test(window.location.hostname);
|
|
@@ -635,12 +816,11 @@ function renderDashboard(data) {
|
|
| 635 |
const SPACE_IS_PRIVATE = ${JSON.stringify(SPACE_IS_PRIVATE)};
|
| 636 |
if (SPACE_IS_PRIVATE && isDirectHfSpaceHost && !inEmbeddedApp && HF_SPACE_URL) {
|
| 637 |
const notice = document.createElement('div');
|
| 638 |
-
notice.style.cssText = 'position:fixed;inset:0;display:flex;align-items:center;justify-content:center;background:#
|
| 639 |
-
notice.innerHTML = '<span style="font-size:1.1rem">π Private Space — Redirecting…</span><a href="' + HF_SPACE_URL + '" style="color:#
|
| 640 |
document.body.appendChild(notice);
|
| 641 |
setTimeout(() => { window.location.replace(HF_SPACE_URL); }, 300);
|
| 642 |
}
|
| 643 |
-
// Force new-tab navigation when running inside the HF App iframe or on a raw .hf.space link
|
| 644 |
const openInNewTab = inEmbeddedApp || isDirectHfSpaceHost;
|
| 645 |
document.querySelectorAll('a[data-space-link]').forEach((a) => {
|
| 646 |
if (openInNewTab) {
|
|
|
|
| 446 |
}
|
| 447 |
|
| 448 |
function renderTile({
|
| 449 |
+
title, value, detail = "", tone = "neutral", meta = "",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 450 |
}) {
|
| 451 |
+
const dotColors = { ok: "var(--good)", warn: "var(--warn)", off: "var(--bad)", neutral: "var(--line)" };
|
| 452 |
+
const glowColors = { ok: "rgba(34,197,94,.18)", warn: "rgba(245,197,66,.18)", off: "rgba(251,113,133,.18)", neutral: "transparent" };
|
| 453 |
+
return `<article class="tile tile--${tone}">
|
| 454 |
+
<div class="tile__header">
|
| 455 |
+
<span class="tile__label">${escapeHtml(title)}</span>
|
| 456 |
+
<span class="tile__dot" style="background:${dotColors[tone]};box-shadow:0 0 6px ${glowColors[tone]}"></span>
|
| 457 |
</div>
|
| 458 |
+
<div class="tile__value">${value}</div>
|
| 459 |
+
${detail ? `<div class="tile__detail">${detail}</div>` : ""}
|
| 460 |
+
${meta ? `<div class="tile__meta">${meta}</div>` : ""}
|
| 461 |
</article>`;
|
| 462 |
}
|
| 463 |
|
| 464 |
function renderDashboard(data) {
|
| 465 |
const syncStatus = String(data.backup?.status || "unknown");
|
| 466 |
+
const syncTone = ["success", "restored", "synced", "configured"].includes(syncStatus) ? "ok"
|
| 467 |
+
: syncStatus === "disabled" ? "warn" : "neutral";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 468 |
const telegramTone = data.telegram.configured
|
| 469 |
+
? (data.telegram.webhookListening || !data.telegram.webhook ? "ok" : "warn") : "warn";
|
|
|
|
|
|
|
|
|
|
| 470 |
const keepaliveConfigured = data.keepalive?.configured === true;
|
| 471 |
+
const keepaliveStatus = String(data.keepalive?.status || (process.env.CLOUDFLARE_WORKERS_TOKEN ? "pending" : "not configured"));
|
| 472 |
+
const keepAliveTone = keepaliveConfigured ? "ok" : process.env.CLOUDFLARE_WORKERS_TOKEN ? "warn" : "neutral";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 473 |
const telegramDetail = data.telegram.configured
|
| 474 |
+
? `${data.telegram.webhook ? "Webhook" : "Polling"}${data.telegram.proxy ? " Β· via CF proxy" : ""}`
|
| 475 |
: "Not configured";
|
| 476 |
+
const backupDetail = data.backup?.message ? escapeHtml(data.backup.message) : "No status yet";
|
|
|
|
|
|
|
| 477 |
const keepAliveDetail = keepaliveConfigured
|
| 478 |
? `Pinging <code>${escapeHtml(data.keepalive.targetUrl || "/health")}</code>`
|
| 479 |
: keepaliveStatus === "error" && data.keepalive?.message
|
| 480 |
? escapeHtml(data.keepalive.message)
|
| 481 |
+
: process.env.CLOUDFLARE_WORKERS_TOKEN ? "Worker pending or failed" : "Not configured";
|
|
|
|
|
|
|
|
|
|
| 482 |
|
| 483 |
const tiles = [
|
| 484 |
+
{ title: "Gateway", value: toneBadge(data.gateway ? "Online" : "Offline", data.gateway ? "ok" : "off"), detail: data.gateway ? `API on port ${data.ports.gateway}` : "Unreachable", tone: data.gateway ? "ok" : "off", meta: data.authConfigured ? "Protected" : "Unprotected" },
|
| 485 |
+
{ title: "Model", value: `<code>${valueOrUnset(data.model)}</code>`, detail: `Provider: ${valueOrUnset(data.provider || "auto")}`, tone: data.model ? "ok" : "warn" },
|
| 486 |
+
{ title: "Runtime", value: escapeHtml(data.uptime), detail: `Port ${data.ports.public}`, tone: "neutral" },
|
| 487 |
+
{ title: "Telegram", value: toneBadge(data.telegram.configured ? "Active" : "Disabled", telegramTone), detail: telegramDetail, tone: telegramTone },
|
| 488 |
+
{ title: "Backup", value: toneBadge(syncStatus.toUpperCase(), syncTone), detail: backupDetail, tone: syncTone, meta: data.backup?.timestamp ? `<span class="local-time" data-iso="${data.backup.timestamp}"></span>` : "" },
|
| 489 |
+
{ title: "Keep Awake", value: toneBadge(keepaliveConfigured ? "CF Cron" : keepaliveStatus.toUpperCase(), keepAliveTone), detail: keepAliveDetail, tone: keepAliveTone },
|
| 490 |
+
].map(renderTile).join("");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 491 |
|
| 492 |
return `<!doctype html>
|
| 493 |
<html lang="en">
|
|
|
|
| 495 |
<meta charset="utf-8" />
|
| 496 |
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
| 497 |
<title>HuggingMes</title>
|
| 498 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 499 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 500 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
| 501 |
<style>
|
| 502 |
+
:root {
|
| 503 |
+
--bg: #06060c;
|
| 504 |
+
--surface: rgba(255,255,255,0.03);
|
| 505 |
+
--surface2: rgba(255,255,255,0.05);
|
| 506 |
+
--border: rgba(255,255,255,0.08);
|
| 507 |
+
--border2: rgba(255,255,255,0.14);
|
| 508 |
+
--text: #f0f2ff;
|
| 509 |
+
--muted: #6b7280;
|
| 510 |
+
--soft: #9ca3af;
|
| 511 |
+
--good: #34d399;
|
| 512 |
+
--warn: #fbbf24;
|
| 513 |
+
--bad: #f87171;
|
| 514 |
+
--accent: #818cf8;
|
| 515 |
+
--accent-glow: rgba(129,140,248,.25);
|
| 516 |
+
--r: 16px;
|
| 517 |
+
--r2: 10px;
|
| 518 |
+
}
|
| 519 |
+
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
| 520 |
+
html { scroll-behavior: smooth; }
|
| 521 |
+
body {
|
| 522 |
+
font-family: 'Inter', ui-sans-serif, system-ui, sans-serif;
|
| 523 |
+
background: var(--bg);
|
| 524 |
+
color: var(--text);
|
| 525 |
+
min-height: 100vh;
|
| 526 |
+
-webkit-font-smoothing: antialiased;
|
| 527 |
+
}
|
| 528 |
+
body::before {
|
| 529 |
+
content: '';
|
| 530 |
+
position: fixed;
|
| 531 |
+
inset: 0;
|
| 532 |
+
background:
|
| 533 |
+
radial-gradient(ellipse 80% 60% at 50% -20%, rgba(99,102,241,.12) 0%, transparent 60%),
|
| 534 |
+
radial-gradient(ellipse 50% 40% at 80% 110%, rgba(129,140,248,.06) 0%, transparent 50%);
|
| 535 |
+
pointer-events: none;
|
| 536 |
+
z-index: 0;
|
| 537 |
+
}
|
| 538 |
+
main {
|
| 539 |
+
position: relative;
|
| 540 |
+
z-index: 1;
|
| 541 |
+
max-width: 860px;
|
| 542 |
+
margin: 0 auto;
|
| 543 |
+
padding: 64px 24px 80px;
|
| 544 |
+
}
|
| 545 |
+
/* ββ Hero ββ */
|
| 546 |
+
.hero { text-align: center; margin-bottom: 48px; }
|
| 547 |
+
.hero-badge {
|
| 548 |
+
display: inline-flex;
|
| 549 |
+
align-items: center;
|
| 550 |
+
gap: 7px;
|
| 551 |
+
background: rgba(129,140,248,.1);
|
| 552 |
+
border: 1px solid rgba(129,140,248,.25);
|
| 553 |
+
border-radius: 999px;
|
| 554 |
+
padding: 6px 14px;
|
| 555 |
+
font-size: .72rem;
|
| 556 |
+
font-weight: 600;
|
| 557 |
+
color: var(--accent);
|
| 558 |
+
letter-spacing: .04em;
|
| 559 |
+
text-transform: uppercase;
|
| 560 |
+
margin-bottom: 20px;
|
| 561 |
+
}
|
| 562 |
+
.pulse-dot {
|
| 563 |
+
width: 7px; height: 7px; border-radius: 50%;
|
| 564 |
+
background: var(--good);
|
| 565 |
+
box-shadow: 0 0 0 0 rgba(52,211,153,.4);
|
| 566 |
+
animation: pulse 2s infinite;
|
| 567 |
+
}
|
| 568 |
+
@keyframes pulse {
|
| 569 |
+
0% { box-shadow: 0 0 0 0 rgba(52,211,153,.4); }
|
| 570 |
+
70% { box-shadow: 0 0 0 7px rgba(52,211,153,0); }
|
| 571 |
+
100% { box-shadow: 0 0 0 0 rgba(52,211,153,0); }
|
| 572 |
+
}
|
| 573 |
+
h1 {
|
| 574 |
+
font-size: clamp(2.2rem, 6vw, 3.2rem);
|
| 575 |
+
font-weight: 800;
|
| 576 |
+
letter-spacing: -.04em;
|
| 577 |
+
line-height: 1.05;
|
| 578 |
+
background: linear-gradient(135deg, #f0f2ff 30%, #818cf8 70%, #c084fc 100%);
|
| 579 |
+
-webkit-background-clip: text;
|
| 580 |
+
-webkit-text-fill-color: transparent;
|
| 581 |
+
background-clip: text;
|
| 582 |
+
margin-bottom: 12px;
|
| 583 |
+
}
|
| 584 |
+
.subtitle {
|
| 585 |
+
font-size: 1rem;
|
| 586 |
+
color: var(--muted);
|
| 587 |
+
font-weight: 400;
|
| 588 |
+
line-height: 1.5;
|
| 589 |
+
max-width: 480px;
|
| 590 |
+
margin: 0 auto 28px;
|
| 591 |
+
}
|
| 592 |
+
/* ββ CTA buttons ββ */
|
| 593 |
+
.cta-row {
|
| 594 |
+
display: flex;
|
| 595 |
+
gap: 10px;
|
| 596 |
+
justify-content: center;
|
| 597 |
+
flex-wrap: wrap;
|
| 598 |
+
margin-bottom: 56px;
|
| 599 |
+
}
|
| 600 |
+
.btn {
|
| 601 |
+
display: inline-flex;
|
| 602 |
+
align-items: center;
|
| 603 |
+
gap: 8px;
|
| 604 |
+
padding: 11px 22px;
|
| 605 |
+
border-radius: var(--r2);
|
| 606 |
+
font-weight: 600;
|
| 607 |
+
font-size: .88rem;
|
| 608 |
+
text-decoration: none;
|
| 609 |
+
transition: all .2s cubic-bezier(.4,0,.2,1);
|
| 610 |
+
cursor: pointer;
|
| 611 |
+
border: none;
|
| 612 |
+
}
|
| 613 |
+
.btn-primary {
|
| 614 |
+
background: linear-gradient(135deg, #6366f1, #818cf8);
|
| 615 |
+
color: #fff;
|
| 616 |
+
box-shadow: 0 4px 20px rgba(99,102,241,.35);
|
| 617 |
+
}
|
| 618 |
+
.btn-primary:hover {
|
| 619 |
+
transform: translateY(-2px);
|
| 620 |
+
box-shadow: 0 8px 30px rgba(99,102,241,.45);
|
| 621 |
+
}
|
| 622 |
+
.btn-secondary {
|
| 623 |
+
background: var(--surface2);
|
| 624 |
+
color: var(--text);
|
| 625 |
+
border: 1px solid var(--border2);
|
| 626 |
+
backdrop-filter: blur(8px);
|
| 627 |
+
}
|
| 628 |
+
.btn-secondary:hover {
|
| 629 |
+
background: rgba(255,255,255,.08);
|
| 630 |
+
border-color: rgba(255,255,255,.2);
|
| 631 |
+
transform: translateY(-1px);
|
| 632 |
+
}
|
| 633 |
+
/* ββ Divider ββ */
|
| 634 |
+
.divider {
|
| 635 |
+
display: flex;
|
| 636 |
+
align-items: center;
|
| 637 |
+
gap: 12px;
|
| 638 |
+
margin-bottom: 20px;
|
| 639 |
+
}
|
| 640 |
+
.divider-line {
|
| 641 |
+
flex: 1;
|
| 642 |
+
height: 1px;
|
| 643 |
+
background: linear-gradient(90deg, transparent, var(--border), transparent);
|
| 644 |
+
}
|
| 645 |
+
.divider-label {
|
| 646 |
+
font-size: .7rem;
|
| 647 |
+
font-weight: 600;
|
| 648 |
+
letter-spacing: .12em;
|
| 649 |
+
text-transform: uppercase;
|
| 650 |
+
color: var(--muted);
|
| 651 |
+
}
|
| 652 |
+
/* ββ Stats row ββ */
|
| 653 |
+
.stats-row {
|
| 654 |
+
display: grid;
|
| 655 |
+
grid-template-columns: repeat(3, 1fr);
|
| 656 |
+
gap: 12px;
|
| 657 |
+
margin-bottom: 32px;
|
| 658 |
+
}
|
| 659 |
+
.stat {
|
| 660 |
+
background: var(--surface);
|
| 661 |
+
border: 1px solid var(--border);
|
| 662 |
+
border-radius: var(--r);
|
| 663 |
+
padding: 18px 16px;
|
| 664 |
+
text-align: center;
|
| 665 |
+
backdrop-filter: blur(10px);
|
| 666 |
+
}
|
| 667 |
+
.stat__val {
|
| 668 |
+
font-family: 'JetBrains Mono', monospace;
|
| 669 |
+
font-size: 1.5rem;
|
| 670 |
+
font-weight: 600;
|
| 671 |
+
color: var(--text);
|
| 672 |
+
margin-bottom: 4px;
|
| 673 |
+
}
|
| 674 |
+
.stat__lab {
|
| 675 |
+
font-size: .68rem;
|
| 676 |
+
color: var(--muted);
|
| 677 |
+
text-transform: uppercase;
|
| 678 |
+
letter-spacing: .1em;
|
| 679 |
+
font-weight: 600;
|
| 680 |
+
}
|
| 681 |
+
/* ββ Tiles grid ββ */
|
| 682 |
+
.tiles {
|
| 683 |
+
display: grid;
|
| 684 |
+
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
| 685 |
+
gap: 12px;
|
| 686 |
+
margin-bottom: 32px;
|
| 687 |
+
}
|
| 688 |
+
.tile {
|
| 689 |
+
background: var(--surface);
|
| 690 |
+
border: 1px solid var(--border);
|
| 691 |
+
border-radius: var(--r);
|
| 692 |
+
padding: 20px;
|
| 693 |
+
display: flex;
|
| 694 |
+
flex-direction: column;
|
| 695 |
+
gap: 10px;
|
| 696 |
+
position: relative;
|
| 697 |
+
overflow: hidden;
|
| 698 |
+
backdrop-filter: blur(10px);
|
| 699 |
+
transition: border-color .2s, box-shadow .2s, transform .2s;
|
| 700 |
+
}
|
| 701 |
+
.tile::before {
|
| 702 |
+
content: '';
|
| 703 |
+
position: absolute;
|
| 704 |
+
top: 0; left: 0; right: 0;
|
| 705 |
+
height: 1px;
|
| 706 |
+
background: linear-gradient(90deg, transparent, rgba(255,255,255,.1), transparent);
|
| 707 |
+
}
|
| 708 |
+
.tile--ok { border-color: rgba(52,211,153,.2); }
|
| 709 |
+
.tile--ok:hover { box-shadow: 0 0 20px rgba(52,211,153,.08); }
|
| 710 |
+
.tile--warn { border-color: rgba(251,191,36,.2); }
|
| 711 |
+
.tile--warn:hover { box-shadow: 0 0 20px rgba(251,191,36,.08); }
|
| 712 |
+
.tile--off { border-color: rgba(248,113,113,.2); }
|
| 713 |
+
.tile--off:hover { box-shadow: 0 0 20px rgba(248,113,113,.08); }
|
| 714 |
+
.tile:hover { transform: translateY(-2px); border-color: var(--border2); }
|
| 715 |
+
.tile__header {
|
| 716 |
+
display: flex;
|
| 717 |
+
align-items: center;
|
| 718 |
+
justify-content: space-between;
|
| 719 |
+
}
|
| 720 |
+
.tile__label {
|
| 721 |
+
font-size: .66rem;
|
| 722 |
+
font-weight: 700;
|
| 723 |
+
letter-spacing: .14em;
|
| 724 |
+
text-transform: uppercase;
|
| 725 |
+
color: var(--muted);
|
| 726 |
+
}
|
| 727 |
+
.tile__dot {
|
| 728 |
+
width: 8px; height: 8px;
|
| 729 |
+
border-radius: 50%;
|
| 730 |
+
flex-shrink: 0;
|
| 731 |
+
transition: box-shadow .3s;
|
| 732 |
+
}
|
| 733 |
+
.tile__value { font-size: 1.05rem; font-weight: 700; line-height: 1.2; }
|
| 734 |
+
.tile__detail { color: var(--soft); font-size: .8rem; line-height: 1.45; }
|
| 735 |
+
.tile__meta { color: var(--muted); font-size: .72rem; margin-top: auto; padding-top: 4px; }
|
| 736 |
+
code {
|
| 737 |
+
background: rgba(255,255,255,.06);
|
| 738 |
+
border: 1px solid var(--border);
|
| 739 |
+
border-radius: 6px;
|
| 740 |
+
padding: 2px 7px;
|
| 741 |
+
font-family: 'JetBrains Mono', monospace;
|
| 742 |
+
font-size: .88em;
|
| 743 |
+
color: var(--text);
|
| 744 |
+
}
|
| 745 |
+
/* ββ Badges ββ */
|
| 746 |
+
.badge {
|
| 747 |
+
display: inline-flex;
|
| 748 |
+
align-items: center;
|
| 749 |
+
border-radius: 999px;
|
| 750 |
+
padding: 3px 10px;
|
| 751 |
+
font-size: .7rem;
|
| 752 |
+
font-weight: 700;
|
| 753 |
+
letter-spacing: .04em;
|
| 754 |
+
text-transform: uppercase;
|
| 755 |
+
}
|
| 756 |
+
.badge.ok { color: var(--good); background: rgba(52,211,153,.12); border: 1px solid rgba(52,211,153,.25); }
|
| 757 |
+
.badge.warn { color: var(--warn); background: rgba(251,191,36,.12); border: 1px solid rgba(251,191,36,.25); }
|
| 758 |
+
.badge.off { color: var(--bad); background: rgba(248,113,113,.12); border: 1px solid rgba(248,113,113,.25); }
|
| 759 |
+
.badge.neutral { color: var(--soft); background: rgba(255,255,255,.05); border: 1px solid var(--border); }
|
| 760 |
+
.muted { color: var(--muted); font-style: italic; }
|
| 761 |
+
/* ββ Footer ββ */
|
| 762 |
+
footer {
|
| 763 |
+
text-align: center;
|
| 764 |
+
padding-top: 32px;
|
| 765 |
+
border-top: 1px solid var(--border);
|
| 766 |
+
color: var(--muted);
|
| 767 |
+
font-size: .78rem;
|
| 768 |
+
}
|
| 769 |
+
footer a { color: var(--accent); text-decoration: none; }
|
| 770 |
+
footer a:hover { text-decoration: underline; }
|
| 771 |
+
/* ββ Responsive ββ */
|
| 772 |
+
@media (max-width: 600px) {
|
| 773 |
+
main { padding: 40px 16px 60px; }
|
| 774 |
+
.stats-row { grid-template-columns: 1fr; }
|
| 775 |
+
h1 { font-size: 2rem; }
|
| 776 |
+
}
|
| 777 |
</style>
|
| 778 |
</head>
|
| 779 |
<body>
|
| 780 |
<main>
|
| 781 |
+
<header class="hero">
|
| 782 |
+
<div class="hero-badge">
|
| 783 |
+
<span class="pulse-dot"></span>
|
| 784 |
+
Self-hosted Β· 24/7
|
| 785 |
+
</div>
|
| 786 |
<h1>HuggingMes</h1>
|
| 787 |
+
<p class="subtitle">Self-hosted Hermes Agent gateway on Hugging Face Spaces. Chat, tools, memory β always on.</p>
|
| 788 |
+
<div class="cta-row">
|
| 789 |
+
<a class="btn btn-primary" data-space-link="app" href="${APP_BASE}/">Open Hermes Agent β</a>
|
| 790 |
+
<a class="btn btn-secondary" data-space-link="terminal" href="/terminal/">π» Terminal</a>
|
| 791 |
+
<a class="btn btn-secondary" data-space-link="env-builder" href="/env-builder">βοΈ ENV Builder</a>
|
| 792 |
+
</div>
|
| 793 |
</header>
|
| 794 |
+
|
| 795 |
+
<div class="divider">
|
| 796 |
+
<div class="divider-line"></div>
|
| 797 |
+
<span class="divider-label">System Status</span>
|
| 798 |
+
<div class="divider-line"></div>
|
| 799 |
</div>
|
| 800 |
+
|
| 801 |
+
<div class="tiles">${tiles}</div>
|
| 802 |
+
|
| 803 |
+
<footer>
|
| 804 |
+
Built by <a href="https://github.com/somratpro" target="_blank">@somratpro</a> Β·
|
| 805 |
+
<a href="https://github.com/NousResearch/hermes-agent" target="_blank">Hermes Agent</a>
|
| 806 |
+
</footer>
|
| 807 |
</main>
|
| 808 |
<script>
|
| 809 |
document.querySelectorAll('.local-time').forEach(el => {
|
| 810 |
+
const d = new Date(el.getAttribute('data-iso'));
|
| 811 |
+
if (!isNaN(d)) el.textContent = 'At ' + d.toLocaleTimeString();
|
|
|
|
|
|
|
| 812 |
});
|
| 813 |
const inEmbeddedApp = (() => { try { return window.top !== window.self; } catch { return true; } })();
|
| 814 |
const isDirectHfSpaceHost = /\.hf\.space$/i.test(window.location.hostname);
|
|
|
|
| 816 |
const SPACE_IS_PRIVATE = ${JSON.stringify(SPACE_IS_PRIVATE)};
|
| 817 |
if (SPACE_IS_PRIVATE && isDirectHfSpaceHost && !inEmbeddedApp && HF_SPACE_URL) {
|
| 818 |
const notice = document.createElement('div');
|
| 819 |
+
notice.style.cssText = 'position:fixed;inset:0;display:flex;align-items:center;justify-content:center;background:#06060c;color:#f0f2ff;font-family:sans-serif;flex-direction:column;gap:16px;z-index:9999';
|
| 820 |
+
notice.innerHTML = '<span style="font-size:1.1rem">π Private Space — Redirecting…</span><a href="' + HF_SPACE_URL + '" style="color:#818cf8;font-size:.85rem">Click here if not redirected</a>';
|
| 821 |
document.body.appendChild(notice);
|
| 822 |
setTimeout(() => { window.location.replace(HF_SPACE_URL); }, 300);
|
| 823 |
}
|
|
|
|
| 824 |
const openInNewTab = inEmbeddedApp || isDirectHfSpaceHost;
|
| 825 |
document.querySelectorAll('a[data-space-link]').forEach((a) => {
|
| 826 |
if (openInNewTab) {
|