TypeScript · 3303 bytes Raw Blame History
1 import { defineStore } from 'pinia'
2 import { ref, computed } from 'vue'
3 import { apiClient } from '@/services/api'
4 import type { AuthRequest, AuthResponse } from '@shared/types'
5
6 export const useAuthStore = defineStore('auth', () => {
7 // State
8 const user = ref<{ id: string; username: string } | null>(null)
9 const token = ref<string | null>(null)
10 const refreshToken = ref<string | null>(null)
11 const loading = ref(false)
12 const error = ref<string | null>(null)
13 const initialized = ref(false)
14
15 // Getters
16 const isAuthenticated = computed(() => !!token.value && !!user.value)
17
18 // Actions
19 async function login(credentials: AuthRequest): Promise<void> {
20 loading.value = true
21 error.value = null
22
23 try {
24 const response = await apiClient.login(credentials)
25 await setAuthData(response)
26 } catch (err: any) {
27 error.value = err.response?.data?.message || 'Login failed'
28 throw err
29 } finally {
30 loading.value = false
31 }
32 }
33
34 async function logout(): Promise<void> {
35 loading.value = true
36
37 try {
38 await apiClient.logout()
39 } catch (err) {
40 // Ignore logout errors
41 console.warn('Logout request failed:', err)
42 } finally {
43 clearAuthData()
44 loading.value = false
45 }
46 }
47
48 async function refreshAuthToken(): Promise<void> {
49 if (!refreshToken.value) {
50 throw new Error('No refresh token available')
51 }
52
53 try {
54 const response = await apiClient.refreshAuth(refreshToken.value)
55 await setAuthData(response)
56 } catch (err) {
57 clearAuthData()
58 throw err
59 }
60 }
61
62 async function initializeAuth(): Promise<void> {
63 if (initialized.value) return
64
65 // Check for stored tokens
66 const storedToken = localStorage.getItem('auth_token')
67 const storedRefreshToken = localStorage.getItem('refresh_token')
68
69 if (storedToken && storedRefreshToken) {
70 token.value = storedToken
71 refreshToken.value = storedRefreshToken
72
73 try {
74 // Verify token by fetching user info
75 user.value = await apiClient.getCurrentUser()
76 } catch (err) {
77 // Token is invalid, try to refresh
78 try {
79 await refreshAuthToken()
80 } catch (refreshErr) {
81 clearAuthData()
82 }
83 }
84 }
85
86 initialized.value = true
87 }
88
89 async function setAuthData(authResponse: AuthResponse): Promise<void> {
90 token.value = authResponse.token
91 refreshToken.value = authResponse.refreshToken
92 user.value = authResponse.user
93
94 // Persist to localStorage
95 localStorage.setItem('auth_token', authResponse.token)
96 localStorage.setItem('refresh_token', authResponse.refreshToken)
97
98 // Clear any previous errors
99 error.value = null
100 }
101
102 function clearAuthData(): void {
103 user.value = null
104 token.value = null
105 refreshToken.value = null
106
107 // Clear from localStorage
108 localStorage.removeItem('auth_token')
109 localStorage.removeItem('refresh_token')
110 }
111
112 function clearError(): void {
113 error.value = null
114 }
115
116 return {
117 // State
118 user,
119 token,
120 refreshToken,
121 loading,
122 error,
123 initialized,
124
125 // Getters
126 isAuthenticated,
127
128 // Actions
129 login,
130 logout,
131 refreshAuthToken,
132 initializeAuth,
133 setAuthData,
134 clearAuthData,
135 clearError,
136 }
137 })