This commit is contained in:
Mateusz Gruszczyński committed 2026-11-08 09:46:32 +01:00
1 parent 34490fd5fc
commit 6ef01212ec
21 files changed
+204 -77

No files matched your search

+53
View File
@@ -727,3 +727,56 @@ const rustProxyAutoId = (() => {
return { slugify, unique, attach };
})();
window.rustProxyAutoId = rustProxyAutoId;
// Automatic certificate choice: for a new entry, pick the stored certificate that covers every host name (exact names beat wildcards); the user can still choose another one.
const rustProxyAutoCertificate = (() => {
const states = new WeakMap();
const covers = (pattern, host) => {
pattern = String(pattern).toLowerCase(); host = String(host).toLowerCase();
if (pattern === host) return true;
if (host.startsWith("*.") || !pattern.startsWith("*.")) return false;
const suffix = pattern.slice(1);
return host.length > suffix.length && host.endsWith(suffix) && !host.slice(0, -suffix.length).includes(".");
};
function suggest(hosts, certificates) {
const names = [...new Set((hosts || []).map(host => String(host).trim().toLowerCase()).filter(Boolean))];
if (!names.length) return "";
let best = "", bestScore = -Infinity;
for (const cert of certificates || []) {
const coverage = certificateInventory.get(cert.id)?.covered_hosts?.length ? certificateInventory.get(cert.id).covered_hosts : (cert.covered_hosts || []);
let score = 0, all = true;
for (const host of names) {
if (coverage.some(pattern => String(pattern).toLowerCase() === host)) score += 2;
else if (coverage.some(pattern => covers(pattern, host))) score += 1;
else { all = false; break; }
}
if (!all) continue;
score -= coverage.length / 1000;
if (score > bestScore) { best = cert.id; bestScore = score; }
}
return best;
}
// options: { hosts: () => host names, certificates: () => certificate objects, auto: boolean, anchor: element after which the hint is placed }
function attach(select, options) {
let state = states.get(select);
if (!state) {
state = { hint: document.createElement("small") };
state.hint.className = "fine auto-cert-hint"; state.hint.hidden = true;
(options.anchor || select).after(state.hint);
select.addEventListener("change", event => { if (!event.isTrusted) return; const current = states.get(select); current.auto = false; current.hint.hidden = true; });
states.set(select, state);
}
state.options = options; state.auto = options.auto !== false && !select.value; state.hint.hidden = true;
const refresh = hosts => {
const current = states.get(select);
if (!current.auto) return;
const list = hosts || current.options.hosts();
const best = suggest(list, current.options.certificates());
if (select.value !== best) { select.value = best; select.dispatchEvent(new Event("change", { bubbles: true })); }
current.hint.hidden = !best; current.hint.textContent = best ? t("autoCertHint").replace("{id}", best) : "";
};
return { refresh };
}
return { attach, suggest, covers };
})();
window.rustProxyAutoCertificate = rustProxyAutoCertificate;