import React, { createContext, useContext, useState, useEffect, useCallback } from 'react'; import type { AppUser } from '../types'; import { setApiToken, setApiOrganizationId, getBaseUrl } from '../services/api'; import { useLogto } from '@logto/react'; const API_URL = getBaseUrl(); export interface AuthContextType { appUser: AppUser | null; isLoading: boolean; isSignedIn: boolean; error: string | null; token: string | null; login: (token: string, user: AppUser) => void; logout: () => void; isAdmin: () => boolean; isUser: () => boolean; isGuest: () => boolean; isDeveloper: () => boolean; canEdit: () => boolean; refetchUser: () => Promise; } export const AuthContext = createContext(undefined); export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { const { isAuthenticated, getAccessToken, signOut, isLoading: isLogtoLoading } = useLogto(); const [appUser, setAppUser] = useState(null); const [token, setToken] = useState(null); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { const fetchTokenAndUser = async () => { if (isAuthenticated) { try { const accessToken = await getAccessToken(import.meta.env.VITE_LOGTO_RESOURCE || 'https://gpi.reifonas.cloud/api'); if (accessToken) { setToken(accessToken); setApiToken(accessToken); } } catch (err) { console.error(err); } } else if (!isLogtoLoading) { setIsLoading(false); } }; fetchTokenAndUser(); }, [isAuthenticated, isLogtoLoading, getAccessToken]); // Initial load: se tem token, setar no interceptor e buscar dados do usuário useEffect(() => { if (token) { refetchUser(); } }, [token]); const login = useCallback((newToken: string, user: AppUser) => { setToken(newToken); setAppUser(user); setApiToken(newToken); if (user.organizationId) { setApiOrganizationId(user.organizationId); } }, []); const logout = useCallback(() => { setToken(null); setAppUser(null); setApiToken(null); setApiOrganizationId(null); signOut(window.location.origin); }, [signOut]); const refetchUser = useCallback(async () => { if (!token) return; setIsLoading(true); try { const response = await fetch(`${API_URL}/auth/me`, { headers: { 'Authorization': `Bearer ${token}` }, }); if (response.ok) { const userData = await response.json(); setAppUser(userData); if (userData.organizationId) { setApiOrganizationId(userData.organizationId); } } else { // Token inválido ou expirado logout(); } } catch (err) { console.error('Error refetching user:', err); setError('Falha na comunicação de autenticação.'); } finally { setIsLoading(false); } }, [token, logout]); const isDeveloper = useCallback(() => { return appUser?.email === 'admtracksteel@gmail.com'; }, [appUser]); const isAdmin = useCallback(() => appUser?.role === 'admin' || isDeveloper(), [appUser, isDeveloper]); const isUser = useCallback(() => appUser?.role === 'user' || isAdmin(), [appUser, isAdmin]); const isGuest = useCallback(() => appUser?.role === 'guest' && !isDeveloper(), [appUser, isDeveloper]); const canEdit = useCallback(() => (appUser?.role !== 'guest' && appUser !== null) || isDeveloper(), [appUser, isDeveloper]); return ( {children} ); }; export const useAuth = () => { const context = useContext(AuthContext); if (!context) { throw new Error('useAuth must be used within an AuthProvider'); } return context; };