// src/api/auth.ts var AUTH_ROLES = ["guest", "read", "control", "transmit", "write", "administrator"]; var AUTH_ADMIN_ROLES = AUTH_ROLES.filter((role) => role !== "guest"); var AUTH_ROLE_LABELS = { guest: "Guest", read: "Read", control: "Control", transmit: "Transmit", write: "Write", administrator: "Administrator" }; function isAuthRole(value) { return typeof value === "string" && AUTH_ROLES.includes(value); } function normalizeAuthRoles(roles) { return AUTH_ROLES.filter((role) => roles.includes(role)); } function hasAuthRole(roles, required) { return roles.includes("administrator") || roles.includes(required) || required === "read" && roles.includes("guest") || required === "read" && (roles.includes("control") || roles.includes("transmit")); } function hasAccountControls(roles) { return roles.length > 0 && !roles.includes("guest"); } function decodeRoles(value, context) { if (!Array.isArray(value) || !value.every(isAuthRole)) { throw new TypeError(`${context} has invalid roles`); } return normalizeAuthRoles(value); } function decodeAuthSession(value) { if (typeof value !== "object" || value === null) { throw new TypeError("The authentication response is malformed"); } const session = value; if (typeof session.authenticated !== "boolean") { throw new TypeError("The authentication response has no authenticated flag"); } if (session.auth_disabled !== void 0 && typeof session.auth_disabled !== "boolean") { throw new TypeError("The authentication response has an invalid auth_disabled flag"); } const decoded = { authenticated: session.authenticated, roles: decodeRoles(session.roles, "The authentication response") }; if (session.username !== void 0) { if (typeof session.username !== "string") throw new TypeError("The authentication response has an invalid username"); decoded.username = session.username; } if (session.auth_disabled !== void 0) decoded.auth_disabled = session.auth_disabled; return decoded; } var authDisabledSession = { authenticated: true, roles: [...AUTH_ADMIN_ROLES], auth_disabled: true }; async function fetchAuthSession() { try { const response = await fetch("/auth/session"); if (response.status === 404) return authDisabledSession; if (!response.ok) return { authenticated: false, roles: [] }; return decodeAuthSession(await response.json()); } catch (error) { console.error("Auth check failed:", error); return { authenticated: false, roles: [] }; } } async function login(username, password) { const response = await fetch("/auth/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username, password }) }); if (response.status === 404) return authDisabledSession; if (!response.ok) { const message = await response.text(); throw new Error(message || "Login failed"); } return decodeAuthSession(await response.json()); } async function userRequest(path, init) { const response = await fetch(path, init); if (!response.ok) { const payload = await response.json().catch(() => ({})); throw new Error(payload.error || `User operation failed (${response.status})`); } return response; } async function listUsers() { const value = await userRequest("/auth/users").then((response) => response.json()); if (!Array.isArray(value) || !value.every((user) => { if (typeof user !== "object" || user === null) return false; const record = user; return typeof record.username === "string" && typeof record.enabled === "boolean" && Array.isArray(record.roles) && record.roles.every(isAuthRole); })) { throw new TypeError("The user list response is malformed"); } return value.map((user) => ({ ...user, roles: normalizeAuthRoles(user.roles) })); } async function createUser(username, password, roles, enabled = true) { await userRequest("/auth/users", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username, password, roles, enabled }) }); } async function updateUser(username, changes) { await userRequest(`/auth/users/${encodeURIComponent(username)}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(changes) }); } async function changeOwnPassword(currentPassword, newPassword) { await userRequest("/auth/account/password", { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ current_password: currentPassword, new_password: newPassword }) }); } async function deleteUser(username) { await userRequest(`/auth/users/${encodeURIComponent(username)}`, { method: "DELETE" }); } async function logout() { const response = await fetch("/auth/logout", { method: "POST" }); if (response.status !== 404 && !response.ok) throw new Error("Logout failed"); } export { AUTH_ROLES, AUTH_ADMIN_ROLES, AUTH_ROLE_LABELS, normalizeAuthRoles, hasAuthRole, hasAccountControls, fetchAuthSession, login, listUsers, createUser, updateUser, changeOwnPassword, deleteUser, logout };