import React, { createContext, useContext, useEffect, useState } from "react"; import { tokenStore } from "./token"; import { createApiClient } from "./axios"; import { AuthUser } from "./models"; export interface AuthServerConfig { serverUrl: string; loginPath: string; registerPath: string; logoutPath: string; mePath: string; introspectPath: string; } interface AuthContextModel { currentUser: AuthUser | null; token: string | null; loading: boolean; error: string | null; login(username: string, password: string): Promise; register(username: string, password: string): Promise; logout(): void; } const AuthContext = createContext(undefined); export function AuthProvider({ children, authConfig, }: { children: React.ReactNode; authConfig: AuthServerConfig; }) { const [currentUser, setCurrentUser] = useState(null); const [token, setToken] = useState(tokenStore.get()); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const auth = createApiClient(authConfig.serverUrl); const login = async (username: string, password: string) => { try { setLoading(true); setError(null); const res = await auth.post(authConfig.loginPath, { username, password }); const { access_token, user } = res.data; tokenStore.set(access_token); setToken(access_token); setCurrentUser(user); } catch (e: any) { setError(e.response?.data?.detail ?? "Login failed"); } finally { setLoading(false); } }; const register = async (username: string, password: string) => { try { setLoading(true); setError(null); await auth.post(authConfig.registerPath, { username, password }); await login(username, password); } catch (e: any) { setError(e.response?.data?.detail ?? "Registration failed"); } finally { setLoading(false); } }; const logout = async () => { try { await auth.post(authConfig.logoutPath); } catch { // Server logout is best-effort; clear token locally regardless } tokenStore.clear(); setToken(null); setCurrentUser(null); }; const fetchCurrentUser = async () => { if (!token) return; try { const me = await auth.get(authConfig.mePath); setCurrentUser({ ...me.data }); } catch { tokenStore.clear(); setToken(null); setCurrentUser(null); } }; useEffect(() => { fetchCurrentUser(); }, [token]); return ( {children} ); } export function useAuth(): AuthContextModel { const ctx = useContext(AuthContext); if (!ctx) throw new Error("useAuth must be used inside AuthProvider"); return ctx; }