Spaces:
Sleeping
Sleeping
| import { writable, derived } from 'svelte/store'; | |
| import type { AuthState } from '$lib/types/client'; | |
| import { STORAGE_KEY_AUTH_TOKEN, STORAGE_KEY_USER_PROFILE } from '$lib/utils/constants'; | |
| import { getItem, setItem, removeItem } from '$lib/services/storage'; | |
| /** | |
| * Auth store managing user authentication state | |
| * Persists token and user info to localStorage | |
| */ | |
| // Create initial state from localStorage if available | |
| function createInitialState(): AuthState { | |
| const token = getItem<string>(STORAGE_KEY_AUTH_TOKEN); | |
| const user = getItem<AuthState['user']>(STORAGE_KEY_USER_PROFILE); | |
| if (token && user) { | |
| // Check if token is expired | |
| const tokenExpiry = user.tokenExpiry; | |
| if (tokenExpiry && Date.now() < tokenExpiry) { | |
| return { | |
| user, | |
| token, | |
| tokenExpiry, | |
| isAuthenticated: true | |
| }; | |
| } | |
| } | |
| return { | |
| user: null, | |
| token: null, | |
| tokenExpiry: null, | |
| isAuthenticated: false | |
| }; | |
| } | |
| // Create the auth store | |
| function createAuthStore() { | |
| const { subscribe, set, update } = writable<AuthState>(createInitialState()); | |
| return { | |
| subscribe, | |
| /** | |
| * Set authenticated user and token | |
| */ | |
| login: (user: NonNullable<AuthState['user']>, token: string, tokenExpiry?: number) => { | |
| const expiry = tokenExpiry || Date.now() + 24 * 60 * 60 * 1000; // Default 24h | |
| const newState: AuthState = { | |
| user: { ...user, tokenExpiry: expiry }, | |
| token, | |
| tokenExpiry: expiry, | |
| isAuthenticated: true | |
| }; | |
| // Persist to storage | |
| setItem(STORAGE_KEY_AUTH_TOKEN, token); | |
| setItem(STORAGE_KEY_USER_PROFILE, newState.user); | |
| set(newState); | |
| }, | |
| /** | |
| * Clear authentication state | |
| */ | |
| logout: () => { | |
| // Clear storage | |
| removeItem(STORAGE_KEY_AUTH_TOKEN); | |
| removeItem(STORAGE_KEY_USER_PROFILE); | |
| set({ | |
| user: null, | |
| token: null, | |
| tokenExpiry: null, | |
| isAuthenticated: false | |
| }); | |
| }, | |
| /** | |
| * Update user profile | |
| */ | |
| updateUser: (user: NonNullable<AuthState['user']>) => { | |
| update((state) => { | |
| if (!state.isAuthenticated) return state; | |
| const newUser = { ...state.user, ...user }; | |
| setItem(STORAGE_KEY_USER_PROFILE, newUser); | |
| return { | |
| ...state, | |
| user: newUser | |
| }; | |
| }); | |
| }, | |
| /** | |
| * Check if token is expired and logout if needed | |
| */ | |
| checkExpiration: () => { | |
| update((state) => { | |
| if (!state.isAuthenticated || !state.tokenExpiry) return state; | |
| if (Date.now() >= state.tokenExpiry) { | |
| // Token expired - clear state | |
| removeItem(STORAGE_KEY_AUTH_TOKEN); | |
| removeItem(STORAGE_KEY_USER_PROFILE); | |
| return { | |
| user: null, | |
| token: null, | |
| tokenExpiry: null, | |
| isAuthenticated: false | |
| }; | |
| } | |
| return state; | |
| }); | |
| } | |
| }; | |
| } | |
| export const authStore = createAuthStore(); | |
| // Derived store for checking if authenticated | |
| export const isAuthenticated = derived( | |
| authStore, | |
| ($auth) => $auth.isAuthenticated | |
| ); | |
| // Derived store for getting current user | |
| export const currentUser = derived( | |
| authStore, | |
| ($auth) => $auth.user | |
| ); | |
| // Check token expiration every minute | |
| if (typeof window !== 'undefined') { | |
| setInterval(() => { | |
| authStore.checkExpiration(); | |
| }, 60 * 1000); | |
| } | |