From 887cd2dfccf918648806967b7c2cdbb42477772d Mon Sep 17 00:00:00 2001 From: Marvin Wade Date: Mon, 21 Sep 2026 10:03:10 -0400 Subject: [PATCH] Distinguish disabled routes from monitoring-off routes in status text --- ROADMAP.md | 2 ++ package.json | 2 +- src/public/app.js | 6 +++--- src/public/features.js | 2 +- src/public/index.html | 4 ++-- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 4a0de67..7163885 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -270,3 +270,5 @@ Roughly in priority order: `v0.16.72` fixes the favicon not appearing in some Chromium-family browsers (reported: DuckDuckGo's browser showed the default globe icon while Safari showed the real one correctly). The app previously declared a single `` pointing straight at the 1018x1001, 396 KB source PNG with no `sizes` attribute and no `/favicon.ico` fallback -- Safari is forgiving about oversized, unsized favicons, but some Chromium-based browsers silently skip one that large rather than downscale it, and several also probe `/favicon.ico` directly regardless of what the `` tag says. Added properly sized `favicon.ico` (16/32px, multi-size), standalone `favicon-16.png`/`favicon-32.png` with `sizes` attributes, and a 180x180 `apple-touch-icon.png`, all generated from the existing approved icon artwork and served automatically by the existing static file handler -- no server route or icon design changes. `v0.16.73` makes the Dashboard's System panel top accent bar reflect resource state, instead of always showing green -- pointed out after a screenshot showed CPU pinned at 100% (its own stat correctly shown in red) while the panel's top bar stayed the hardcoded `var(--green)` it always had. `renderHeroPanel()` now tracks the worst tone across CPU/memory/swap/disk (the same "warning" at 75%+ / "critical" at 90%+ thresholds each stat's own value and fill bar already used) and applies a `tone-warning`/`tone-critical` class to the panel, which `.system-panel::before` now reads instead of a fixed color. Network, uptime, and throughput don't carry a tone and are excluded from the calculation, same as before. Applies to the Dashboard's System panel only -- the Administration > System tab's equivalent hero grid has no top accent bar to react. + +`v0.16.74` fixes an inaccurate status label on Hosted Site, Proxy Host, and Streaming Host cards, and the Certificates table's Upstream column: a route with monitoring intentionally turned off via "Monitor this site/upstream" in Advanced options -- while the route itself stays enabled and running -- read "Monitoring paused", the same text used for a route that's fully disabled. "Paused" implies a temporary interruption; deliberately unchecking the monitor box is an ongoing, intentional setting. The backend already distinguished the two cases (`status: "disabled"` when the route itself is off vs. `status: "unmonitored"` when only health checks are off, in `checkProxy()`), the frontend just collapsed them into one string in four places. Disabled routes keep "Monitoring paused"; a running route with health checks off now reads "Monitoring disabled". No backend or status-logic changes, text only. diff --git a/package.json b/package.json index 92cd17a..35b3929 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "site-gateway", - "version": "0.16.73", + "version": "0.16.74", "private": true, "description": "Site Gateway: simple self-hosted website publishing, reverse proxying, and automatic HTTPS.", "type": "module", diff --git a/src/public/app.js b/src/public/app.js index feaf168..3dc2636 100644 --- a/src/public/app.js +++ b/src/public/app.js @@ -247,14 +247,14 @@ function upstreamStateClass(enabled, upstream) { } function hostedCard(site) { const status = site.status === "running" ? "running" : site.status === "error" ? "error" : "disabled"; - const upstream = !site.enabled || site.upstream?.status === "unmonitored" ? "Monitoring paused" : !site.upstream || site.upstream.status === "pending" ? "Upstream check pending" : site.upstream.status === "healthy" ? `Upstream ${site.upstream.httpStatus} · ${site.upstream.responseMs} ms` : `Upstream unavailable · ${escapeHtml(site.upstream.error || "check failed")}`; + const upstream = !site.enabled ? "Monitoring paused" : site.upstream?.status === "unmonitored" ? "Monitoring disabled" : !site.upstream || site.upstream.status === "pending" ? "Upstream check pending" : site.upstream.status === "healthy" ? `Upstream ${site.upstream.httpStatus} · ${site.upstream.responseMs} ms` : `Upstream unavailable · ${escapeHtml(site.upstream.error || "check failed")}`; const menu = canManage() ? `` : ""; const toggle = canManage() ? `` : ""; return `
${iconMarkup(site)}
${menu}

${escapeHtml(site.name)}

${escapeHtml(site.domain || `Port ${site.port}`)}

${site.domain ? `

${escapeHtml(publicUrl(site))}

` : ""}

