import { create } from 'zustand' import { persist } from 'zustand/middleware' import type { UserDto } from '@/types/models' interface AuthState { user: UserDto | null token: string | null refreshToken: string | null expiresIn: number | null tokenExpiresAt: number | null // timestamp when token expires isAuthenticated: boolean isLoading: boolean error: string | null } interface AuthActions { login: (token: string, refreshToken: string | null, expiresIn: number | null, user: UserDto) => void updateToken: (token: string, refreshToken: string, expiresIn: number) => void logout: () => void setLoading: (loading: boolean) => void setError: (error: string | null) => void shouldRefreshToken: () => boolean } type AuthStore = AuthState & AuthActions export const useAuthStore = create()( persist( (set, get) => ({ // State user: null, token: null, refreshToken: null, expiresIn: null, tokenExpiresAt: null, isAuthenticated: false, isLoading: false, error: null, // Actions login: (token: string, refreshToken: string | null, expiresIn: number | null, user: UserDto) => { console.log('[AuthStore] Saving token to localStorage, length:', token.length) localStorage.setItem('admin_token', token) if (refreshToken) { localStorage.setItem('admin_refresh_token', refreshToken) } // Calculate expiration time (refresh 5 minutes before expiration) const tokenExpiresAt = expiresIn ? Date.now() + (expiresIn - 300) * 1000 // 5 minutes buffer : null // Verify it was saved const savedToken = localStorage.getItem('admin_token') console.log('[AuthStore] Token saved, verification:', savedToken ? `Token exists, length: ${savedToken.length}` : 'Token NOT found!') set({ user, token, refreshToken: refreshToken ?? null, expiresIn: expiresIn ?? null, tokenExpiresAt, isAuthenticated: true, isLoading: false, error: null, }) }, updateToken: (token: string, refreshToken: string, expiresIn: number) => { console.log('[AuthStore] Updating token') localStorage.setItem('admin_token', token) localStorage.setItem('admin_refresh_token', refreshToken) const tokenExpiresAt = Date.now() + (expiresIn - 300) * 1000 // 5 minutes buffer set({ token, refreshToken, expiresIn, tokenExpiresAt, }) }, logout: () => { localStorage.removeItem('admin_token') localStorage.removeItem('admin_refresh_token') set({ user: null, token: null, refreshToken: null, expiresIn: null, tokenExpiresAt: null, isAuthenticated: false, isLoading: false, error: null, }) }, setLoading: (isLoading: boolean) => { set({ isLoading }) }, setError: (error: string | null) => { set({ error, isLoading: false }) }, shouldRefreshToken: () => { const state = get() if (!state.refreshToken || !state.token) { return false } // If we have expiresIn but no tokenExpiresAt, calculate it if (state.expiresIn && !state.tokenExpiresAt) { const calculatedExpiresAt = Date.now() + (state.expiresIn - 300) * 1000 set({ tokenExpiresAt: calculatedExpiresAt }) return Date.now() >= calculatedExpiresAt } if (!state.tokenExpiresAt) { return false } // Refresh if token expires in less than 5 minutes return Date.now() >= state.tokenExpiresAt }, }), { name: 'admin-auth', partialize: (state) => ({ user: state.user, token: state.token, refreshToken: state.refreshToken, expiresIn: state.expiresIn, tokenExpiresAt: state.tokenExpiresAt, isAuthenticated: state.isAuthenticated, }), onRehydrateStorage: () => (state) => { // After rehydration, recalculate tokenExpiresAt if needed if (state && state.expiresIn && !state.tokenExpiresAt) { const tokenExpiresAt = Date.now() + (state.expiresIn - 300) * 1000 state.tokenExpiresAt = tokenExpiresAt } }, } ) )