Christian Kniep
new webapp
1fff71f
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);
}