/* iAOP 用户/角色管理页逻辑(issue #134)。 * 会话守卫复用 #150 的 IAOP_AUTH.requireLoginElseRedirect(core/auth 后端 /auth/me); * 仅 admin(manage 权限)可访问;CRUD/审计/OIDC 走 user_store.js(Demo 存储)。 */ "use strict"; (function () { IAOP_AUTH.requireLoginElseRedirect("login.html").then(function (user) { if (!user) return; // 正在跳转登录页 if (user.role !== "admin") { alert("仅 Publisher(admin)可访问用户管理"); location.href = "../studio/index.html"; return; } document.getElementById("who").textContent = user.username + " · " + UserStore.ROLE_LABELS[user.role]; function el(tag, text) { var n = document.createElement(tag); if (text !== undefined) n.textContent = text; return n; } function renderUsers() { var tbody = document.getElementById("user-tbody"); tbody.innerHTML = ""; UserStore.list().forEach(function (u) { var tr = el("tr"); if (!u.active) tr.className = "disabled-user"; tr.appendChild(el("td", u.username)); // 角色分配(下拉即改) var tdRole = el("td"); var sel = el("select"); UserStore.ROLES.forEach(function (r) { var opt = el("option", UserStore.ROLE_LABELS[r]); opt.value = r; if (r === u.role) opt.selected = true; sel.appendChild(opt); }); sel.className = "role-" + u.role; sel.onchange = function () { UserStore.update(u.username, { role: sel.value }, user.username).then(renderUsers); }; tdRole.appendChild(sel); tr.appendChild(tdRole); tr.appendChild(el("td", u.active ? "启用" : "禁用")); tr.appendChild(el("td", (u.created_at || "").slice(0, 10))); var tdOps = el("td"); var toggle = el("button", u.active ? "禁用" : "启用"); toggle.onclick = function () { UserStore.update(u.username, { active: !u.active }, user.username).then(renderUsers); }; var reset = el("button", "重置密码"); reset.onclick = function () { var pwd = prompt("为 " + u.username + " 设置新密码(≥6 位)"); if (pwd) UserStore.update(u.username, { password: pwd }, user.username).then(renderUsers); }; var del = el("button", "删除"); del.className = "danger"; del.onclick = function () { if (u.username === user.username) { alert("不能删除当前登录账号"); return; } if (confirm("确认删除用户 " + u.username + "?")) { UserStore.remove(u.username, user.username).then(renderUsers); } }; [toggle, reset, del].forEach(function (b) { b.style.marginRight = "4px"; tdOps.appendChild(b); }); tr.appendChild(tdOps); tbody.appendChild(tr); }); renderAudit(); } function renderAudit() { var box = document.getElementById("audit-list"); box.innerHTML = ""; UserStore.auditLog().slice().reverse().forEach(function (e) { box.appendChild(el("div", e.time.slice(0, 19).replace("T", " ") + " | " + e.actor + " | " + e.action + " | " + e.resource + " | " + e.reason)); }); } document.getElementById("btn-add").onclick = function () { var err = document.getElementById("form-err"); err.textContent = ""; UserStore.create(document.getElementById("new-username").value.trim(), document.getElementById("new-password").value, document.getElementById("new-role").value) .then(function () { document.getElementById("new-username").value = ""; document.getElementById("new-password").value = ""; renderUsers(); }) .catch(function (e) { err.textContent = e.message; }); }; // OIDC 配置点 var cfg = UserStore.oidcConfig() || {}; document.getElementById("oidc-issuer").value = cfg.issuer || ""; document.getElementById("oidc-client").value = cfg.client_id || ""; document.getElementById("oidc-redirect").value = cfg.redirect_uri || ""; document.getElementById("btn-oidc").onclick = function () { UserStore.configureOidc({ issuer: document.getElementById("oidc-issuer").value.trim(), client_id: document.getElementById("oidc-client").value.trim(), redirect_uri: document.getElementById("oidc-redirect").value.trim() }, user.username); alert("OIDC 配置已保存;登录页将出现 SSO 入口"); }; document.getElementById("logout-btn").onclick = function () { fetch((IAOP_AUTH.AUTH_BASE || "") + "/auth/logout", { method: "POST", credentials: "include" }) .finally(function () { location.href = "login.html"; }); }; renderUsers(); }); })();