// /composables/useAuth.ts interface User { id: number; email: string | null; // Can be null for anonymous users nickname: string | null; avatar: string | null; coins: number; exp: number; dailyStreak: number; soundOn: boolean; confettiOn: boolean; createdAt: string; updatedAt: string; isAnonymous?: boolean; // Flag to distinguish anonymous users anonymousSessionId?: string; } export function useAuth() { const user = useState('user', () => null); const initialized = useState('auth_initialized', () => false); const api = useApi(); // A user is fully authenticated only if they exist and are NOT anonymous. const isAuthenticated = computed(() => !!user.value && !user.value.isAnonymous); // A user is anonymous if they exist and have the isAnonymous flag. const isAnonymous = computed(() => !!user.value && !!user.value.isAnonymous); /** * Initializes the authentication state for EXISTING users. * It should be called once in app.vue. * It will only try to fetch a logged-in user via /api/auth/me. * If it fails, the user state remains null. */ const initAuth = async () => { if (initialized.value) return; try { const response = await api<{ user: User }>('/auth/me'); if (response.user) { user.value = { ...response.user, isAnonymous: false }; } else { user.value = null; } } catch (error) { // It's expected this will fail for non-logged-in users. user.value = null; } finally { initialized.value = true; } }; /** * Starts the onboarding process by creating a new anonymous user. */ const startOnboarding = async () => { try { const anonymousUserData = await api('/onboarding/initiate', { method: 'POST' }); // Explicitly set isAnonymous to true for robustness user.value = { ...anonymousUserData, isAnonymous: true }; } catch (anonError) { console.error('Could not initiate anonymous session:', anonError); // Optionally, show an error message to the user user.value = null; } }; const register = async (email, password, nickname) => { await api('/auth/register', { method: 'POST', body: { email, password, nickname }, }); // After a successful registration, force a re-fetch of the new user state. initialized.value = false; await initAuth(); }; const login = async (email, password) => { await api('/auth/login', { method: 'POST', body: { email, password }, }); // After a successful login, force a re-fetch of the new user state. initialized.value = false; await initAuth(); }; const logout = async () => { try { await api('/auth/logout', { method: 'POST' }); } finally { user.value = null; await navigateTo('/'); } }; const updateUser = (partialUser: Partial) => { if (user.value) { user.value = { ...user.value, ...partialUser }; } }; return { user, isAuthenticated, isAnonymous, // Expose this new state initialized, initAuth, // Called from app.vue startOnboarding, // Called from index.vue register, // Expose register function login, logout, updateUser, }; }