Add new Frontend
This commit is contained in:
@@ -1,222 +0,0 @@
|
||||
// frontend/src/contexts/AuthContext.tsx - FIXED VERSION
|
||||
import React, { createContext, useContext, useEffect, useState, useCallback } from 'react';
|
||||
// FIXED: Import authService directly, not through the index
|
||||
import { authService } from '../api/services/authService';
|
||||
import { tokenManager } from '../api/auth/tokenManager';
|
||||
import {
|
||||
UserProfile,
|
||||
RegisterRequest,
|
||||
} from '../api/types/api';
|
||||
|
||||
interface AuthContextType {
|
||||
user: UserProfile | null;
|
||||
isAuthenticated: boolean;
|
||||
isLoading: boolean;
|
||||
login: (email: string, password: string) => Promise<void>;
|
||||
register: (data: RegisterRequest) => Promise<void>;
|
||||
logout: () => Promise<void>;
|
||||
updateProfile: (updates: Partial<UserProfile>) => Promise<void>;
|
||||
refreshUser: () => Promise<void>;
|
||||
}
|
||||
|
||||
const AuthContext = createContext<AuthContextType | null>(null);
|
||||
|
||||
export const useAuth = () => {
|
||||
const context = useContext(AuthContext);
|
||||
if (!context) {
|
||||
throw new Error('useAuth must be used within an AuthProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
const [user, setUser] = useState<UserProfile | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
// Initialize auth state
|
||||
useEffect(() => {
|
||||
const initAuth = async () => {
|
||||
try {
|
||||
await tokenManager.initialize();
|
||||
|
||||
if (authService.isAuthenticated()) {
|
||||
// Get user from token first (faster), then validate with API
|
||||
const tokenUser = tokenManager.getUserFromToken();
|
||||
if (tokenUser) {
|
||||
setUser({
|
||||
id: tokenUser.user_id,
|
||||
email: tokenUser.email,
|
||||
full_name: tokenUser.full_name,
|
||||
is_active: true,
|
||||
is_verified: tokenUser.is_verified,
|
||||
role: 'user', // Default role
|
||||
language: 'es',
|
||||
timezone: 'Europe/Madrid',
|
||||
created_at: '', // Will be filled by API call
|
||||
});
|
||||
}
|
||||
|
||||
// Validate with API and get complete profile
|
||||
try {
|
||||
const profile = await authService.getCurrentUser();
|
||||
setUser(profile);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch user profile:', error);
|
||||
// Keep token-based user data if API fails
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Auth initialization failed:', error);
|
||||
// Clear potentially corrupted tokens
|
||||
tokenManager.clearTokens();
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
initAuth();
|
||||
}, []);
|
||||
|
||||
const login = useCallback(async (email: string, password: string) => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
// Login and store tokens
|
||||
const tokenResponse = await authService.login({ email, password });
|
||||
|
||||
// After login, get user profile
|
||||
const profile = await authService.getCurrentUser();
|
||||
setUser(profile);
|
||||
} catch (error) {
|
||||
setIsLoading(false);
|
||||
throw error; // Re-throw to let components handle the error
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const register = useCallback(async (data: RegisterRequest) => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
// ✅ FIX: Handle registration conflicts properly
|
||||
try {
|
||||
// Try to register first
|
||||
const tokenResponse = await authService.register(data);
|
||||
|
||||
// After successful registration, get user profile
|
||||
const profile = await authService.getCurrentUser();
|
||||
setUser(profile);
|
||||
} catch (registrationError: any) {
|
||||
// ✅ FIX: If user already exists (409), try to login instead
|
||||
if (registrationError.response?.status === 409 ||
|
||||
registrationError.message?.includes('already exists')) {
|
||||
|
||||
console.log('User already exists');
|
||||
|
||||
} else {
|
||||
// If it's not a "user exists" error, re-throw it
|
||||
throw registrationError;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
setIsLoading(false);
|
||||
throw error; // Re-throw to let components handle the error
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const logout = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
await authService.logout();
|
||||
setUser(null);
|
||||
} catch (error) {
|
||||
console.error('Logout error:', error);
|
||||
// Clear local state even if API call fails
|
||||
setUser(null);
|
||||
tokenManager.clearTokens();
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const updateProfile = useCallback(async (updates: Partial<UserProfile>) => {
|
||||
if (!user) return;
|
||||
|
||||
try {
|
||||
const updated = await authService.updateProfile(updates);
|
||||
setUser(updated);
|
||||
} catch (error) {
|
||||
console.error('Profile update error:', error);
|
||||
throw error;
|
||||
}
|
||||
}, [user]);
|
||||
|
||||
const refreshUser = useCallback(async () => {
|
||||
if (!authService.isAuthenticated()) return;
|
||||
|
||||
try {
|
||||
const profile = await authService.getCurrentUser();
|
||||
setUser(profile);
|
||||
} catch (error) {
|
||||
console.error('User refresh error:', error);
|
||||
// If refresh fails with 401, user might need to re-login
|
||||
if (error.status === 401) {
|
||||
await logout();
|
||||
}
|
||||
}
|
||||
}, [logout]);
|
||||
|
||||
// Set up token refresh interval
|
||||
useEffect(() => {
|
||||
if (!user) return;
|
||||
|
||||
const interval = setInterval(async () => {
|
||||
try {
|
||||
await tokenManager.refreshAccessToken();
|
||||
} catch (error) {
|
||||
console.error('Scheduled token refresh failed:', error);
|
||||
// If token refresh fails, user needs to re-login
|
||||
await logout();
|
||||
}
|
||||
}, 60000); // Check every 1 minute
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [user, logout]);
|
||||
|
||||
// Monitor token expiration
|
||||
useEffect(() => {
|
||||
if (!user) return;
|
||||
|
||||
const checkTokenValidity = () => {
|
||||
if (!authService.isAuthenticated()) {
|
||||
console.warn('Token became invalid, logging out user');
|
||||
logout();
|
||||
}
|
||||
};
|
||||
|
||||
// Check token validity every 30 seconds
|
||||
const interval = setInterval(checkTokenValidity, 30000);
|
||||
return () => clearInterval(interval);
|
||||
}, [user, logout]);
|
||||
|
||||
const contextValue = {
|
||||
user,
|
||||
isAuthenticated: !!user && authService.isAuthenticated(),
|
||||
isLoading,
|
||||
login,
|
||||
register,
|
||||
logout,
|
||||
updateProfile,
|
||||
refreshUser,
|
||||
};
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={contextValue}>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
// Export the RegisterRequest type for use in components
|
||||
export type { RegisterRequest };
|
||||
Reference in New Issue
Block a user