auth-yes/public/admin-scripts.js

294 lines
9.4 KiB
JavaScript

function showNotice(msg, isError) {
const banner = document.getElementById("status-banner");
if (!banner) return;
banner.textContent = msg;
banner.style.display = "block";
banner.style.position = "fixed";
banner.style.top = "1.25rem";
banner.style.right = "1.25rem";
banner.style.zIndex = "99999";
banner.style.minWidth = "280px";
banner.style.maxWidth = "480px";
banner.style.padding = "0.85rem 1.25rem";
banner.style.borderRadius = "var(--radius-md)";
banner.style.boxShadow = "0 10px 25px -5px rgba(0, 0, 0, 0.35)";
banner.style.fontSize = "0.9rem";
banner.style.fontWeight = "600";
banner.style.background = isError ? "var(--danger-bg)" : "var(--success-bg)";
banner.style.color = isError ? "var(--danger-text)" : "var(--success-text)";
banner.style.border = isError
? "1px solid var(--danger-border)"
: "1px solid var(--success-border)";
if (banner._timeout) clearTimeout(banner._timeout);
banner._timeout = setTimeout(() => {
banner.style.display = "none";
}, 4000);
}
globalThis.showNotice = showNotice;
function filterUsersList() {
const queryInput = document.getElementById("userSearchInput");
if (!queryInput) return;
const query = queryInput.value.toLowerCase().trim();
const rows = document.querySelectorAll(".user-row");
const cards = document.querySelectorAll(".user-card");
rows.forEach((r) => {
const text = r.getAttribute("data-search") || "";
r.style.display = text.includes(query) ? "" : "none";
});
cards.forEach((c) => {
const text = c.getAttribute("data-search") || "";
c.style.display = text.includes(query) ? "" : "none";
});
}
globalThis.filterUsersList = filterUsersList;
function filterAppsList() {
const queryInput = document.getElementById("appSearchInput");
if (!queryInput) return;
const query = queryInput.value.toLowerCase().trim();
const rows = document.querySelectorAll(".app-row");
const cards = document.querySelectorAll(".app-card");
rows.forEach((r) => {
const text = r.getAttribute("data-search") || "";
r.style.display = text.includes(query) ? "" : "none";
});
cards.forEach((c) => {
const text = c.getAttribute("data-search") || "";
c.style.display = text.includes(query) ? "" : "none";
});
}
globalThis.filterAppsList = filterAppsList;
function filterAuditLogs() {
const queryInput = document.getElementById("auditSearchInput");
if (!queryInput) return;
const query = queryInput.value.toLowerCase().trim();
const rows = document.querySelectorAll(".log-row");
const cards = document.querySelectorAll(".log-card");
rows.forEach((r) => {
const text = r.getAttribute("data-search") || "";
r.style.display = text.includes(query) ? "" : "none";
});
cards.forEach((c) => {
const text = c.getAttribute("data-search") || "";
c.style.display = text.includes(query) ? "" : "none";
});
}
globalThis.filterAuditLogs = filterAuditLogs;
async function updateStatus(userId, status, username) {
if (!confirm("Set user @" + username + " to " + status.toUpperCase() + "?")) {
return;
}
try {
const res = await fetch("/api/admin/users/" + userId + "/status", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ status }),
});
if (res.ok) {
showNotice("User status updated to " + status, false);
setTimeout(() => globalThis.location.reload(), 600);
} else {
const data = await res.json();
showNotice(data.error || "Failed to update status", true);
}
} catch (_err) {
showNotice("Network error updating status", true);
}
}
globalThis.updateStatus = updateStatus;
async function handleUpdateProfile(e, userId) {
e.preventDefault();
const displayNameInput = document.getElementById("displayNameInput");
const displayName = displayNameInput ? displayNameInput.value.trim() : "";
try {
const res = await fetch("/api/admin/users/" + userId + "/profile", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ displayName }),
});
const data = await res.json();
if (res.ok) {
showNotice("User display name saved successfully!", false);
setTimeout(() => globalThis.location.reload(), 600);
} else {
showNotice(data.error || "Failed to update display name", true);
}
} catch (_err) {
showNotice("Network error updating display name", true);
}
}
globalThis.handleUpdateProfile = handleUpdateProfile;
async function handleGrantAccess(e, userId) {
e.preventDefault();
const grantAppId = document.getElementById("grantAppId");
const grantRole = document.getElementById("grantRole");
if (!grantAppId || !grantRole) return;
const appId = grantAppId.value;
const role = grantRole.value;
try {
const res = await fetch("/api/admin/users/" + userId + "/grants", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ appId, role }),
});
const data = await res.json();
if (res.ok) {
showNotice("Application access granted successfully!", false);
setTimeout(() => globalThis.location.reload(), 600);
} else {
showNotice(data.error || "Failed to update application grant", true);
}
} catch (_err) {
showNotice("Network error updating grant", true);
}
}
globalThis.handleGrantAccess = handleGrantAccess;
async function revokeGrant(userId, appId, appName) {
if (!confirm('Revoke access to "' + appName + '" for this user?')) return;
try {
const res = await fetch("/api/admin/users/" + userId + "/grants/" + appId, {
method: "DELETE",
});
if (res.ok) {
showNotice("Access revoked", false);
setTimeout(() => globalThis.location.reload(), 600);
} else {
const data = await res.json();
showNotice(data.error || "Failed to revoke grant", true);
}
} catch (_err) {
showNotice("Network error revoking grant", true);
}
}
globalThis.revokeGrant = revokeGrant;
async function generateRecoveryLink(userId) {
try {
const res = await fetch("/api/admin/users/" + userId + "/recovery", {
method: "POST",
});
const data = await res.json();
if (res.ok) {
const link = globalThis.location.origin + "/recovery?code=" +
data.recoveryCode;
const textEl = document.getElementById("recovery-link-text");
const containerEl = document.getElementById("recovery-link-container");
if (textEl) textEl.textContent = link;
if (containerEl) containerEl.style.display = "block";
showNotice("Recovery link generated!", false);
} else {
showNotice(data.error || "Failed to generate link", true);
}
} catch (_err) {
showNotice("Network error generating recovery link", true);
}
}
globalThis.generateRecoveryLink = generateRecoveryLink;
async function revokeSession(sessionId) {
if (!confirm("Revoke this session?")) return;
try {
const res = await fetch("/api/admin/sessions/" + sessionId, {
method: "DELETE",
});
if (res.ok) {
showNotice("Session revoked", false);
setTimeout(() => globalThis.location.reload(), 600);
} else {
showNotice("Failed to revoke session", true);
}
} catch (_err) {
showNotice("Network error revoking session", true);
}
}
globalThis.revokeSession = revokeSession;
async function revokeAllSessions(userId) {
if (
!confirm(
"Revoke ALL sessions for this user? They will be immediately logged out.",
)
) return;
try {
const res = await fetch("/api/admin/users/" + userId + "/sessions", {
method: "DELETE",
});
if (res.ok) {
showNotice("All sessions revoked", false);
setTimeout(() => globalThis.location.reload(), 600);
} else {
showNotice("Failed to revoke all sessions", true);
}
} catch (_err) {
showNotice("Network error revoking sessions", true);
}
}
globalThis.revokeAllSessions = revokeAllSessions;
async function deletePasskey(userId, passkeyId) {
if (
!confirm(
"Permanently delete this device? The user will no longer be able to log in with it.",
)
) return;
try {
const res = await fetch(
"/api/admin/users/" + userId + "/passkeys/" + passkeyId,
{ method: "DELETE" },
);
const data = await res.json();
if (res.ok) {
showNotice("Passkey deleted", false);
setTimeout(() => globalThis.location.reload(), 600);
} else {
showNotice(data.error || "Failed to delete passkey", true);
}
} catch (_err) {
showNotice("Network error deleting passkey", true);
}
}
globalThis.deletePasskey = deletePasskey;
function updateRoleOptions(allRolesJson) {
const grantAppIdEl = document.getElementById("grantAppId");
if (!grantAppIdEl) return;
const appId = grantAppIdEl.value;
const roleSelect = document.getElementById("grantRole");
if (!roleSelect) return;
roleSelect.innerHTML = "";
const roles = typeof allRolesJson === "string"
? JSON.parse(allRolesJson)
: (allRolesJson || []);
const available = roles.filter((r) => !r.app_id || r.app_id === appId);
if (available.length === 0) {
const opt = document.createElement("option");
opt.value = "user";
opt.textContent = "user";
roleSelect.appendChild(opt);
return;
}
available.forEach((r) => {
const opt = document.createElement("option");
opt.value = r.name;
opt.textContent = r.name + (r.app_id ? " (App Custom)" : " (Global)");
roleSelect.appendChild(opt);
});
}
globalThis.updateRoleOptions = updateRoleOptions;