import { createContext, useContext, useState, useEffect, ReactNode, useCallback } from 'react'; import { useNavigate } from 'react-router-dom'; import { useLogin, useLogout, useGetMe } from '../generated/anthoLumeAPIV1'; interface AuthState { isAuthenticated: boolean; user: { username: string; is_admin: boolean } | null; isCheckingAuth: boolean; } interface AuthContextType extends AuthState { login: (_username: string, _password: string) => Promise; logout: () => void; } const AuthContext = createContext(undefined); export function AuthProvider({ children }: { children: ReactNode }) { const [authState, setAuthState] = useState({ isAuthenticated: false, user: null, isCheckingAuth: true, // Start with checking state to prevent redirects during initial load }); const loginMutation = useLogin(); const logoutMutation = useLogout(); // Always call /me to check authentication status const { data: meData, error: meError, isLoading: meLoading } = useGetMe(); const navigate = useNavigate(); // Update auth state based on /me endpoint response useEffect(() => { setAuthState(prev => { if (meLoading) { // Still checking authentication console.log('[AuthContext] Checking authentication status...'); return { ...prev, isCheckingAuth: true }; } else if (meData?.data && meData.status === 200) { // User is authenticated - check that response has valid data console.log('[AuthContext] User authenticated:', meData.data); return { isAuthenticated: true, user: meData.data, isCheckingAuth: false, }; } else if ( meError || (meData && meData.status === 401) || (meData && meData.status === 403) ) { // User is not authenticated or error occurred console.log('[AuthContext] User not authenticated:', meError?.message || String(meError)); return { isAuthenticated: false, user: null, isCheckingAuth: false, }; } console.log('[AuthContext] Unexpected state - checking...'); return { ...prev, isCheckingAuth: false }; // Assume not authenticated if we can't determine }); }, [meData, meError, meLoading]); const login = useCallback( async (username: string, password: string) => { try { const response = await loginMutation.mutateAsync({ data: { username, password, }, }); // The backend uses session-based authentication, so no token to store // The session cookie is automatically set by the browser setAuthState({ isAuthenticated: true, user: response.data, isCheckingAuth: false, }); navigate('/'); } catch (_error) { console.error('[AuthContext] Login failed:', _error); throw new Error('Login failed'); } }, [loginMutation, navigate] ); const logout = useCallback(() => { logoutMutation.mutate(undefined, { onSuccess: () => { setAuthState({ isAuthenticated: false, user: null, isCheckingAuth: false, }); navigate('/login'); }, }); }, [logoutMutation, navigate]); return ( {children} ); } export function useAuth() { const context = useContext(AuthContext); if (context === undefined) { throw new Error('useAuth must be used within an AuthProvider'); } return context; }