Compare commits

...

3 Commits

4 changed files with 19 additions and 18 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "site-gateway",
"version": "0.11.50",
"version": "0.11.53",
"private": true,
"description": "Site Gateway: simple self-hosted website publishing, reverse proxying, and automatic HTTPS.",
"type": "module",
+4 -3
View File
@@ -124,6 +124,7 @@ function renderDashboard() {
const overall = $("#overall-health");
overall.className = `health-badge ${hasErrors ? "error" : isChecking || hasNothingRunning ? "warning" : "healthy"}`;
overall.textContent = hasErrors ? "Needs attention" : isChecking ? "Checking" : hasNothingRunning ? "Idle" : "Healthy";
$("#health-panel").className = `dashboard-panel health-panel ${hasErrors ? "status-error" : isChecking || hasNothingRunning ? "status-warning" : "status-healthy"}`;
$("#gateway-health-dot").className = `status-dot ${probeClass(data.gateway)}`;
$("#gateway-health-copy").textContent = probeCopy(data.gateway, data.gateway.lastReload ? `Ready · reloaded ${formatTime(data.gateway.lastReload)}` : "Ready and responding", "Caddy is not responding");
$("#http-health-dot").className = `status-dot ${probeClass(data.services.http)}`;
@@ -132,7 +133,7 @@ function renderDashboard() {
$("#https-health-copy").textContent = probeCopy(data.services.https, `Ready and responding · ${data.services.https.activeDomains} TLS domain${data.services.https.activeDomains === 1 ? "" : "s"}`, "Not responding", "Not configured · no TLS domains enabled");
$("#storage-health-dot").className = `status-dot ${data.services.storage.healthy ? "running" : "error"}`;
$("#storage-health-copy").textContent = data.services.storage.healthy ? "Ready · /data is readable and writable" : "Permission error · check /data";
$("#health-checked").textContent = `Last checked ${formatTime(data.checkedAt)}`;
$("#health-checked").innerHTML = `<span class="live-dot" id="health-live-dot"></span>Last checked ${formatTime(data.checkedAt)}`;
updateDashboardUptime(data.system.uptimeSeconds);
$("#system-memory").textContent = formatBytes(data.system.memoryBytes);
$("#system-data").textContent = formatBytes(data.system.dataBytes);
@@ -247,7 +248,7 @@ async function loadFeatureView() {
}
function render() {
const viewHash = state.view === "administration" ? `administration/${state.adminTab || "users"}` : state.view;
if (location.hash !== `#${viewHash}`) history.replaceState(null, "", `${location.pathname}${location.search}#${viewHash}`);
if (location.hash !== `#${viewHash}`) history.pushState(null, "", `${location.pathname}${location.search}#${viewHash}`);
$("#hosted-count").textContent = state.sites.length; $("#proxy-count").textContent = state.proxies.length; $("#streaming-count").textContent = state.streams.length; $("#redirect-count").textContent = state.redirects.length; $("#access-count").textContent = state.accessLists.length; $("#certificate-count").textContent = state.certificates?.summary.total || 0;
document.querySelectorAll("nav [data-view], .aside-utilities [data-view]").forEach(button => button.classList.toggle("nav-active", button.dataset.view === state.view));
const overview = state.view === "overview";
@@ -305,7 +306,7 @@ async function refreshPendingProxies(ids = []) {
}
}
async function refreshDashboard() {
const button = $("#refresh-health"); button.disabled = true; button.classList.add("spinning"); $("#health-checked").textContent = "Checking services…";
const button = $("#refresh-health"); button.disabled = true; button.classList.add("spinning"); $("#health-checked").innerHTML = '<span class="live-dot checking"></span>Checking services…';
try { state.dashboard = await api("/api/dashboard"); renderDashboard(); }
finally { button.disabled = false; button.classList.remove("spinning"); }
}
+12 -12
View File
@@ -74,25 +74,25 @@
</header>
<section id="dashboard-view" class="dashboard-view" aria-label="Gateway dashboard">
<div class="metric-grid">
<button class="metric-card accent-green" data-target="hosted"><span class="metric-label">Hosted sites</span><strong id="dash-hosted-total">0</strong><span id="dash-hosted-detail">None configured</span></button>
<button class="metric-card accent-blue" data-target="proxies"><span class="metric-label">Proxy hosts</span><strong id="dash-proxy-total">0</strong><span id="dash-proxy-detail">None configured</span></button>
<button class="metric-card accent-blue" data-target="certificates"><span class="metric-label">Certificates</span><strong id="dash-tls-total">0</strong><span id="dash-tls-detail">No TLS domains</span></button>
<button class="metric-card accent-green" data-target="hosted"><span class="metric-icon"></span><span class="metric-label">Hosted sites</span><strong id="dash-hosted-total">0</strong><span id="dash-hosted-detail">None configured</span></button>
<button class="metric-card accent-blue" data-target="proxies"><span class="metric-icon"></span><span class="metric-label">Proxy hosts</span><strong id="dash-proxy-total">0</strong><span id="dash-proxy-detail">None configured</span></button>
<button class="metric-card accent-blue" data-target="certificates"><span class="metric-icon"></span><span class="metric-label">Certificates</span><strong id="dash-tls-total">0</strong><span id="dash-tls-detail">No TLS domains</span></button>
</div>
<div class="metric-strip">
<button class="metric-chip accent-amber" data-target="redirects"><span class="chip-icon"></span><span class="chip-copy"><span class="metric-label">Redirect hosts</span><strong id="dash-redirect-total">0</strong></span></button>
<button class="metric-chip accent-purple" data-target="streaming"><span class="chip-icon"></span><span class="chip-copy"><span class="metric-label">Streaming hosts</span><strong id="dash-stream-total">0</strong></span></button>
<button class="metric-chip accent-amber" data-target="redirects"><span class="chip-icon"></span><span class="chip-copy"><span class="metric-label">Redirect hosts</span><strong id="dash-redirect-total">0</strong></span></button>
<button class="metric-chip accent-purple" data-target="streaming"><span class="chip-icon"></span><span class="chip-copy"><span class="metric-label">Streaming hosts</span><strong id="dash-stream-total">0</strong></span></button>
<div class="metric-chip" id="dash-attention-chip"><span class="chip-icon" id="dash-attention-icon"></span><span class="chip-copy"><span class="metric-label">Needs attention</span><strong id="dash-attention-total">0</strong><small id="dash-attention-detail">No current issues</small></span></div>
</div>
<div class="dashboard-columns">
<section class="dashboard-panel">
<section class="dashboard-panel health-panel status-healthy" id="health-panel">
<div class="panel-heading"><div><p class="eyebrow">Live health</p><h2>Services</h2></div><div class="health-actions"><span id="overall-health" class="health-badge healthy">Healthy</span><button id="refresh-health" class="icon-button" aria-label="Refresh health checks" title="Refresh health checks"></button></div></div>
<div class="health-list">
<div><span id="gateway-health-dot" class="status-dot running"></span><span><strong>Gateway</strong><small id="gateway-health-copy">Configuration valid</small></span></div>
<div><span id="http-health-dot" class="status-dot running"></span><span><strong>HTTP · Port 80</strong><small id="http-health-copy">Ready and responding</small></span></div>
<div><span id="https-health-dot" class="status-dot inactive"></span><span><strong>HTTPS · Port 443</strong><small id="https-health-copy">Not configured</small></span></div>
<div><span id="storage-health-dot" class="status-dot running"></span><span><strong>Persistent storage</strong><small id="storage-health-copy">Data directory writable</small></span></div>
<div class="health-grid">
<div class="health-tile"><span id="gateway-health-dot" class="status-dot running"></span><span class="health-tile-copy"><strong>Gateway</strong><small id="gateway-health-copy">Configuration valid</small></span></div>
<div class="health-tile"><span id="http-health-dot" class="status-dot running"></span><span class="health-tile-copy"><strong>HTTP · Port 80</strong><small id="http-health-copy">Ready and responding</small></span></div>
<div class="health-tile"><span id="https-health-dot" class="status-dot inactive"></span><span class="health-tile-copy"><strong>HTTPS · Port 443</strong><small id="https-health-copy">Not configured</small></span></div>
<div class="health-tile"><span id="storage-health-dot" class="status-dot running"></span><span class="health-tile-copy"><strong>Persistent storage</strong><small id="storage-health-copy">Data directory writable</small></span></div>
</div>
<p id="health-checked" class="checked-time">Last checked —</p>
<p id="health-checked" class="checked-time"><span class="live-dot" id="health-live-dot"></span>Last checked —</p>
</section>
<section class="dashboard-panel">
<div class="panel-heading"><div><p class="eyebrow">Runtime</p><h2>System</h2></div></div>
File diff suppressed because one or more lines are too long