import { useEffect, useState, useCallback, type ReactNode } from "react"; import { AuthContext, type AuthContextValue, type AuthStatus } from "./useAuth"; import { authApi } from "./api"; import type { User } from "@/types"; // Re-export useAuth so consumers can import both the context provider and // the hook from the same module. The hook itself lives in `./useAuth` so // the type definitions stay tree-shakeable independent of the provider. export { useAuth } from "./useAuth"; /** * Wraps the app and exposes the auth context. On mount it probes * /api/auth/me to decide whether the existing `cyclone_session` * cookie is still valid. Callers (route guard, sidebar, RoleGate, * Login page) read the resulting `status` + `user` via `useAuth()`. * * - "loading" → while the probe is in flight * - "authenticated" → probe succeeded; user populated * - "unauthenticated"→ probe failed; user is null * * `login` and `logout` mutate local state directly so the UI flips * without waiting for a second /me round-trip. */ export function AuthProvider({ children }: { children: ReactNode }) { const [status, setStatus] = useState("loading"); const [user, setUser] = useState(null); const refresh = useCallback(async () => { setStatus("loading"); try { const me = await authApi.me(); setUser(me as User); setStatus("authenticated"); } catch { setUser(null); setStatus("unauthenticated"); } }, []); useEffect(() => { void refresh(); }, [refresh]); const login = useCallback(async (username: string, password: string) => { const u = await authApi.login(username, password); setUser(u as User); setStatus("authenticated"); }, []); const logout = useCallback(async () => { await authApi.logout().catch(() => undefined); setUser(null); setStatus("unauthenticated"); }, []); const value: AuthContextValue = { status, user, login, logout, refresh }; return {children}; }