aipackage/cezen-portal/health.html

296 lines
15 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Health Center — Nexus One AI</title>
<link rel="stylesheet" href="style.css?v=12">
<style>
/* ── Health Center layout ──────────────────────────────────────── */
.hc-summary {
display: flex; align-items: center; gap: 14px;
background: var(--surface); border: 1px solid var(--border);
border-radius: var(--radius-lg); padding: 20px 24px; margin-bottom: 24px;
box-shadow: var(--shadow-sm);
}
.hc-summary-dot { width: 16px; height: 16px; border-radius: 50%; flex-shrink: 0; }
.hc-summary-dot.loading { background: var(--border-strong); }
.hc-summary-dot.healthy { background: var(--green); box-shadow: 0 0 0 5px rgba(22,163,74,.15); }
.hc-summary-dot.warn { background: var(--amber); box-shadow: 0 0 0 5px rgba(217,119,6,.15); }
.hc-summary-dot.crit { background: var(--red); box-shadow: 0 0 0 5px rgba(220,38,38,.15); animation: hc-pulse 1.6s ease-in-out infinite; }
@keyframes hc-pulse { 0%,100% { opacity: 1; } 50% { opacity: .45; } }
.hc-summary-text { flex: 1; }
.hc-summary-title { font-size: 16px; font-weight: 800; color: var(--text-primary); }
.hc-summary-sub { font-size: 13px; color: var(--text-tertiary); margin-top: 2px; }
.hc-refresh-btn {
padding: 9px 16px; border-radius: var(--radius-md); border: 1px solid var(--border);
background: var(--surface); color: var(--text-secondary); font-family: inherit;
font-size: 13px; font-weight: 600; cursor: pointer; transition: .15s;
}
.hc-refresh-btn:hover { border-color: var(--brand); color: var(--brand); }
.hc-updated { font-size: 12px; color: var(--text-tertiary); margin-top: 10px; }
.hc-grid {
display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px;
}
@media(max-width: 900px) { .hc-grid { grid-template-columns: repeat(2, 1fr); } }
@media(max-width: 600px) { .hc-grid { grid-template-columns: 1fr; } }
.hc-card {
background: var(--surface); border: 1px solid var(--border);
border-radius: var(--radius-lg); padding: 18px 20px;
display: flex; gap: 14px; align-items: flex-start;
}
.hc-card-icon {
width: 36px; height: 36px; border-radius: var(--radius-md); flex-shrink: 0;
display: flex; align-items: center; justify-content: center;
}
.hc-card-icon svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; }
.hc-card.ok .hc-card-icon { background: var(--green-lt); color: var(--green); }
.hc-card.warning .hc-card-icon { background: var(--amber-lt); color: var(--amber); }
.hc-card.critical .hc-card-icon{ background: var(--red-lt); color: var(--red); }
.hc-card.loading .hc-card-icon { background: var(--bg); color: var(--text-tertiary); }
.hc-card-body { flex: 1; min-width: 0; }
.hc-card-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 4px; }
.hc-card-name { font-size: 13.5px; font-weight: 700; color: var(--text-primary); }
.hc-card-status {
font-size: .6875rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
padding: 2px 9px; border-radius: var(--radius-pill); flex-shrink: 0;
}
.hc-card.ok .hc-card-status { background: var(--green-lt); color: var(--green); }
.hc-card.warning .hc-card-status { background: var(--amber-lt); color: var(--amber); }
.hc-card.critical .hc-card-status { background: var(--red-lt); color: var(--red); }
.hc-card.loading .hc-card-status { background: var(--bg); color: var(--text-tertiary); }
.hc-card-detail { font-size: 12.5px; color: var(--text-secondary); line-height: 1.5; }
.hc-unavailable {
display: flex; align-items: flex-start; gap: 10px;
background: var(--amber-lt); border: 1px solid var(--amber-border);
border-radius: var(--radius-md); padding: 14px 16px; margin-bottom: 20px;
color: var(--amber); font-size: 13px; line-height: 1.5;
}
</style>
</head>
<body data-role="admin">
<header class="topnav">
<a href="index.html" class="brand">Nexus One <span>AI</span></a>
<nav>
<a href="index.html">Home</a>
<a href="quickstart.html">Quick Start</a>
<a href="prompts.html">Prompt Library</a>
<a href="usecases.html">Use Cases</a>
<div class="nav-dropdown">
<button class="nav-drop-btn">AI Tools ▾</button>
<div class="nav-drop-menu">
<span class="nav-drop-cat">INTELLIGENCE /</span>
<a href="documents.html">Document Intelligence</a>
<a href="chat-multi.html">AI Workspace</a>
<a href="prompt-studio.html">Prompt Studio</a>
<a href="meeting.html">Meeting Assistant</a>
<span class="nav-drop-cat">AUTOMATION /</span>
<a href="agents.html">Agent Studio</a>
<a href="schedules.html">Scheduled Jobs</a>
<a href="workflows.html">Workflow Designer</a>
<span class="nav-drop-cat">QUALITY /</span>
<a href="evals.html">AI Eval Suite</a>
<a href="chatrooms.html">Chat Rooms</a>
</div>
</div>
<span class="nav-sep"></span>
<div class="nav-dropdown">
<button class="nav-drop-btn">Help ▾</button>
<div class="nav-drop-menu">
<span class="nav-drop-cat">LEARN /</span>
<a href="quickstart.html">Quick Start</a>
<a href="models.html">Models</a>
<span class="nav-drop-cat">SUPPORT /</span>
<a href="troubleshooting.html">Troubleshoot</a>
<a href="faq.html">FAQ</a>
<span class="nav-drop-cat">MORE /</span>
<a href="glossary.html">Glossary</a>
<a href="whats-new.html">What's New</a>
</div>
</div>
<div class="nav-dropdown">
<button class="nav-drop-btn active">Admin ▾</button>
<div class="nav-drop-menu nav-drop-menu-wide">
<span class="nav-drop-cat">DOCS /</span>
<a href="security.html">Security &amp; Privacy</a>
<a href="admin.html">Admin Guide</a>
<span class="nav-drop-cat">MONITOR /</span>
<a href="dashboard.html">Dashboard</a>
<a href="analytics.html">Usage Analytics</a>
<a href="audit.html">Governance & Audit</a>
<a href="health.html" class="active">Health Center</a>
<a href="feedback.html">Feedback &amp; Ratings</a>
<span class="nav-drop-cat">MANAGE /</span>
<a href="users.html">Users</a>
<a href="teams.html">Teams</a>
<a href="models-admin.html">Model Catalog</a>
<a href="training.html">Training</a>
<a href="knowledge.html">Knowledge Base</a>
<span class="nav-drop-cat">TOOLS /</span>
<a href="apikeys.html">API Keys</a>
<a href="benchmark.html">Benchmarking</a>
<a href="model-compare.html">Model Compare</a>
<a href="api-playground.html">API Playground</a>
<a href="guardrails.html">Guardrails</a>
<a href="rag-quality.html">RAG Quality</a>
<a href="router.html">Model Router</a>
<a href="connectors.html">MCP & Enterprise Connectors</a>
<span class="nav-drop-cat">SYSTEM /</span>
<a href="appliance.html">Appliance Ops</a>
<a href="console.html">Console</a>
<a href="settings.html">Settings</a>
</div>
</div>
</nav>
<div class="nav-right">
<div id="nav-org-logo" class="nav-org-logo"></div>
<span class="nav-tier-badge" data-brand="tier" data-tier-slug="basic">Basic Tier</span>
<a href="notifications.html" class="nav-icon-btn" title="Notifications">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg>
</a>
</div>
</header>
<div class="page-hero">
<div class="eyebrow">Admin Dashboard</div>
<h1>Health Center</h1>
<p>Unified status for every subsystem Nexus One AI depends on — GPU, drivers, CUDA, models, portal, API, scheduler, storage, backup, license, SSL and database.</p>
</div>
<div class="content">
<div class="hc-summary">
<div class="hc-summary-dot loading" id="hc-summary-dot"></div>
<div class="hc-summary-text">
<div class="hc-summary-title" id="hc-summary-title">Checking system health…</div>
<div class="hc-summary-sub" id="hc-summary-sub">Contacting the API</div>
</div>
<button class="hc-refresh-btn" onclick="loadHealth()">Refresh</button>
</div>
<div class="hc-unavailable" id="hc-unavailable" style="display:none">
<span class="icon-svg icon-sm" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg></span>
<span>Couldn't reach <code>/api/health</code>. The API itself may be down — check that the cezen-api service is running.</span>
</div>
<div class="hc-grid" id="hc-grid"></div>
<div class="hc-updated" id="hc-updated"></div>
</div>
<footer>
<p>Nexus One AI &nbsp;·&nbsp; Powered by Cezen &nbsp;·&nbsp; <span data-brand="tier">Basic Tier</span></p>
<p>Questions? <a href="mailto:support@cezentech.com">support@cezentech.com</a> &nbsp;·&nbsp; <a href="https://cezentech.com" target="_blank">cezentech.com</a></p>
</footer>
<script>
const _API = '/api';
const CHECK_META = {
gpu: { label: 'GPU', icon: '<path d="M2 7h20v10H2z"/><path d="M6 7V5M10 7V5M14 7V5M18 7V5M6 17v2M10 17v2M14 17v2M18 17v2"/>' },
drivers: { label: 'Drivers', icon: '<path d="M12 2v4"/><path d="M12 18v4"/><path d="M4.93 4.93l2.83 2.83"/><path d="M16.24 16.24l2.83 2.83"/><path d="M2 12h4"/><path d="M18 12h4"/><path d="M4.93 19.07l2.83-2.83"/><path d="M16.24 7.76l2.83-2.83"/>' },
cuda: { label: 'CUDA', icon: '<rect x="4" y="4" width="16" height="16" rx="2"/><rect x="9" y="9" width="6" height="6"/>' },
models: { label: 'Models', icon: '<circle cx="12" cy="12" r="10"/><path d="M12 8v4l3 2"/>' },
portal: { label: 'Portal', icon: '<rect x="3" y="4" width="18" height="16" rx="2"/><path d="M3 9h18"/>' },
api: { label: 'API', icon: '<path d="M4 17V7a2 2 0 0 1 2-2h4l2 2h6a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2z"/>' },
scheduler: { label: 'Scheduler', icon: '<rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/>' },
storage: { label: 'Storage', icon: '<ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M3 5v14c0 1.66 4.03 3 9 3s9-1.34 9-3V5"/><path d="M3 12c0 1.66 4.03 3 9 3s9-1.34 9-3"/>' },
backup: { label: 'Backup', icon: '<polyline points="20 7 20 3 4 3 4 7"/><path d="M20 21H4a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2z"/><path d="M12 12v6M9 15l3-3 3 3"/>' },
license: { label: 'License', icon: '<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/>' },
ssl: { label: 'SSL', icon: '<rect x="3" y="11" width="18" height="10" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>' },
database: { label: 'Database', icon: '<ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M3 5v6c0 1.66 4.03 3 9 3s9-1.34 9-3V5"/><path d="M3 11v8c0 1.66 4.03 3 9 3s9-1.34 9-3v-8"/>' },
};
const CHECK_ORDER = ['api','portal','database','scheduler','models','gpu','drivers','cuda','storage','backup','license','ssl'];
function escHtml(s) { return (s==null?'':String(s)).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }
function renderLoadingGrid() {
document.getElementById('hc-grid').innerHTML = CHECK_ORDER.map(key => {
const meta = CHECK_META[key];
return `<div class="hc-card loading">
<div class="hc-card-icon"><svg viewBox="0 0 24 24">${meta.icon}</svg></div>
<div class="hc-card-body">
<div class="hc-card-top"><span class="hc-card-name">${meta.label}</span><span class="hc-card-status">Checking</span></div>
<div class="hc-card-detail">Loading…</div>
</div>
</div>`;
}).join('');
}
async function loadHealth() {
renderLoadingGrid();
document.getElementById('hc-unavailable').style.display = 'none';
try {
const res = await fetch(`${_API}/health`, { credentials: 'include' });
if (!res.ok) throw new Error('HTTP ' + res.status);
const data = await res.json();
renderSummary(data.status);
renderGrid(data.checks || {});
document.getElementById('hc-updated').textContent = 'Updated ' + new Date(data.generated_at || Date.now()).toLocaleTimeString([], {hour:'2-digit',minute:'2-digit',second:'2-digit'});
} catch (e) {
document.getElementById('hc-unavailable').style.display = '';
renderSummary(null);
document.getElementById('hc-grid').innerHTML = '';
document.getElementById('hc-updated').textContent = '';
}
}
function renderSummary(status) {
const dot = document.getElementById('hc-summary-dot');
const title = document.getElementById('hc-summary-title');
const sub = document.getElementById('hc-summary-sub');
if (window.CezenExperience) {
const mapped = status === 'ok' ? 'ready' : status === 'warning' ? 'degraded' : status === 'critical' ? 'failed' : 'unavailable';
title.className = 'hc-summary-title cx-state cx-state-' + mapped;
title.setAttribute('role', mapped === 'failed' ? 'alert' : 'status');
}
if (status === 'ok') {
dot.className = 'hc-summary-dot healthy';
title.textContent = 'Healthy — all systems normal';
sub.textContent = 'Every subsystem below is reporting OK.';
} else if (status === 'warning') {
dot.className = 'hc-summary-dot warn';
title.textContent = 'Warning — some items need attention';
sub.textContent = 'Review the amber cards below.';
} else if (status === 'critical') {
dot.className = 'hc-summary-dot crit';
title.textContent = 'Critical — action required';
sub.textContent = 'Review the red cards below as soon as possible.';
} else {
dot.className = 'hc-summary-dot loading';
title.textContent = 'Health data unavailable';
sub.textContent = 'Could not reach the API.';
}
}
function renderGrid(checks) {
const statusLabel = { ok: 'Healthy', warning: 'Warning', critical: 'Critical' };
document.getElementById('hc-grid').innerHTML = CHECK_ORDER.map(key => {
const meta = CHECK_META[key];
const c = checks[key] || { status: 'warning', detail: 'No data returned for this check.' };
const cls = ['ok','warning','critical'].includes(c.status) ? c.status : 'warning';
return `<div class="hc-card ${cls}">
<div class="hc-card-icon"><svg viewBox="0 0 24 24">${meta.icon}</svg></div>
<div class="hc-card-body">
<div class="hc-card-top"><span class="hc-card-name">${meta.label}</span><span class="hc-card-status">${statusLabel[cls]}</span></div>
<div class="hc-card-detail">${escHtml(c.detail || '')}</div>
</div>
</div>`;
}).join('');
}
loadHealth();
setInterval(loadHealth, 30000);
</script>
<script src="auth.js"></script>
<script src="experience.js?v=1"></script>
<script src="branding.js?v=3"></script>
</body>
</html>