Files
railiance-platform/helm/openbao-ui-overlay/overlay.js
tegwick a6a87ae282 Fix OpenBao login overlay runaway DOM loop and slow loads
Replace the MutationObserver feedback loop with bounded, idempotent apply
retries so Firefox no longer hangs on the auth page. Route static UI assets
and API calls around HTML sub_filter injection to keep bundles compressed.
2026-06-19 20:58:44 +02:00

213 lines
5.8 KiB
JavaScript

(function () {
"use strict";
const PRESETS_URL = "/ui/platform-overlay/presets.json";
const MAX_APPLY_ATTEMPTS = 40;
const APPLY_INTERVAL_MS = 250;
const DEFAULT_PRESETS = {
namespace: "",
method: "oidc",
mount: "netkingdom",
role: "platform-admin",
title: "Sign in with KeyCape",
signInLabel: "Sign in with KeyCape",
banner:
"Platform operators authenticate through KeyCape at kc.coulomb.social.",
};
let presets = { ...DEFAULT_PRESETS };
let applyAttempts = 0;
let applyTimer = null;
let overlayApplied = false;
function isAuthPage() {
const path = window.location.pathname;
return (
/\/ui\/vault\/auth(?:\/|$)/.test(path) ||
/\/ui\/?$/.test(path)
);
}
function hideNode(node) {
if (!node || node.dataset.keycapeOverlayHidden === "true") return;
const field =
node.closest(".field.is-horizontal") ||
node.closest(".field") ||
node.closest(".box") ||
node;
if (field.dataset.keycapeOverlayHidden === "true") return;
field.style.display = "none";
field.setAttribute("aria-hidden", "true");
field.dataset.keycapeOverlayHidden = "true";
}
function setInputValue(input, value) {
if (!input || input.dataset.keycapeOverlayPreset === value) return;
input.value = value;
input.dataset.keycapeOverlayPreset = value;
// Fire once so Ember picks up the preset without a mutation feedback loop.
input.dispatchEvent(new Event("input", { bubbles: true }));
input.dispatchEvent(new Event("change", { bubbles: true }));
}
function ensureAuthMountSelected() {
const mount = presets.mount || "netkingdom";
const withValue = mount.endsWith("/") ? mount : `${mount}/`;
const params = new URLSearchParams(window.location.search);
const current = params.get("with") || "";
if (current.replace(/\/$/, "") === withValue.replace(/\/$/, "")) {
return;
}
if (!isAuthPage() || window.location.pathname.includes("/oidc/")) {
return;
}
params.set("with", withValue);
const next = `${window.location.pathname}?${params.toString()}`;
if (next !== `${window.location.pathname}${window.location.search}`) {
window.location.replace(next);
}
}
function loginShellReady() {
return Boolean(
document.querySelector(".login-form") ||
document.querySelector(".auth-form") ||
document.querySelector(".toolbar-namespace-picker")
);
}
function applyDom() {
if (!isAuthPage() || overlayApplied) return false;
hideNode(document.querySelector(".toolbar-namespace-picker"));
document
.querySelectorAll(
'#namespace, input[name="namespace"], label[for="namespace"]'
)
.forEach(hideNode);
document
.querySelectorAll('select[name="auth-method"], #auth-method')
.forEach((el) => hideNode(el.closest(".field") || el));
document
.querySelectorAll('#custom-path, input[name="custom-path"]')
.forEach(hideNode);
document
.querySelectorAll('#role, input[name="role"], label[for="role"]')
.forEach(hideNode);
document.querySelectorAll("nav.tabs").forEach((el) => {
if (el.dataset.keycapeOverlayHidden === "true") return;
el.style.display = "none";
el.setAttribute("aria-hidden", "true");
el.dataset.keycapeOverlayHidden = "true";
});
document
.querySelectorAll(".auth-form .has-bottom-margin-s")
.forEach(hideNode);
document.querySelectorAll("h1.title.is-3").forEach((heading) => {
if (
/Sign in to OpenBao|Authenticate/.test(heading.textContent) &&
heading.textContent !== presets.title
) {
heading.textContent = presets.title;
}
});
document
.querySelectorAll('#auth-submit, button[data-test="auth-submit"]')
.forEach((button) => {
if (button.textContent !== presets.signInLabel) {
button.textContent = presets.signInLabel;
}
});
document
.querySelectorAll('#namespace, input[name="namespace"]')
.forEach((input) => setInputValue(input, presets.namespace || ""));
document
.querySelectorAll('#role, input[name="role"]')
.forEach((input) =>
setInputValue(input, presets.role || "platform-admin")
);
if (!document.getElementById("keycape-overlay-banner")) {
const banner = document.createElement("div");
banner.id = "keycape-overlay-banner";
banner.className = "keycape-overlay-banner";
banner.textContent = presets.banner;
const loginForm = document.querySelector(".login-form");
if (loginForm) {
loginForm.prepend(banner);
}
}
document.documentElement.classList.add("keycape-overlay-active");
if (loginShellReady()) {
overlayApplied = true;
return true;
}
return false;
}
function stopApplyLoop() {
if (applyTimer !== null) {
window.clearInterval(applyTimer);
applyTimer = null;
}
}
function scheduleApply() {
stopApplyLoop();
applyAttempts = 0;
const tick = () => {
applyAttempts += 1;
if (applyDom() || applyAttempts >= MAX_APPLY_ATTEMPTS) {
stopApplyLoop();
return;
}
};
tick();
applyTimer = window.setInterval(tick, APPLY_INTERVAL_MS);
}
async function loadPresets() {
try {
const response = await fetch(PRESETS_URL, { cache: "no-store" });
if (!response.ok) return;
const data = await response.json();
presets = { ...DEFAULT_PRESETS, ...data };
} catch (_error) {
presets = { ...DEFAULT_PRESETS };
}
}
async function init() {
await loadPresets();
if (!isAuthPage()) return;
ensureAuthMountSelected();
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", scheduleApply, {
once: true,
});
} else {
scheduleApply();
}
}
init();
})();