mitlist/fe/src/stores/auth.ts
mohamad 229f6b7b1c feat: Introduce activity tracking and management features
This commit adds new functionality for tracking user activities within the application, including:

- Implementation of a new activity service to fetch and manage group activities.
- Creation of a dedicated activity store to handle state management for activities.
- Introduction of new API endpoints for retrieving paginated activity data.
- Enhancements to the UI with new components for displaying activity feeds and items.
- Refactoring of existing components to utilize the new activity features, improving user engagement and interaction.

These changes aim to enhance the application's activity tracking capabilities and provide users with a comprehensive view of their interactions.
2025-06-28 19:14:51 +02:00

178 lines
5.2 KiB
TypeScript

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<string | null>(localStorage.getItem('token'))
const refreshToken = ref<string | null>(localStorage.getItem('refreshToken'))
const user = ref<AuthState['user']>(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,
}
})