v0.34.10
This commit is contained in:
1 parent
66ff9dfaa2
commit
ee54aa6875
30 files changed
+939
-68
No files matched your search
@@ -7,6 +7,8 @@ function verifyAdminModules() {
|
||||
["rustProxyTrafficPanel", "refresh"],
|
||||
["rustProxyPreferences", "load"],
|
||||
["rustProxyRefresh", "start"],
|
||||
["rustProxyPermissions", "render"],
|
||||
["rustProxyCertificateInspection", "read"],
|
||||
];
|
||||
const missing = required.find(([module, method]) => typeof window[module]?.[method] !== "function");
|
||||
if (missing) throw new Error(`Panel resources incomplete (${missing[0]}). Reload to retry.`);
|
||||
@@ -34,6 +36,12 @@ else document.addEventListener("DOMContentLoaded", startAdminPanel, { once: true
|
||||
function certificateUsesExternalFiles(cert) {
|
||||
return String(cert?.cert_file || "").startsWith("/") && String(cert?.key_file || "").startsWith("/");
|
||||
}
|
||||
function syncCertificatePermissions() {
|
||||
window.rustProxyPermissions?.render($("certificate-external-acl-example"), {
|
||||
kind: "certificate", paths: [$("certificate-file").value, $("certificate-key").value],
|
||||
enabled: $("certificate-file-source").value === "external",
|
||||
});
|
||||
}
|
||||
function syncCertificateFileSource() {
|
||||
const external = $("certificate-file-source").value === "external";
|
||||
const cert = $("certificate-file"), key = $("certificate-key");
|
||||
@@ -47,9 +55,14 @@ function syncCertificateFileSource() {
|
||||
const helpKey = external ? "certificateSourceExternalHelp" : "certificateSourceManagedHelp";
|
||||
$("certificate-file-source-help").dataset.i18n = helpKey;
|
||||
$("certificate-file-source-help").textContent = t(helpKey);
|
||||
$("certificate-external-acl-example").hidden = !external;
|
||||
syncCertificatePermissions();
|
||||
window.rustProxyCertificateInspection?.refresh();
|
||||
}
|
||||
$("certificate-file-source").addEventListener("change", syncCertificateFileSource);
|
||||
for (const id of ["certificate-file", "certificate-key"]) {
|
||||
$(id).addEventListener("input", syncCertificatePermissions);
|
||||
$(id).addEventListener("change", syncCertificatePermissions);
|
||||
}
|
||||
function renderCertificates() {
|
||||
if (!envelope) return;
|
||||
const settings = envelope.config.tls || {};
|
||||
@@ -148,7 +161,15 @@ function renderCertificates() {
|
||||
if (result.administration_restart_required) toast(t("certificateAdminRestartRequired"), true);
|
||||
} catch (error) { toast(error.message, true); } finally { busy = false; }
|
||||
}));
|
||||
buttons.append(action(t("edit"), () => { window.rustProxyCertificates?.open("existing"); $("certificate-advanced").open = true; $("certificate-id").value = cert.id; $("certificate-hosts").value = cert.hosts.join(", "); $("certificate-file-source").value = certificateUsesExternalFiles(cert) ? "external" : "managed"; syncCertificateFileSource(); $("certificate-file").value = cert.cert_file; $("certificate-key").value = cert.key_file; $("certificate-default").checked = settings.default_certificate === cert.id; }));
|
||||
buttons.append(action(t("edit"), () => {
|
||||
window.rustProxyCertificates?.open("existing"); $("certificate-advanced").open = true;
|
||||
$("certificate-id").value = cert.id; $("certificate-hosts").value = (cert.hosts || []).join(", ");
|
||||
$("certificate-file-source").value = certificateUsesExternalFiles(cert) ? "external" : "managed";
|
||||
$("certificate-file").value = cert.cert_file; $("certificate-key").value = cert.key_file;
|
||||
$("certificate-default").checked = settings.default_certificate === cert.id;
|
||||
window.rustProxyCertificateInspection?.reset({ preserveNames: true });
|
||||
syncCertificateFileSource(); window.rustProxyCertificateInspection?.schedule();
|
||||
}));
|
||||
}
|
||||
const remove = action(t("remove"), async () => { if (inUse) { toast(t("certInUse"), true); return; } if (!(await appConfirm(t("confirmDelete"), { danger: true }))) return; const next = structuredClone(envelope.config); next.tls.certificates = next.tls.certificates.filter(v => v.id !== cert.id); await saveSection("tls", next.tls); });
|
||||
remove.disabled = inUse; remove.title = inUse ? t("certInUse") : ""; buttons.append(remove);
|
||||
@@ -157,8 +178,38 @@ function renderCertificates() {
|
||||
});
|
||||
$("certificate-list").replaceChildren(...entries);
|
||||
if (!entries.length) $("certificate-list").append(el("p", t("emptyCertificates"), "empty"));
|
||||
syncCertificatePermissions();
|
||||
}
|
||||
$("certificate-form").addEventListener("submit", async event => { event.preventDefault(); const form = event.currentTarget; if (!envelope || busy) return; try { await withFormSubmitState(form, event.submitter, async () => { const next = structuredClone(envelope.config); next.tls = next.tls || {}; next.tls.certificates = next.tls.certificates || []; const cert = { id: $("certificate-id").value.trim(), hosts: $("certificate-hosts").value.split(",").map(v => v.trim()).filter(Boolean), cert_file: $("certificate-file").value.trim(), key_file: $("certificate-key").value.trim() }; const index = next.tls.certificates.findIndex(v => v.id === cert.id); if (index >= 0 && next.tls.certificates[index].issuer) throw fieldError($("certificate-id"), t("certManagedReadOnly")); if (index < 0) next.tls.certificates.push(cert); else { const old = next.tls.certificates[index]; const sameFiles = old.cert_file === cert.cert_file && old.key_file === cert.key_file; next.tls.certificates[index] = { ...old, ...cert, covered_hosts: sameFiles ? (old.covered_hosts || []) : [], issuer: sameFiles ? old.issuer : null }; } if ($("certificate-default").checked) next.tls.default_certificate = cert.id; else if (next.tls.default_certificate === cert.id) delete next.tls.default_certificate; if (await saveSection("tls", next.tls)) { form.reset(); syncCertificateFileSource(); window.rustProxyCertificates?.close(); } }); } catch (error) { toast(error.message, true); } });
|
||||
$("certificate-form").addEventListener("submit", async event => {
|
||||
event.preventDefault(); const form = event.currentTarget;
|
||||
if (!envelope || busy) return;
|
||||
try {
|
||||
await withFormSubmitState(form, event.submitter, async () => {
|
||||
const epoch = authEpoch;
|
||||
if (await window.rustProxyCertificateInspection?.beforeSave() === false) return;
|
||||
if (!authenticated || epoch !== authEpoch || !envelope || busy) return;
|
||||
const next = structuredClone(envelope.config);
|
||||
next.tls ||= {}; next.tls.certificates ||= [];
|
||||
const cert = {
|
||||
id: $("certificate-id").value.trim(),
|
||||
hosts: $("certificate-hosts").value.split(",").map(v => v.trim()).filter(Boolean),
|
||||
cert_file: $("certificate-file").value.trim(), key_file: $("certificate-key").value.trim(),
|
||||
};
|
||||
const index = next.tls.certificates.findIndex(v => v.id === cert.id);
|
||||
if (index >= 0 && next.tls.certificates[index].issuer) throw fieldError($("certificate-id"), t("certManagedReadOnly"));
|
||||
if (index < 0) next.tls.certificates.push(cert);
|
||||
else {
|
||||
const old = next.tls.certificates[index], sameFiles = old.cert_file === cert.cert_file && old.key_file === cert.key_file;
|
||||
next.tls.certificates[index] = { ...old, ...cert, covered_hosts: sameFiles ? (old.covered_hosts || []) : [], issuer: sameFiles ? old.issuer : null };
|
||||
}
|
||||
if ($("certificate-default").checked) next.tls.default_certificate = cert.id;
|
||||
else if (next.tls.default_certificate === cert.id) delete next.tls.default_certificate;
|
||||
if (await saveSection("tls", next.tls)) {
|
||||
form.reset(); syncCertificateFileSource(); window.rustProxyCertificates?.close();
|
||||
}
|
||||
});
|
||||
} catch (error) { toast(error.message, true); }
|
||||
});
|
||||
$("reload-certificates").addEventListener("click", async () => {
|
||||
if (!envelope || busy) return;
|
||||
try {
|
||||
@@ -331,8 +382,6 @@ function renderNetworkInterfaceCharts(interfaces, appendSample) {
|
||||
}
|
||||
}
|
||||
|
||||
// Cumulative process counters, not rate-sampler deltas. Never invent OTHER
|
||||
// or force a 100% mix if independently read totals are inconsistent.
|
||||
function cacheOutcomeMix(totals) {
|
||||
if (!Array.isArray(totals) || totals.length !== 4 ||
|
||||
totals.some(value => !Number.isSafeInteger(value) || value < 0)) return null;
|
||||
|
||||
Reference in new issue
Block a user