sto-k-odnomu/admin/src/stores/authStore.ts
2026-01-06 23:12:36 +03:00

146 lines
4.4 KiB
TypeScript

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<AuthStore>()(
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
}
},
}
)
)