import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; import { api, ApiError, AUTH_UNAUTHORIZED_EVENT } from '../services/apiClient.ts'; import type { User } from '../types/api.ts'; type AuthContextValue = { user: User | null; loading: boolean; login: (userName: string, password: string) => Promise; logout: () => Promise; }; const AuthContext = createContext(null); export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { const onUnauthorized = () => setUser(null); window.addEventListener(AUTH_UNAUTHORIZED_EVENT, onUnauthorized); return () => window.removeEventListener(AUTH_UNAUTHORIZED_EVENT, onUnauthorized); }, []); useEffect(() => { let cancelled = false; (async () => { try { const me = await api.auth.me(); if (!cancelled) setUser(me); } catch (err) { if ( !cancelled && !(err instanceof ApiError && (err.status === 401 || err.status === 403)) ) { console.error(err); } if (!cancelled) setUser(null); } finally { if (!cancelled) setLoading(false); } })(); return () => { cancelled = true; }; }, []); const login = useCallback(async (userName: string, password: string) => { await api.auth.csrf(); const next = await api.auth.login(userName, password); setUser(next); }, []); const logout = useCallback(async () => { try { await api.auth.logout(); } finally { setUser(null); } }, []); const value = useMemo(() => ({ user, loading, login, logout }), [user, loading, login, logout]); return {children}; }; export function useAuth(): AuthContextValue { const ctx = useContext(AuthContext); if (!ctx) throw new Error('useAuth must be used within AuthProvider'); return ctx; }