import { defineStore } from "pinia"; import { ref, computed } from "vue"; interface Claims { sub: string; permissions: string; exp: number; } function parseJwt(token: string): Claims | null { try { const payload = token.split(".")[1]; return payload ? (JSON.parse(atob(payload)) as Claims) : null; } catch { return null; } } export const useAuthStore = defineStore("auth", () => { const token = ref(localStorage.getItem("mercury_token")); const claims = computed(() => token.value ? parseJwt(token.value) : null, ); const isAuthenticated = computed(() => { if (!claims.value) return false; return claims.value.exp * 1000 > Date.now(); }); const username = computed(() => claims.value?.sub ?? ""); function hasPermission(bit: bigint): boolean { if (!claims.value) return false; const mask = BigInt(claims.value.permissions); return (mask & bit) !== 0n; } const isSuperAdmin = computed(() => hasPermission(32n)); async function login(username: string, password: string): Promise { const res = await fetch("/auth/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username, password }), }); if (!res.ok) throw new Error("Invalid credentials"); const data = await res.json(); token.value = data.token; localStorage.setItem("mercury_token", data.token); } function logout() { token.value = null; localStorage.removeItem("mercury_token"); } function authHeaders(): Record { return token.value ? { Authorization: `Bearer ${token.value}` } : {}; } return { token, claims, isAuthenticated, username, isSuperAdmin, hasPermission, login, logout, authHeaders, }; });