import { ref, computed } from "vue"; import type { UserOut } from "~/lib/api/types/user"; interface AuthData { value: UserOut | null; } interface AuthStatus { value: "loading" | "authenticated" | "unauthenticated"; } interface AuthState { data: AuthData; status: AuthStatus; signIn: (credentials: FormData, options?: { redirect?: boolean }) => Promise; signOut: (callbackUrl?: string) => Promise; refresh: () => Promise; getSession: () => Promise; setToken: (token: string | null) => void; } const authUser = ref(null); const authStatus = ref<"loading" | "authenticated" | "unauthenticated">("unauthenticated"); export const useAuthBackend = function (): AuthState { const { $axios } = useNuxtApp(); const router = useRouter(); const tokenName = useRuntimeConfig().public.AUTH_TOKEN; const tokenCookie = useCookie(tokenName); function setToken(token: string | null) { tokenCookie.value = token; } function handleAuthError(error: any, redirect = false) { // Only clear token on auth errors, not network errors if (error?.response?.status === 401) { setToken(null); authUser.value = null; authStatus.value = "unauthenticated"; if (redirect) { router.push("/login"); } } return false; } async function getSession(): Promise { if (!tokenCookie.value) { authUser.value = null; authStatus.value = "unauthenticated"; return; } authStatus.value = "loading"; try { const { data } = await $axios.get("/api/users/self"); authUser.value = data; authStatus.value = "authenticated"; } catch (error: any) { handleAuthError(error); authStatus.value = "unauthenticated"; throw error; } } async function signIn(credentials: FormData): Promise { authStatus.value = "loading"; try { const response = await $axios.post("/api/auth/token", credentials, { headers: { "Content-Type": "multipart/form-data", }, }); const { access_token } = response.data; setToken(access_token); await getSession(); } catch (error) { authStatus.value = "unauthenticated"; throw error; } } async function signOut(callbackUrl: string = ""): Promise { try { await $axios.post("/api/auth/logout"); } catch (error) { // Continue with logout even if API call fails console.warn("Logout API call failed:", error); } finally { setToken(null); authUser.value = null; authStatus.value = "unauthenticated"; await router.push(callbackUrl || "/login"); } } async function refresh(): Promise { if (!tokenCookie.value) return; try { const response = await $axios.get("/api/auth/refresh"); const { access_token } = response.data; setToken(access_token); await getSession(); } catch (error: any) { handleAuthError(error, true); throw error; } } // Auto-refresh user data periodically when authenticated if (import.meta.client) { let refreshInterval: NodeJS.Timeout | null = null; watch(() => authStatus.value, (status) => { if (status === "authenticated") { refreshInterval = setInterval(() => { if (tokenCookie.value) { getSession().catch(() => { // Ignore errors in background refresh }); } }, 5 * 60 * 1000); // 5 minutes } else { // Clear interval when not authenticated if (refreshInterval) { clearInterval(refreshInterval); refreshInterval = null; } } }, { immediate: true }); } // Initialize auth state if token exists if (import.meta.client && tokenCookie.value && authStatus.value === "unauthenticated") { getSession().catch((error: any) => { handleAuthError(error); }); } return { data: computed(() => authUser.value), status: computed(() => authStatus.value), signIn, signOut, refresh, getSession, setToken, }; };