${upstream}

`; } function proxyCard(proxy) { const status = proxy.status === "running" ? "running" : proxy.status === "error" ? "error" : "disabled"; - const upstream = !proxy.enabled || proxy.upstream?.status === "unmonitored" ? "Monitoring paused" : !proxy.upstream || proxy.upstream.status === "pending" ? "Upstream check pending" : proxy.upstream.status === "healthy" ? `Upstream ${proxy.upstream.httpStatus} · ${proxy.upstream.responseMs} ms` : `Upstream unavailable · ${escapeHtml(proxy.upstream.error || "check failed")}`; + const upstream = !proxy.enabled ? "Monitoring paused" : proxy.upstream?.status === "unmonitored" ? "Monitoring disabled" : !proxy.upstream || proxy.upstream.status === "pending" ? "Upstream check pending" : proxy.upstream.status === "healthy" ? `Upstream ${proxy.upstream.httpStatus} · ${proxy.upstream.responseMs} ms` : `Upstream unavailable · ${escapeHtml(proxy.upstream.error || "check failed")}`; const menu = canManage() ? `` : ""; const toggle = canManage() ? `` : ""; const access = proxy.accessListId ? (state.accessLists.find(item => item.id === proxy.accessListId)?.name || "Access List") : "Public · no Access List"; @@ -277,7 +277,7 @@ function renderCertificates() { const tlsOk = item ? ["healthy", "warning", "critical", "not-configured"].includes(item.tls.status) : null; const dnsCell = item ? `${dnsOk ? "Resolved" : "Failed"}` : `—`; const tlsCell = item ? `${escapeHtml(item.tls.status.replaceAll("-", " "))}` : `—`; - const upstreamCell = !item ? `—` : !item.upstream || item.upstream.status === "unmonitored" ? `Monitoring paused` : item.upstream.status === "pending" ? `Check pending` : item.upstream.status === "healthy" ? `${item.upstream.httpStatus}` : `${escapeHtml(item.upstream.error || "Unavailable")}`; + const upstreamCell = !item ? `—` : !item.upstream ? `Monitoring paused` : item.upstream.status === "unmonitored" ? `Monitoring disabled` : item.upstream.status === "pending" ? `Check pending` : item.upstream.status === "healthy" ? `${item.upstream.httpStatus}` : `${escapeHtml(item.upstream.error || "Unavailable")}`; const statusLabel = cert.status === "mismatch" ? "Domain mismatch" : cert.status.charAt(0).toUpperCase() + cert.status.slice(1); return `${escapeHtml(cert.domain)}
${escapeHtml(cert.kind)} · ${escapeHtml(cert.source)}${escapeHtml(statusLabel)}${cert.expiresAt ? `${cert.daysRemaining} days` : "—"}${escapeHtml(cert.issuer || "—")}${dnsCell}${tlsCell}${upstreamCell}`; }).join("") : 'No HTTPS domains are configured.'; diff --git a/src/public/features.js b/src/public/features.js index 5ccd36d..b85afa1 100644 --- a/src/public/features.js +++ b/src/public/features.js @@ -22,7 +22,7 @@ function renderStreams() { empty.classList.toggle("hidden", !state.loaded || state.streams.length > 0); list.innerHTML = state.streams.map(item => { const status = item.status === "running" ? "running" : item.status === "error" ? "error" : "disabled"; - const upstream = item.enabled === false || item.upstream?.status === "unmonitored" ? "Monitoring paused" : !item.upstream || item.upstream.status === "pending" ? "Target check pending" : item.upstream.status === "healthy" ? `Target reachable · ${item.upstream.responseMs} ms` : `Target unreachable · ${extendedEscape(item.upstream.error || "check failed")}`; + const upstream = item.enabled === false ? "Monitoring paused" : item.upstream?.status === "unmonitored" ? "Monitoring disabled" : !item.upstream || item.upstream.status === "pending" ? "Target check pending" : item.upstream.status === "healthy" ? `Target reachable · ${item.upstream.responseMs} ms` : `Target unreachable · ${extendedEscape(item.upstream.error || "check failed")}`; const protocols = [item.tcp !== false ? "TCP" : null, item.udp ? "UDP" : null].filter(Boolean).map(value => `${value}`).join(""); const toggle = ``; return `
${featureIcon(item,"SH")}

${extendedEscape(item.name)}

Port ${item.port}

→ ${extendedEscape(item.target)}

${upstream}

`; diff --git a/src/public/index.html b/src/public/index.html index 34512fa..b915765 100644 --- a/src/public/index.html +++ b/src/public/index.html @@ -11,7 +11,7 @@ - + - +