import { API_ENDPOINTS } from '@/config/api-config' import { api } from '@/services/api' import { defineStore } from 'pinia' import { ref, computed } from 'vue' import router from '@/router' export interface AuthState { accessToken: string | null refreshToken: string | null user: { email: string name: string id?: string | number is_guest?: boolean avatarUrl?: string } | null } export const useAuthStore = defineStore('auth', () => { // State const accessToken = ref(localStorage.getItem('token')) const refreshToken = ref(localStorage.getItem('refreshToken')) const user = ref(null) const isRefreshing = ref(false) const fetchingUser = ref(false) // Getters const isAuthenticated = computed(() => !!accessToken.value) const getUser = computed(() => user.value) const isGuest = computed(() => user.value?.is_guest ?? false) // Actions const setTokens = (tokens: { access_token: string; refresh_token?: string }) => { try { accessToken.value = tokens.access_token localStorage.setItem('token', tokens.access_token) if (tokens.refresh_token) { refreshToken.value = tokens.refresh_token localStorage.setItem('refreshToken', tokens.refresh_token) } } catch (error) { console.error('Error setting tokens:', error) clearTokens() } } const clearTokens = () => { try { accessToken.value = null refreshToken.value = null user.value = null localStorage.removeItem('token') localStorage.removeItem('refreshToken') isRefreshing.value = false fetchingUser.value = false } catch (error) { console.error('Error clearing tokens:', error) } } const setUser = (userData: AuthState['user']) => { user.value = userData } const fetchCurrentUser = async () => { if (!accessToken.value) { // No token, so definitely clear any residual state and return. clearTokens() return null } // Prevent multiple simultaneous user fetch calls if (fetchingUser.value) { return user.value } fetchingUser.value = true try { const response = await api.get(API_ENDPOINTS.USERS.PROFILE) setUser(response.data) return response.data } catch (error: any) { // Check if the error is from an Axios request and has a response status if (error.isAxiosError && error.response) { const status = error.response.status if (status === 401 || status === 403) { // Only clear tokens if we're not currently refreshing // The API interceptor will handle the refresh if (!isRefreshing.value) { console.error('Authentication error fetching user, tokens will be handled by interceptor:', error) // Don't clear tokens here - let the API interceptor handle it // This prevents race conditions where tokens get cleared while refresh is happening } } else { // Other HTTP error, log it but don't clear tokens. console.error('HTTP error fetching user, token preserved:', error) } } else { // Network error (offline) or other non-HTTP error. // Log the error but preserve tokens. console.error('Network or other error fetching user, token preserved:', error) } return null } finally { fetchingUser.value = false } } const login = async (email: string, password: string) => { const formData = new FormData() formData.append('username', email) formData.append('password', password) const response = await api.post(API_ENDPOINTS.AUTH.LOGIN, formData, { headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, }) const { access_token, refresh_token } = response.data setTokens({ access_token, refresh_token }) await fetchCurrentUser() return response.data } const loginAsGuest = async () => { const response = await api.post(API_ENDPOINTS.AUTH.GUEST) const { access_token, refresh_token } = response.data setTokens({ access_token, refresh_token }) await fetchCurrentUser() return response.data } const signup = async (userData: { name: string; email: string; password: string }) => { const response = await api.post(API_ENDPOINTS.AUTH.SIGNUP, userData) return response.data } const logout = async () => { clearTokens() await router.push('/auth/login') } const requestMagicLink = async (email: string) => { const response = await api.post(API_ENDPOINTS.AUTH.MAGIC_LINK, { email }) return response.data as { detail: string; token: string } } const verifyMagicLink = async (token: string) => { const response = await api.get(API_ENDPOINTS.AUTH.MAGIC_LINK_VERIFY, { params: { token } }) const { access_token, refresh_token } = response.data setTokens({ access_token, refresh_token }) await fetchCurrentUser() return response.data } return { accessToken, user, refreshToken, isAuthenticated, getUser, isGuest, isRefreshing, fetchingUser, setTokens, clearTokens, setUser, fetchCurrentUser, login, loginAsGuest, signup, logout, requestMagicLink, verifyMagicLink, } })