148 lines
5.8 KiB
JavaScript
148 lines
5.8 KiB
JavaScript
/* 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();
|
||
renderPermMatrix();
|
||
});
|
||
|
||
/* 角色权限矩阵展示(issue #151 验收:矩阵生效可见;与 studio 门控同源) */
|
||
var PERM_MATRIX = {
|
||
readonly: { view: true, edit: false, publish: false, manage: false },
|
||
engineer: { view: true, edit: true, publish: false, manage: false },
|
||
admin: { view: true, edit: true, publish: true, manage: true }
|
||
};
|
||
function renderPermMatrix() {
|
||
var tbody = document.querySelector("#perm-matrix tbody");
|
||
tbody.innerHTML = "";
|
||
UserStore.ROLES.forEach(function (r) {
|
||
var tr = document.createElement("tr");
|
||
var td = document.createElement("td");
|
||
td.textContent = UserStore.ROLE_LABELS[r];
|
||
td.className = "role-" + r;
|
||
tr.appendChild(td);
|
||
["view", "edit", "publish", "manage"].forEach(function (a) {
|
||
var c = document.createElement("td");
|
||
c.textContent = PERM_MATRIX[r][a] ? "✓" : "✗";
|
||
c.style.color = PERM_MATRIX[r][a] ? "#2ecc71" : "#64748b";
|
||
tr.appendChild(c);
|
||
});
|
||
tbody.appendChild(tr);
|
||
});
|
||
}
|
||
})();
|