Admins can change passwords. Request user confirmation to open gate
This commit is contained in:
@@ -27,7 +27,7 @@ async function api(method, path, body) {
|
||||
if (res.status === 401) {
|
||||
clearToken();
|
||||
showLogin();
|
||||
throw new Error("Session expired.");
|
||||
throw new Error("Session expired or invalid credentials");
|
||||
}
|
||||
if (!res.ok) {
|
||||
const j = await res.json().catch(() => null);
|
||||
@@ -53,7 +53,9 @@ function showLogin() {
|
||||
function showAdmin() {
|
||||
document.getElementById("login-view").classList.add("hidden");
|
||||
document.getElementById("admin-view").classList.remove("hidden");
|
||||
const isAdmin = _tokenPayload().scope === "admin";
|
||||
const payload = _tokenPayload();
|
||||
const isAdmin = payload.scope === "admin";
|
||||
document.getElementById("header-username").textContent = payload.sub || "";
|
||||
document.querySelectorAll(".admin-only").forEach(el => {
|
||||
el.style.display = isAdmin ? "" : "none";
|
||||
});
|
||||
@@ -504,11 +506,21 @@ async function loadAdmins() {
|
||||
const tr = document.createElement("tr");
|
||||
tr.innerHTML = `
|
||||
<td>${esc(u.username)}${u.username === me ? ' <span class="badge badge-green" style="font-size:.75em">you</span>' : ""} ${roleBadge}</td>
|
||||
<td style="text-align:right">
|
||||
<td style="text-align:right;display:flex;gap:.5rem;justify-content:flex-end">
|
||||
<button class="btn btn-ghost" style="font-size:.8rem;padding:.35rem .9rem" data-chpw="${esc(u.username)}">Change password</button>
|
||||
${u.username !== me ? `<button class="btn btn-danger" style="font-size:.8rem;padding:.35rem .9rem" data-del-admin="${esc(u.username)}">Delete</button>` : ""}
|
||||
</td>`;
|
||||
tbody.appendChild(tr);
|
||||
}
|
||||
tbody.querySelectorAll("[data-chpw]").forEach(btn => {
|
||||
btn.addEventListener("click", () => {
|
||||
document.getElementById("chpw-username").value = btn.dataset.chpw;
|
||||
document.getElementById("chpw-new").value = "";
|
||||
document.getElementById("chpw-confirm").value = "";
|
||||
document.getElementById("chpw-error").classList.add("hidden");
|
||||
document.getElementById("chpw-modal").classList.remove("hidden");
|
||||
});
|
||||
});
|
||||
tbody.querySelectorAll("[data-del-admin]").forEach(btn => {
|
||||
btn.addEventListener("click", async () => {
|
||||
if (!confirm(`Delete admin "${btn.dataset.delAdmin}"?`)) return;
|
||||
@@ -549,6 +561,32 @@ document.getElementById("admin-form").addEventListener("submit", async e => {
|
||||
}
|
||||
});
|
||||
|
||||
// ── Change password modal ─────────────────────────────────────────────────────
|
||||
document.getElementById("chpw-cancel").addEventListener("click", () => {
|
||||
document.getElementById("chpw-modal").classList.add("hidden");
|
||||
});
|
||||
document.getElementById("chpw-form").addEventListener("submit", async e => {
|
||||
e.preventDefault();
|
||||
const username = document.getElementById("chpw-username").value;
|
||||
const newPw = document.getElementById("chpw-new").value;
|
||||
const confirm = document.getElementById("chpw-confirm").value;
|
||||
const errEl = document.getElementById("chpw-error");
|
||||
errEl.classList.add("hidden");
|
||||
if (newPw !== confirm) {
|
||||
errEl.textContent = "Passwords do not match";
|
||||
errEl.classList.remove("hidden");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await api("PATCH", `/api/admin/admins/${encodeURIComponent(username)}/password`, { new_password: newPw });
|
||||
document.getElementById("chpw-modal").classList.add("hidden");
|
||||
showToast("Password updated");
|
||||
} catch (e) {
|
||||
errEl.textContent = e.message;
|
||||
errEl.classList.remove("hidden");
|
||||
}
|
||||
});
|
||||
|
||||
// ── Load all data ─────────────────────────────────────────────────────────────
|
||||
function loadAllData() {
|
||||
const isAdmin = _tokenPayload().scope === "admin";
|
||||
|
||||
Reference in New Issue
Block a user