app/lib/authContext.tsx (136 lines of code) (raw):

import React, { createContext, useContext, useState, useEffect, useCallback, useMemo, } from "react"; import { AuthService, AuthStatus } from "./authService"; interface AuthContextType { authStatus: AuthStatus; userInfo: { username: string; fullName: string; avatarUrl: string; } | null; isLoading: boolean; login: (credentials: { openaiApiKey?: string; huggingfaceToken?: string; }) => Promise<{ success: boolean; error?: string }>; logout: () => Promise<void>; refreshAuth: () => Promise<void>; } const AuthContext = createContext<AuthContextType | undefined>(undefined); export const useAuth = () => { const context = useContext(AuthContext); if (context === undefined) { throw new Error("useAuth must be used within an AuthProvider"); } return context; }; interface AuthProviderProps { children: React.ReactNode; } export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => { // The AuthStatus state is used to track authentication status const [authStatus, setAuthStatus] = useState<AuthStatus>({ isAuthenticated: false, hasOpenAI: false, hasHuggingFace: false, }); const [userInfo, setUserInfo] = useState<{ username: string; fullName: string; avatarUrl: string; } | null>(null); const [isLoading, setIsLoading] = useState(true); const refreshAuth = useCallback(async () => { try { const status = await AuthService.getAuthStatus(); setAuthStatus(status); // Get user info if authenticated if (status.isAuthenticated) { // Use user info from status if available if (status.hfUserInfo) { setUserInfo(status.hfUserInfo); } else { // Fallback to getCredentials if user info not in status try { const credentials = await AuthService.getCredentials(); if (credentials?.hfUserInfo) { setUserInfo(credentials.hfUserInfo); } else { setUserInfo(null); } } catch (error) { console.error("Error fetching user info:", error); setUserInfo(null); } } } else { setUserInfo(null); } } catch (error) { console.error("Error fetching auth status:", error); setAuthStatus({ isAuthenticated: false, hasOpenAI: false, hasHuggingFace: false, }); setUserInfo(null); } finally { setIsLoading(false); } }, []); const login = useCallback( async (credentials: { openaiApiKey?: string; huggingfaceToken?: string; }) => { try { const result = await AuthService.authenticate(credentials); if (result.success) { // Wait a bit for the cookie to be set properly, then refresh setTimeout(() => { refreshAuth(); }, 100); return { success: true }; } else { return { success: false, error: result.error }; } } catch (error) { return { success: false, error: "Authentication failed" }; } }, [refreshAuth] ); const logout = useCallback(async () => { try { await AuthService.logout(); setAuthStatus({ isAuthenticated: false, hasOpenAI: false, hasHuggingFace: false, }); setUserInfo(null); } catch (error) { console.error("Error during logout:", error); } }, []); // Initialize auth status on mount useEffect(() => { refreshAuth(); // Check auth status periodically (every minute) const interval = setInterval(() => { refreshAuth(); }, 60000); return () => clearInterval(interval); }, [refreshAuth]); // Memoize the context value to prevent unnecessary re-renders const contextValue = useMemo( () => ({ authStatus, userInfo, isLoading, login, logout, refreshAuth, }), [authStatus, userInfo, isLoading, login, logout, refreshAuth] ); return ( <AuthContext.Provider value={contextValue}>{children}</AuthContext.Provider> ); };