import type { AdminUserDto, AuthResponse, LoginInput } from "@imen/shared"; import { defineStore } from "pinia"; import { rawApi, setAuthHooks } from "@/api/http"; import { router } from "@/router"; let refreshing: Promise | null = null; export const useAuthStore = defineStore("auth", { state: () => ({ /** short-lived access token, kept in memory only */ token: "", user: null as AdminUserDto | null, /** true once the initial session check has run */ ready: false, }), getters: { isAdmin: (s) => s.user?.role === "ADMIN", }, actions: { setSession(r: AuthResponse) { this.token = r.accessToken; this.user = r.user; }, clear() { this.token = ""; this.user = null; }, async login(input: LoginInput) { this.setSession(await rawApi("/admin/auth/login", { method: "POST", body: input })); }, /** Exchanges the refresh cookie for a new access token; concurrent calls share one request */ refresh(): Promise { refreshing ??= rawApi("/admin/auth/refresh", { method: "POST" }) .then((r) => { this.setSession(r); return true; }) .catch(() => { this.clear(); return false; }) .finally(() => { refreshing = null; }); return refreshing; }, /** Restores the session after a page load (the refresh cookie survives reloads) */ async init() { if (this.ready) return; setAuthHooks({ token: () => this.token, refresh: () => this.refresh(), onExpired: () => { this.clear(); void router.push({ name: "login", query: { redirect: router.currentRoute.value.fullPath } }); }, }); await this.refresh(); this.ready = true; }, async logout() { await rawApi("/admin/auth/logout", { method: "POST" }).catch(() => undefined); this.clear(); await router.push({ name: "login" }); }, }, });