import { createContext, useCallback, useContext, useEffect, useState } from "react"; import { apiGet, apiPost, ApiError } from "../api/client"; const STORAGE_ACCESS = "auth_access"; const STORAGE_REFRESH = "auth_refresh"; const AuthContext = createContext(null); export function AuthProvider({ children }) { const [user, setUser] = useState(null); const [accessToken, setAccessToken] = useState(() => { if (typeof window === "undefined") return null; return localStorage.getItem(STORAGE_ACCESS); }); const [refreshToken, setRefreshToken] = useState(() => { if (typeof window === "undefined") return null; return localStorage.getItem(STORAGE_REFRESH); }); const [loading, setLoading] = useState(true); const persistTokens = useCallback((access, refresh) => { setAccessToken(access); setRefreshToken(refresh); if (typeof window !== "undefined") { if (access) localStorage.setItem(STORAGE_ACCESS, access); else localStorage.removeItem(STORAGE_ACCESS); if (refresh) localStorage.setItem(STORAGE_REFRESH, refresh); else localStorage.removeItem(STORAGE_REFRESH); } }, []); const logout = useCallback(() => { setUser(null); persistTokens(null, null); }, [persistTokens]); const login = useCallback((access, refresh, userData) => { persistTokens(access, refresh); setUser(userData); }, [persistTokens]); // Restore user from token on mount useEffect(() => { if (!accessToken) { setLoading(false); return; } apiGet("/auth/me/", accessToken) .then((data) => { setUser(data); setLoading(false); }) .catch((err) => { const status = ApiError && err instanceof ApiError ? err.status : err?.status; const message = typeof err?.message === "string" ? err.message : ""; const isUnauthorized = status === 401 || message.includes("401"); if (!isUnauthorized) { setLoading(false); return; } // Token invalid, try refresh if (!refreshToken) { logout(); setLoading(false); return; } apiPost("/auth/token/refresh/", { refresh: refreshToken }) .then(({ access }) => { persistTokens(access, refreshToken); return apiGet("/auth/me/", access); }) .then((data) => { setUser(data); }) .catch(() => { logout(); }) .finally(() => { setLoading(false); }); }); }, [accessToken, refreshToken, logout, persistTokens]); const value = { user, accessToken, loading, login, logout, isAuthenticated: !!user, }; return {children}; } export function useAuth() { const ctx = useContext(AuthContext); if (!ctx) { throw new Error("useAuth must be used within AuthProvider"); } return ctx; }