v0.32.6_official

This commit is contained in:
Mateusz Gruszczyński committed 2026-10-14 13:04:06 +02:00
1 parent fc8711d4d8
commit d2c6cf59a9
16 files changed
+1801 -1027

No files matched your search

+11 -16
View File
@@ -1,4 +1,3 @@
function verifyAdminModules() {
const required = [
["rustProxyIntegrations", "bindings"],
@@ -12,7 +11,6 @@ function verifyAdminModules() {
const missing = required.find(([module, method]) => typeof window[module]?.[method] !== "function");
if (missing) throw new Error(`Panel resources incomplete (${missing[0]}). Reload to retry.`);
}
async function startAdminPanel() {
try {
verifyAdminModules();
@@ -33,7 +31,6 @@ async function startAdminPanel() {
}
if (document.readyState === "complete") startAdminPanel();
else document.addEventListener("DOMContentLoaded", startAdminPanel, { once: true });
function certificateUsesExternalFiles(cert) {
return String(cert?.cert_file || "").startsWith("/") && String(cert?.key_file || "").startsWith("/");
}
@@ -53,7 +50,6 @@ function syncCertificateFileSource() {
$("certificate-external-acl-example").hidden = !external;
}
$("certificate-file-source").addEventListener("change", syncCertificateFileSource);
function renderCertificates() {
if (!envelope) return;
const settings = envelope.config.tls || {};
@@ -69,7 +65,6 @@ function renderCertificates() {
if (settings.default_certificate === cert.id) used.push(t("defaultLabel"));
const managed = !!cert.issuer, inUse = used.length > 0;
const box = el("div", undefined, "entity certificate-card");
const head = el("div", undefined, "certificate-card-head");
const identity = el("div", undefined, "certificate-card-identity");
identity.append(el("span", cert.id, "entity-title"));
@@ -152,7 +147,7 @@ $("reload-certificates").addEventListener("click", async () => {
$("reset-certificate").addEventListener("click", () => { $("certificate-form").reset(); $("certificate-file-source").value = "managed"; syncCertificateFileSource(); });
syncCertificateFileSource();
let systemSamples = [], connectionOpenSamples = [], connectionExchangeSamples = [], cacheMemorySamples = [], cacheTransientSamples = [], cacheFillSamples = [], cacheColdQueueSamples = [], cacheColdIoSamples = [], cacheColdStorageSamples = [], cacheHitSamples = [], cacheMissSamples = [], cacheBypassSamples = [], lastCacheCounters = null,
let systemSamples = [], connectionOpenSamples = [], connectionExchangeSamples = [], cacheMemorySamples = [], cacheTransientSamples = [], cacheFillSamples = [], cacheColdQueueSamples = [], cacheColdIoSamples = [], cacheColdStorageSamples = [], cacheHitSamples = [], cacheMissSamples = [], cacheBypassSamples = [], lastCacheCounters = null, lastCacheOutcomeRates = null,
processCpuSamples = [], processRssSamples = [], networkRxSamples = [], networkTxSamples = [], responseLatencySamples = [], networkInterfaceSamples = new Map(), settingsDirty = new Set();
const hiddenNetworkInterfaces = (() => {
try {
@@ -206,8 +201,7 @@ function svgNode(tag, attrs = {}) {
for (const [name, value] of Object.entries(attrs)) node.setAttribute(name, value);
return node;
}
// Diagnostic charts keep their SVG and label nodes across samples. Updating the
// existing Text node avoids remove/insert mutations while the user is scrolling.
function setChartText(node, value) {
const text = String(value);
if (node.firstChild?.nodeType === 3 && node.firstChild === node.lastChild) {
@@ -216,8 +210,7 @@ function setChartText(node, value) {
node.textContent = text;
}
}
// Preserve stable Overview DOM while live samples arrive: replacing network SVGs
// during an iOS WebKit momentum scroll can invalidate the current scroll anchor.
let latestNetworkInterfaceSnapshot = [];
function renderNetworkInterfaceCharts(interfaces, appendSample) {
const current = Array.isArray(interfaces) ? interfaces.slice().sort((a, b) => String(a.name || "").localeCompare(String(b.name || ""))) : [];
@@ -305,8 +298,7 @@ function renderNetworkInterfaceCharts(interfaces, appendSample) {
children.push(empty);
}
}
// The common live-sample path updates only text and SVG path attributes.
// Reparent rows solely when interfaces or visibility have actually changed.
if (children.length !== target.children.length || children.some((node, index) => node !== target.children[index])) {
target.replaceChildren(...children);
}
@@ -316,7 +308,6 @@ function renderDiagnostics(status, appendSample = true, systemSample = appendSam
$("metric-h3").textContent = fmt(m.http3_requests);
$("metric-cache-ratio").textContent = c.hit_ratio == null ? "-" : `${fmt(c.hit_ratio * 100, 1)}%`;
$("cache-summary").textContent = `${fmt(c.hits)} HIT / ${fmt(c.misses)} MISS`;
// Slow details vs cached live chart telemetry.
if (systemSample) {
if (d.process_rss_bytes != null) $("metric-rss").textContent = fmtBytes(d.process_rss_bytes);
$("process-summary").hidden = d.process_threads == null;
@@ -358,9 +349,13 @@ function renderDiagnostics(status, appendSample = true, systemSample = appendSam
const currentCounters = [c.hits, c.misses, c.bypasses].map(Number);
if (currentCounters.every(Number.isFinite)) {
const delta = rustProxyRateSampling.difference(lastCacheCounters, currentCounters, performance.now(), rustProxyRateSampling.cadence("overview_seconds"));
if (delta.deltas || !lastCacheCounters) { const values = delta.deltas || [0, 0, 0];
cacheHitSamples.push(values[0]); cacheMissSamples.push(values[1]); cacheBypassSamples.push(values[2]); cacheHitSamples = cacheHitSamples.slice(-60); cacheMissSamples = cacheMissSamples.slice(-60); cacheBypassSamples = cacheBypassSamples.slice(-60);
} lastCacheCounters = delta.next;
const values = delta.deltas || lastCacheOutcomeRates || (!lastCacheCounters ? [0, 0, 0] : null);
if (values) {
cacheHitSamples.push(values[0]); cacheMissSamples.push(values[1]); cacheBypassSamples.push(values[2]);
cacheHitSamples = cacheHitSamples.slice(-60); cacheMissSamples = cacheMissSamples.slice(-60); cacheBypassSamples = cacheBypassSamples.slice(-60);
lastCacheOutcomeRates = [...values];
}
lastCacheCounters = delta.next;
}
}
if (appendSample) {