// Reactive auth state shared across the app. Token + user are persisted to // localStorage so a refresh keeps the session. The actual network calls live in // api.js (which reads the token from localStorage on each request). import { reactive, computed } from "vue"; import { api, TOKEN_KEY, USER_KEY } from "./api"; import { applyProfilePrefs } from "./prefs"; export const state = reactive({ token: localStorage.getItem(TOKEN_KEY) || "", user: JSON.parse(localStorage.getItem(USER_KEY) || "null"), // Full settings-panel profile (bio, theme, locale, ...), fetched separately // from /api/me since the login response only carries id/email/name. profile: null, }); export const isAuthenticated = computed(() => !!state.token); // Roles that may manage users. A superadmin is an admin that also spans every // organization; the API Server enforces the difference, the UI just needs to // know whether to offer the Settings › Users tab at all. const MANAGER_ROLES = ["admin", "superadmin"]; // Admin gate for the UI. Driven by the full profile (fetched from /api/me), // which always reflects the current role from the DB — so a promotion/demotion // takes effect on the next profile refresh without needing a re-login. export const isAdmin = computed( () => MANAGER_ROLES.includes(state.profile?.role) || MANAGER_ROLES.includes(state.user?.role) ); export async function login(email, password) { // The API Server proxies login to PocketBase and relays its response // verbatim, so the user is under `record` (PocketBase's name) and the token // is PocketBase's own — this app no longer holds a server-minted JWT. const res = await api.login(email, password); state.token = res.token; state.user = res.record; localStorage.setItem(TOKEN_KEY, res.token); localStorage.setItem(USER_KEY, JSON.stringify(res.record)); await refreshProfile(); return res; } export function logout() { state.token = ""; state.user = null; state.profile = null; localStorage.removeItem(TOKEN_KEY); localStorage.removeItem(USER_KEY); } // Fetches the full profile (used for Settings + to apply appearance prefs). // Safe to call on app boot when a token already exists from a previous visit. export async function refreshProfile() { if (!state.token) return null; const profile = await api.getMe(); state.profile = profile; applyProfilePrefs(profile); return profile; }