Files
MajoorWaldi-ComfyUI-Majoor-…/js/app/settings/settingsRuntime.js
T
MajoorWaldi 977179de90 feat(security): implement settings-first remote setup flow and improve documentation
- Add Recommended Remote LAN Setup option in Settings for trusted LAN configurations
- Implement loopback recovery bootstrap for local browser sessions
- Automatically rotate legacy token hashes to new plaintext tokens
- Update documentation to reflect new remote setup process and security features
- Add runtime write authorization status indicators in Assets Manager
- Improve client-side token handling and session management
- Update environment variables guide with new bootstrap capabilities
2026-04-06 16:59:18 +02:00

220 lines
7.9 KiB
JavaScript

/**
* Runtime status dashboard widget for Majoor.
* Renders a floating overlay inside the Assets Manager panel showing live
* DB / enrichment-queue / watcher metrics fetched from the backend.
*/
import { getRuntimeStatus } from "../../api/client.js";
import { getSecuritySettings } from "../../api/client.js";
import { t } from "../i18n.js";
const RUNTIME_DASHBOARD_ID = "mjr-runtime-status-dashboard";
const RUNTIME_TOKEN_KEY = "__mjr_write_token";
function readRuntimeWriteToken() {
try {
return String(sessionStorage?.getItem?.(RUNTIME_TOKEN_KEY) || "").trim();
} catch {
return "";
}
}
function ensureDashboardLine(el, key) {
const storeKey = key === "auth" ? "__mjrAuthLine" : "__mjrMetricsLine";
if (el?.[storeKey]) {
return el[storeKey];
}
const line = document.createElement("div");
line.style.whiteSpace = "nowrap";
line.style.lineHeight = "1.35";
if (key === "auth") {
line.style.marginTop = "4px";
line.style.fontWeight = "600";
}
el.appendChild(line);
el[storeKey] = line;
return line;
}
function buildWriteAuthSummary(prefs) {
const token = readRuntimeWriteToken();
const tokenHint = String(prefs?.token_hint || "").trim();
const sessionHint = tokenHint || (token ? `...${token.slice(-4)}` : "");
const allowWrite = prefs?.allow_write !== false;
const requireAuth = prefs?.require_auth === true;
const tokenConfigured = prefs?.token_configured === true;
if (!allowWrite) {
return {
text: t("runtime.writeAuthBlocked", "Write auth: writes blocked by server"),
color: "#ff9b9b",
};
}
if (token) {
return {
text: t("runtime.writeAuthActive", "Write auth: active {tokenHint}", {
tokenHint: sessionHint || "(session)",
}),
color: "#7ee0a0",
};
}
if (requireAuth && tokenConfigured) {
return {
text: t(
"runtime.writeAuthMissing",
"Write auth: missing in this browser {tokenHint}",
{ tokenHint: sessionHint || "(server token configured)" },
),
color: "#f1c36d",
};
}
if (requireAuth) {
return {
text: t("runtime.writeAuthRequired", "Write auth: required"),
color: "#f1c36d",
};
}
if (prefs && typeof prefs === "object") {
return {
text: t("runtime.writeAuthNotRequired", "Write auth: not required"),
color: "#8fd0ff",
};
}
return {
text: t("runtime.writeAuthUnknown", "Write auth: unknown"),
color: "#c8ced8",
};
}
// ─── DOM helpers ──────────────────────────────────────────────────────────
function ensureRuntimeStatusDashboard() {
try {
const host = document.querySelector(".mjr-assets-manager.mjr-am-container");
const existing = document.getElementById(RUNTIME_DASHBOARD_ID);
if (!host) {
try {
existing?.remove?.();
} catch (e) {
console.debug?.(e);
}
return null;
}
// Anchor to the Assets Manager container (not the scrollable grid),
// so the widget stays fixed while the grid scrolls and disappears when panel closes.
try {
const hostPos = String(getComputedStyle(host).position || "").toLowerCase();
if (!hostPos || hostPos === "static") {
host.style.position = "relative";
}
} catch (e) {
console.debug?.(e);
}
let el = document.getElementById(RUNTIME_DASHBOARD_ID);
if (!el) {
el = document.createElement("div");
el.id = RUNTIME_DASHBOARD_ID;
el.style.position = "absolute";
el.style.bottom = "10px";
el.style.right = "10px";
el.style.zIndex = "9999";
el.style.padding = "6px 10px";
el.style.borderRadius = "10px";
el.style.border = "1px solid rgba(255,255,255,0.16)";
el.style.background = "rgba(0,0,0,0.45)";
el.style.backdropFilter = "blur(4px)";
el.style.color = "var(--content-fg, #fff)";
el.style.fontSize = "11px";
el.style.pointerEvents = "none";
el.style.display = "flex";
el.style.flexDirection = "column";
host.appendChild(el);
} else if (el.parentElement !== host) {
host.appendChild(el);
}
return el;
} catch {
return null;
}
}
async function refreshRuntimeStatusDashboard() {
const el = ensureRuntimeStatusDashboard();
if (!el) return false;
const metricsEl = ensureDashboardLine(el, "metrics");
const authEl = ensureDashboardLine(el, "auth");
try {
const [runtimeRes, securityRes] = await Promise.all([
getRuntimeStatus(),
getSecuritySettings(),
]);
let runtimeTitle = t("runtime.unavailable", "Runtime: unavailable");
if (!runtimeRes?.ok || !runtimeRes?.data) {
metricsEl.textContent = runtimeTitle;
} else {
const db = runtimeRes.data.db || {};
const idx = runtimeRes.data.index || {};
const w = runtimeRes.data.watcher || {};
const active = Number(db.active_connections || 0);
const enrichQ = Number(idx.enrichment_queue_length || 0);
const pending = Number(w.pending_files || 0);
metricsEl.textContent = t(
"runtime.metricsLine",
"DB active: {active} | Enrich Q: {enrichQ} | Watcher pending: {pending}",
{ active, enrichQ, pending },
);
runtimeTitle = t(
"runtime.metricsTitle",
"Runtime Metrics\nDB active connections: {active}\nEnrichment queue: {enrichQ}\nWatcher pending files: {pending}",
{ active, enrichQ, pending },
);
}
const authSummary = buildWriteAuthSummary(securityRes?.data?.prefs || null);
authEl.textContent = authSummary.text;
authEl.style.color = authSummary.color;
el.title = `${runtimeTitle}\n${authSummary.text}`;
return true;
} catch {
metricsEl.textContent = t("runtime.unavailable", "Runtime: unavailable");
authEl.textContent = t("runtime.writeAuthUnknown", "Write auth: unknown");
authEl.style.color = "#c8ced8";
el.title = `${t("runtime.unavailable", "Runtime: unavailable")}\n${authEl.textContent}`;
return true;
}
}
export function startRuntimeStatusDashboard() {
try {
refreshRuntimeStatusDashboard().catch(() => {});
if (window.__MJR_RUNTIME_STATUS_INFLIGHT__ == null) {
window.__MJR_RUNTIME_STATUS_INFLIGHT__ = false;
}
if (window.__MJR_RUNTIME_STATUS_MISS_COUNT__ == null) {
window.__MJR_RUNTIME_STATUS_MISS_COUNT__ = 0;
}
if (!window.__MJR_RUNTIME_STATUS_INTERVAL__) {
window.__MJR_RUNTIME_STATUS_INTERVAL__ = setInterval(() => {
if (window.__MJR_RUNTIME_STATUS_INFLIGHT__) return;
window.__MJR_RUNTIME_STATUS_INFLIGHT__ = true;
refreshRuntimeStatusDashboard()
.then((visible) => {
if (visible) {
window.__MJR_RUNTIME_STATUS_MISS_COUNT__ = 0;
return;
}
window.__MJR_RUNTIME_STATUS_MISS_COUNT__ =
Number(window.__MJR_RUNTIME_STATUS_MISS_COUNT__ || 0) + 1;
})
.catch(() => {})
.finally(() => {
window.__MJR_RUNTIME_STATUS_INFLIGHT__ = false;
});
}, 10000);
}
} catch (e) {
console.debug?.(e);
}
}