/** * 实例:导航显示、env 配置、改密、PM2 重启。 */ (function (global) { const DISPLAY = () => global.__INSTANCE_DISPLAY__ || {}; function setStatus(el, text, isErr) { if (!el) return; el.textContent = text || ""; el.classList.toggle("err", !!isErr); } async function fetchJson(url, opts) { const res = await fetch(url, Object.assign({ credentials: "same-origin" }, opts || {})); const data = await res.json().catch(() => ({})); if (!res.ok) { throw new Error(data.msg || res.statusText || "请求失败"); } return data; } function applyDisplayToNav(display) { const map = { strategy: "show_nav_strategy", strategy_records: "show_nav_strategy_records", records: "show_nav_records", stats: "show_nav_stats", options: "show_nav_options", risk_policy: "show_nav_risk_policy", env_config: "show_nav_env_config", }; document.querySelectorAll(".embed-top-nav [data-embed-tab], .top-nav a[href^='/']").forEach((a) => { const tab = a.getAttribute("data-embed-tab") || (a.getAttribute("href") || "").replace(/^\//, "").split("?")[0]; const key = map[tab]; if (!key) return; const show = display[key] !== false; a.classList.toggle("nav-hidden", !show); a.style.display = show ? "" : "none"; }); global.__INSTANCE_DISPLAY__ = display; } function pageNavAllowed(tab) { const d = DISPLAY(); const map = { strategy: "show_nav_strategy", strategy_records: "show_nav_strategy_records", records: "show_nav_records", stats: "show_nav_stats", options: "show_nav_options", risk_policy: "show_nav_risk_policy", env_config: "show_nav_env_config", }; const key = map[tab]; if (!key) return true; return d[key] !== false; } function displayPrefsRoot() { const pane = document.querySelector(".embed-tab-pane.is-active-pane"); if (pane) { const inPane = pane.querySelector("#display-prefs-form"); if (inPane) return inPane; } return document.getElementById("display-prefs-form"); } function envConfigGrid() { const pane = document.querySelector(".embed-tab-pane.is-active-pane"); if (pane) { const inPane = pane.querySelector("#env-config-grid"); if (inPane) return inPane; } return document.getElementById("env-config-grid"); } async function loadDisplayPrefsForm(force) { const root = displayPrefsRoot(); if (!root) return; if (!force && root.getAttribute("data-prefs-ssr") === "1" && root.querySelector("[data-pref-key]")) { return; } return loadDisplayPrefsFormIn(root); } async function loadDisplayPrefsFormIn(root) { try { const data = await fetchJson("/api/settings/display"); const display = data.display || {}; const meta = data.meta || []; root.innerHTML = ""; meta.forEach((group) => { const section = document.createElement("div"); section.className = "display-prefs-group"; const title = document.createElement("h3"); title.className = "settings-subcard-title"; title.textContent = group.group; section.appendChild(title); const grid = document.createElement("div"); grid.className = "display-prefs-checks"; (group.items || []).forEach((item) => { const label = document.createElement("label"); label.className = "chk-label"; const cb = document.createElement("input"); cb.type = "checkbox"; cb.dataset.prefKey = item.key; cb.checked = display[item.key] !== false; label.appendChild(cb); label.appendChild(document.createTextNode(" " + item.label)); grid.appendChild(label); }); section.appendChild(grid); root.appendChild(section); }); root.setAttribute("data-prefs-ssr", "1"); } catch (e) { root.innerHTML = '' + (e.message || "加载失败") + ""; } } async function saveDisplayPrefs() { const status = document.getElementById("display-prefs-status"); const root = displayPrefsRoot(); const display = {}; (root ? root.querySelectorAll("input[data-pref-key]") : []).forEach((cb) => { display[cb.dataset.prefKey] = !!cb.checked; }); try { const data = await fetchJson("/api/settings/display", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ display }), }); applyDisplayToNav(data.display || display); setStatus(status, "已保存,导航已更新"); } catch (e) { setStatus(status, e.message || "保存失败", true); } } let envSchemaGroups = []; function renderEnvField(field) { const card = document.createElement("div"); card.className = "env-field-card"; const label = document.createElement("label"); label.className = "env-field-label"; label.textContent = field.key; card.appendChild(label); if (field.note) { const note = document.createElement("div"); note.className = "env-field-note muted"; note.textContent = field.note; card.appendChild(note); } let input; if (field.type === "bool") { input = document.createElement("select"); ["true", "false"].forEach((v) => { const o = document.createElement("option"); o.value = v; o.textContent = v; input.appendChild(o); }); const cur = (field.current || field.default || "false").toLowerCase(); input.value = cur === "true" || cur === "1" ? "true" : "false"; } else { input = document.createElement("input"); input.type = field.sensitive ? "password" : "text"; if (field.sensitive && field.has_value) { input.placeholder = field.masked || "留空不修改"; } else { input.value = field.current || field.default || ""; } } input.dataset.envKey = field.key; input.className = "env-field-input"; card.appendChild(input); const badge = document.createElement("span"); badge.className = "env-field-badge " + (field.restart_required ? "env-badge-restart" : "env-badge-hot"); badge.textContent = field.restart_required ? "需重启" : "保存即生效"; card.appendChild(badge); return card; } async function loadEnvConfig(force) { const grid = envConfigGrid(); if (!grid) return; if (!force && grid.getAttribute("data-env-ssr") === "1" && grid.querySelector("[data-env-key]")) { return; } return loadEnvConfigIn(grid); } async function loadEnvConfigIn(grid) { grid.innerHTML = '