This commit is contained in:
Mateusz Gruszczyński committed 2026-10-15 11:10:49 +02:00
1 parent b757b34a31
commit 5a36bb0c4b
20 files changed
+356 -186

No files matched your search

+1 -1
View File
@@ -2,7 +2,7 @@
"openapi": "3.0.3",
"info": {
"title": "RustProxy Administration API",
"version": "0.33.1",
"version": "0.33.2",
"description": "Administration API for the active panel origin. Sign in through the panel; the same-origin HttpOnly cookie authenticates all private operations. Mutations also require the exact Origin, X-RustProxy-Request: 1 and a session-bound X-CSRF-Token. Loopback HTTP is the default; Settings can enable HTTPS before using a non-loopback administration bind. This document contains no live configuration or credentials. Object schemas marked extensible are summaries: full configuration validation is authoritative in Rust."
},
"servers": [
+4 -1
View File
@@ -18,6 +18,7 @@ window.rustProxyBackendDashboard = (() => {
}
function syncVisibility(hiddenOverride = null) {
const prefs = window.rustProxyPreferences?.current?.();
// While unlocked, currentDraft is authoritative until Save or Cancel.
const hidden = new Set(hiddenOverride ?? window.rustProxyInlineLayout?.currentDraft?.()?.hidden
?? prefs?.dashboard_hidden_widgets ?? ADMIN_APPEARANCE_DEFAULTS.dashboard_hidden_widgets);
for (const id of ids) if (card(id)) card(id).hidden = hidden.has(id);
@@ -49,7 +50,8 @@ window.rustProxyBackendDashboard = (() => {
window.rustProxyExtraOverview?.meter?.("backend-dashboard-capacity", null);
reset();
}
// Counter deltas are matched to stable (pool, member) IDs, not aggregate
// totals: removed/rebuilt pools must never become negative or spike rates.
function memberCounters(members) {
const values = new Map();
for (const { pool, member } of members) {
@@ -71,6 +73,7 @@ window.rustProxyBackendDashboard = (() => {
for (const [id, next] of current) {
const old = earlier.get(id);
if (!old) continue;
// A generation reset on a member invalidates its entire interval.
if (tracked.some(field => next[field] < old[field])) continue;
for (const field of tracked) total[field] += next[field] - old[field];
}
+3 -1
View File
@@ -1,4 +1,5 @@
// Opt-in compact metrics and bounded visualizations. All readings reuse the
// existing Overview live-status payload (no additional requests or timers).
window.rustProxyExtraOverview = (() => {
const limit = () => window.rustProxyOverviewSamples?.getLimit?.() ?? 60;
const names = [
@@ -150,6 +151,7 @@ window.rustProxyExtraOverview = (() => {
for (const id of names) {
const tile = node(id);
if (!tile) { lastCounter.delete(id); continue; }
// Visibility is only a presentation concern; retain live history.
const metric = read(id, status, now, record);
const value = metric.value;
if (lastUnits.get(id) !== metric.unit) {
+1 -1
View File
@@ -85,7 +85,7 @@ window.rustProxyWideOverview = (() => {
for (const [id, config] of Object.entries(configurations)) {
const node = tile(id);
if (!node) { baselines.delete(id); continue; }
// Keep collecting while hidden so restoring a widget does not restart rates.
const values = config.counters ? counterRates(id, config.counters(status), now, record)
: config.values(status).map(finite);
const series = history.get(id) || config.names.map(() => []);
+14 -2
View File
@@ -319,8 +319,20 @@ function renderDiagnostics(status, appendSample = true, systemSample = appendSam
$("system-source").hidden = !sourceSupported; if (sourceSupported) $("system-source").textContent = t("realSample");
const systemMemory = knownPair(d.memory_used_bytes, d.memory_total_bytes, value => fmtBytes(value));
setOptionalMetric("system-memory", systemMemory != null, systemMemory);
$("memory-meter").hidden = systemMemory == null;
if (d.memory_used_bytes != null && d.memory_total_bytes > 0) $("memory-meter").value = d.memory_used_bytes / d.memory_total_bytes * 100; else $("memory-meter").removeAttribute("value");
const ramMeter = $("memory-meter"), ramFill = $("memory-meter-fill");
ramMeter.hidden = systemMemory == null;
const ramUsed = Number(d.memory_used_bytes), ramTotal = Number(d.memory_total_bytes);
const ramPercent = d.memory_used_bytes != null && d.memory_total_bytes != null &&
Number.isFinite(ramUsed) && Number.isFinite(ramTotal) && ramTotal > 0
? Math.max(0, Math.min(100, ramUsed / ramTotal * 100)) : null;
ramFill.style.width = `${ramPercent ?? 0}%`;
if (ramPercent == null) {
ramMeter.removeAttribute("aria-valuenow");
ramMeter.setAttribute("aria-valuetext", "-");
} else {
ramMeter.setAttribute("aria-valuenow", String(ramPercent));
ramMeter.setAttribute("aria-valuetext", `${fmt(ramPercent, 1)}%`);
}
const systemSwap = knownPair(d.system_swap_used_bytes, d.system_swap_total_bytes, value => fmtBytes(value));
setOptionalMetric("system-swap", systemSwap != null, systemSwap);
const loadSupported = Array.isArray(d.load_average) && d.load_average.length > 0;
+1
View File
@@ -16,6 +16,7 @@ window.rustProxyStreamDashboard = (() => {
}
function syncVisibility(hiddenOverride = null) {
const prefs = window.rustProxyPreferences?.current?.();
// While unlocked, currentDraft is authoritative until Save or Cancel.
const hidden = new Set(hiddenOverride ?? window.rustProxyInlineLayout?.currentDraft?.()?.hidden
?? prefs?.dashboard_hidden_widgets ?? ADMIN_APPEARANCE_DEFAULTS.dashboard_hidden_widgets);
for (const id of ids) if (card(id)) card(id).hidden = hidden.has(id);
+36 -130
View File
@@ -1,137 +1,43 @@
{
"dashboard_widgets": [
"requests",
"active",
"bandwidth",
"errors",
"memory",
"websocket",
"backend_traffic",
"extra_response_latency",
"http_protocol",
"tls",
"websocket_protocol",
"backend_latency",
"stream_overview",
"stream_traffic",
"stream_sessions",
"stream_backend_health",
"backend_health",
"backend_activity",
"backend_errors",
"backend_retries",
"http2",
"backend_protocols",
"backend_requests",
"backend_capacity",
"backend_http5xx",
"backend_timeouts",
"backend_network",
"http3",
"backend_queue",
"extra_open_connections",
"extra_process_cpu",
"extra_process_rss",
"extra_process_fds",
"backend_rotations",
"extra_websocket_active",
"extra_tls_error_rate",
"extra_request_error_rate",
"extra_cache_hit_rate",
"extra_backend_active",
"extra_h2_share",
"extra_cache_memory",
"extra_backend_unhealthy",
"extra_total_rps",
"extra_active_exchanges",
"extra_h1_share",
"extra_h3_share",
"extra_cache_miss_rate",
"extra_cold_storage",
"wide_protocol_rates",
"wide_cpu",
"wide_memory",
"wide_cache_activity",
"wide_connections",
"wide_backend_health",
"wide_latency",
"cache",
"traffic_chart",
"system_chart",
"connection_chart",
"wide_transform_activity",
"wide_compression_activity",
"cache_diagnostics",
"extra_cold_queue_memory",
"process_diagnostics",
"network_diagnostics",
"backend_table",
"www_status",
"www_kind",
"www_capacity",
"www_methods",
"www_error_rate",
"www_php_requests",
"www_requests",
"www_latency",
"www_failures",
"www_traffic",
"www_top_services",
"www_status_trend"
"requests", "active", "bandwidth", "errors", "memory", "websocket",
"backend_traffic", "extra_response_latency", "http_protocol", "tls",
"websocket_protocol", "backend_latency", "stream_overview", "stream_traffic",
"stream_sessions", "stream_backend_health", "backend_health", "backend_activity",
"backend_errors", "backend_retries", "http2", "backend_protocols",
"backend_requests", "backend_capacity", "backend_http5xx", "backend_timeouts",
"backend_network", "http3", "backend_queue", "extra_open_connections",
"extra_process_cpu", "extra_process_rss", "extra_process_fds", "backend_rotations",
"extra_websocket_active", "extra_tls_error_rate", "extra_request_error_rate",
"extra_cache_hit_rate", "extra_backend_active", "extra_h2_share",
"extra_cache_memory", "extra_backend_unhealthy", "extra_total_rps",
"extra_active_exchanges", "extra_h1_share", "extra_h3_share",
"extra_cache_miss_rate", "extra_cold_storage", "wide_protocol_rates",
"wide_cpu", "wide_memory", "wide_cache_activity", "wide_connections",
"wide_backend_health", "wide_latency", "cache", "traffic_chart",
"system_chart", "connection_chart", "wide_transform_activity",
"wide_compression_activity", "cache_diagnostics", "extra_cold_queue_memory",
"process_diagnostics", "network_diagnostics", "backend_table",
"www_status", "www_kind", "www_capacity", "www_methods", "www_error_rate",
"www_php_requests", "www_requests", "www_latency", "www_failures",
"www_traffic", "www_top_services", "www_status_trend"
],
"dashboard_hidden_widgets": [
"stream_overview",
"stream_backend_health",
"tls",
"stream_sessions",
"backend_health",
"backend_activity",
"backend_errors",
"backend_protocols",
"backend_requests",
"backend_timeouts",
"backend_network",
"extra_process_cpu",
"extra_process_rss",
"extra_process_fds",
"extra_websocket_active",
"extra_tls_error_rate",
"extra_request_error_rate",
"extra_h2_share",
"extra_backend_unhealthy",
"extra_total_rps",
"extra_h1_share",
"extra_h3_share",
"wide_memory",
"wide_connections",
"wide_backend_health",
"wide_protocol_rates",
"extra_active_exchanges",
"extra_cache_miss_rate",
"extra_cache_hit_rate",
"backend_capacity",
"stream_traffic",
"backend_http5xx",
"bandwidth",
"backend_queue",
"wide_cpu",
"wide_latency",
"www_status",
"www_kind",
"www_capacity",
"www_methods",
"www_error_rate",
"www_php_requests",
"www_requests",
"www_latency",
"www_failures",
"www_traffic",
"www_top_services",
"www_status_trend",
"extra_cold_queue_memory",
"wide_cache_activity",
"backend_rotations"
"stream_overview", "stream_backend_health", "tls", "stream_sessions",
"backend_health", "backend_activity", "backend_errors", "backend_protocols",
"backend_requests", "backend_timeouts", "backend_network", "extra_process_cpu",
"extra_process_rss", "extra_process_fds", "extra_websocket_active",
"extra_tls_error_rate", "extra_request_error_rate", "extra_h2_share",
"extra_backend_unhealthy", "extra_total_rps", "extra_h1_share",
"extra_h3_share", "wide_memory", "wide_connections", "wide_backend_health",
"wide_protocol_rates", "extra_active_exchanges", "extra_cache_miss_rate",
"extra_cache_hit_rate", "backend_capacity", "stream_traffic", "backend_http5xx",
"bandwidth", "backend_queue", "wide_cpu", "wide_latency", "www_status",
"www_kind", "www_capacity", "www_methods", "www_error_rate",
"www_php_requests", "www_requests", "www_latency", "www_failures",
"www_traffic", "www_top_services", "www_status_trend",
"extra_cold_queue_memory", "wide_cache_activity", "backend_rotations"
],
"dashboard_density": "compact",
"overview_samples": 600
}
}
+6 -2
View File
@@ -1101,8 +1101,12 @@
<polyline id="system-sparkline" points="0,95 300,95"></polyline>
</svg>
<div class="memory-line"><span data-i18n="systemMemory">System RAM</span><b
id="system-memory">-</b></div><progress aria-label="System RAM"
data-i18n-aria="systemMemory" id="memory-meter" max="100" value="0"></progress>
id="system-memory">-</b></div>
<div class="overview-meter system-memory-meter" id="memory-meter" role="progressbar"
aria-label="System RAM" data-i18n-aria="systemMemory" aria-valuemin="0"
aria-valuemax="100" aria-valuetext="-">
<span class="overview-meter-fill" id="memory-meter-fill"></span>
</div>
<div class="memory-line"><span data-i18n="systemSwap">System swap</span><b
id="system-swap">-</b></div>
<div class="chart-footer"><span data-i18n="loadAverage">Load 1 / 5 / 15 min</span><span
+39 -20
View File
@@ -69,6 +69,9 @@
--chart-grid-opacity: .28;
--chart-grid-dash: none;
--chart-svg-filter: none;
--rp-progress-fill: var(--chart-accent);
--rp-progress-track: var(--line);
--rp-progress-radius: 3px;
--traffic-h1: var(--chart-accent);
--traffic-h2: var(--chart-secondary);
--traffic-h3: var(--chart-tertiary);
@@ -132,9 +135,6 @@ html[data-theme=light] {
--scrollbar-thumb-hover: #879684
}
/* Optional AMOLED palette: near-black surfaces, readable borders and restrained
mint/coral emphasis. Does not change the established dark appearance. */
html[data-theme=ultra_dark] {
color-scheme: dark;
--bg: #030404;
@@ -194,8 +194,6 @@ html[data-theme=ultra_dark] {
--scrollbar-thumb-hover: #617373;
}
/* Warm, muted ivory. No pure-white canvas; numeric values and charts retain
clear contrast against their slightly lighter, neutral card surfaces. */
html[data-theme=cream] {
color-scheme: light;
--bg: #f2ede3;
@@ -1686,21 +1684,25 @@ progress {
border: 0;
appearance: none;
-webkit-appearance: none;
background: var(--line);
accent-color: var(--green);
background: var(--rp-progress-track);
accent-color: var(--rp-progress-fill);
border-radius: var(--rp-progress-radius);
overflow: hidden
}
progress::-webkit-progress-bar {
background: var(--line)
background: var(--rp-progress-track);
border-radius: var(--rp-progress-radius)
}
progress::-webkit-progress-value {
background: var(--green)
background: var(--rp-progress-fill);
border-radius: var(--rp-progress-radius)
}
progress::-moz-progress-bar {
background: var(--green)
background: var(--rp-progress-fill);
border-radius: var(--rp-progress-radius)
}
.diagnostics-grid {
@@ -4465,7 +4467,7 @@ dialog .window-heading {
.traffic-bar-track {
height: 10px;
border-radius: 999px;
border-radius: var(--rp-progress-radius);
background: var(--chart-surface);
box-shadow: inset 0 0 0 1px var(--line);
overflow: hidden
@@ -4476,7 +4478,7 @@ dialog .window-heading {
height: 100%;
min-width: 2px;
border-radius: inherit;
background: var(--chart-accent)
background: var(--rp-progress-fill)
}
.traffic-bar-value {
@@ -11174,21 +11176,25 @@ html[data-dashboard-density=ultra] #panel-overview .dashboard-overview-grid>.car
html[data-corner-style=square] {
--rp-radius-surface: 0px;
--rp-radius-control: 0px;
--rp-progress-radius: 0px;
}
html[data-corner-style=subtle] {
--rp-radius-surface: 5px;
--rp-radius-control: 4px;
--rp-progress-radius: 2px;
}
html[data-corner-style=rounded] {
--rp-radius-surface: 12px;
--rp-radius-control: 8px;
--rp-progress-radius: 4px;
}
html[data-corner-style=extra] {
--rp-radius-surface: 18px;
--rp-radius-control: 12px;
--rp-progress-radius: 999px;
}
html:is([data-corner-style=square], [data-corner-style=subtle], [data-corner-style=rounded], [data-corner-style=extra]) :is(.card, .stat, dialog, .login-card, .appearance-section, .settings-tabs, .dashboard-widget-setting, .overview-layout-tile, .overview-layout-hidden-item, .overview-inline-hidden-item, .overview-inline-hidden, .notice, .toast, .chart-style-choice, .stream-dashboard-card, .pool-card, .vhost-card, .vh-backend-card, .entity, .settings-filter-row, .stream-backend-row, .file-log-advanced-parser, .table-container, .metric-info-popover) {
@@ -11244,8 +11250,8 @@ html:is([data-corner-style=square], [data-corner-style=subtle], [data-corner-sty
height: 10px;
margin: 9px 0 8px;
overflow: hidden;
border-radius: var(--rp-radius-control, 5px);
background: var(--line);
border-radius: var(--rp-progress-radius);
background: var(--rp-progress-track);
}
#panel-overview .overview-meter-fill {
@@ -11253,10 +11259,22 @@ html:is([data-corner-style=square], [data-corner-style=subtle], [data-corner-sty
width: 0;
height: 100%;
border-radius: inherit;
background: var(--chart-accent);
background: var(--rp-progress-fill);
transition: width 180ms ease;
}
/* System Load uses the same track/fill component as other Overview meters,
while retaining its compact 5px height and existing vertical spacing. */
#panel-overview .system-card .system-memory-meter {
display: block;
width: 100%;
height: 5px;
margin: 6px 0 9px;
overflow: hidden;
background: var(--rp-progress-track);
border-radius: var(--rp-progress-radius);
}
#panel-overview .stat-meter>small {
display: block;
overflow-wrap: anywhere;
@@ -11488,8 +11506,8 @@ html:is([data-corner-style=square], [data-corner-style=subtle], [data-corner-sty
width: 100%;
height: 10px;
margin: 9px 0 8px;
background: var(--line);
border-radius: var(--rp-radius-control, 5px);
background: var(--rp-progress-track);
border-radius: var(--rp-progress-radius);
overflow: hidden;
}
@@ -11546,14 +11564,15 @@ html:is([data-corner-style=square], [data-corner-style=subtle], [data-corner-sty
#panel-overview .www-top-meter {
height: 7px;
background: var(--line);
border-radius: 4px;
background: var(--rp-progress-track);
border-radius: var(--rp-progress-radius);
overflow: hidden;
}
#panel-overview .www-top-meter span {
display: block;
background: var(--chart-accent);
background: var(--rp-progress-fill);
border-radius: inherit;
height: 100%;
}