ADD new frontend
This commit is contained in:
205
fdev-ffrontend/src/api/hooks/useAuth.ts
Normal file
205
fdev-ffrontend/src/api/hooks/useAuth.ts
Normal file
@@ -0,0 +1,205 @@
|
||||
// frontend/src/api/hooks/useAuth.ts
|
||||
/**
|
||||
* Authentication Hooks
|
||||
* React hooks for authentication operations
|
||||
*/
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { authService } from '../services';
|
||||
import type {
|
||||
LoginRequest,
|
||||
LoginResponse,
|
||||
RegisterRequest,
|
||||
UserResponse,
|
||||
PasswordResetRequest,
|
||||
} from '../types';
|
||||
|
||||
// Token management
|
||||
const TOKEN_KEY = 'auth_token';
|
||||
const REFRESH_TOKEN_KEY = 'refresh_token';
|
||||
const USER_KEY = 'user_data';
|
||||
|
||||
export const useAuth = () => {
|
||||
const [user, setUser] = useState<UserResponse | null>(null);
|
||||
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
// Initialize auth state from localStorage
|
||||
useEffect(() => {
|
||||
const initializeAuth = async () => {
|
||||
try {
|
||||
const token = localStorage.getItem(TOKEN_KEY);
|
||||
const userData = localStorage.getItem(USER_KEY);
|
||||
|
||||
if (token && userData) {
|
||||
setUser(JSON.parse(userData));
|
||||
setIsAuthenticated(true);
|
||||
|
||||
// Verify token is still valid
|
||||
try {
|
||||
const currentUser = await authService.getCurrentUser();
|
||||
setUser(currentUser);
|
||||
} catch (error) {
|
||||
// Token might be expired - let interceptors handle refresh
|
||||
// Only logout if refresh also fails (handled by ErrorRecoveryInterceptor)
|
||||
console.log('Token verification failed, interceptors will handle refresh if possible');
|
||||
|
||||
// Check if we have a refresh token - if not, logout immediately
|
||||
const refreshToken = localStorage.getItem(REFRESH_TOKEN_KEY);
|
||||
if (!refreshToken) {
|
||||
console.log('No refresh token available, logging out');
|
||||
logout();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Auth initialization error:', error);
|
||||
logout();
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
initializeAuth();
|
||||
}, []);
|
||||
|
||||
const login = useCallback(async (credentials: LoginRequest): Promise<void> => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
const response = await authService.login(credentials);
|
||||
|
||||
// Store tokens and user data
|
||||
localStorage.setItem(TOKEN_KEY, response.access_token);
|
||||
if (response.refresh_token) {
|
||||
localStorage.setItem(REFRESH_TOKEN_KEY, response.refresh_token);
|
||||
}
|
||||
if (response.user) {
|
||||
localStorage.setItem(USER_KEY, JSON.stringify(response.user));
|
||||
setUser(response.user);
|
||||
}
|
||||
|
||||
setIsAuthenticated(true);
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : 'Login failed';
|
||||
setError(message);
|
||||
throw error;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const register = useCallback(async (data: RegisterRequest): Promise<void> => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
const response = await authService.register(data);
|
||||
|
||||
// Auto-login after successful registration
|
||||
if (response && response.user) {
|
||||
await login({ email: data.email, password: data.password });
|
||||
} else {
|
||||
// If response doesn't have user property, registration might still be successful
|
||||
// Try to login anyway in case the user was created but response format is different
|
||||
await login({ email: data.email, password: data.password });
|
||||
}
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : 'Registration failed';
|
||||
setError(message);
|
||||
throw error;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [login]);
|
||||
|
||||
const logout = useCallback(async (): Promise<void> => {
|
||||
try {
|
||||
// Call logout endpoint if authenticated
|
||||
if (isAuthenticated) {
|
||||
await authService.logout();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Logout error:', error);
|
||||
} finally {
|
||||
// Clear local state regardless of API call success
|
||||
localStorage.removeItem(TOKEN_KEY);
|
||||
localStorage.removeItem(REFRESH_TOKEN_KEY);
|
||||
localStorage.removeItem(USER_KEY);
|
||||
setUser(null);
|
||||
setIsAuthenticated(false);
|
||||
setError(null);
|
||||
}
|
||||
}, [isAuthenticated]);
|
||||
|
||||
const updateProfile = useCallback(async (data: Partial<UserResponse>): Promise<void> => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
const updatedUser = await authService.updateProfile(data);
|
||||
setUser(updatedUser);
|
||||
localStorage.setItem(USER_KEY, JSON.stringify(updatedUser));
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : 'Profile update failed';
|
||||
setError(message);
|
||||
throw error;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const requestPasswordReset = useCallback(async (data: PasswordResetRequest): Promise<void> => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
await authService.requestPasswordReset(data);
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : 'Password reset request failed';
|
||||
setError(message);
|
||||
throw error;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const changePassword = useCallback(async (currentPassword: string, newPassword: string): Promise<void> => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
await authService.changePassword(currentPassword, newPassword);
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : 'Password change failed';
|
||||
setError(message);
|
||||
throw error;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return {
|
||||
user,
|
||||
isAuthenticated,
|
||||
isLoading,
|
||||
error,
|
||||
login,
|
||||
register,
|
||||
logout,
|
||||
updateProfile,
|
||||
requestPasswordReset,
|
||||
changePassword,
|
||||
clearError: () => setError(null),
|
||||
};
|
||||
};
|
||||
|
||||
// Hook for getting authentication headers
|
||||
export const useAuthHeaders = () => {
|
||||
const getAuthHeaders = useCallback(() => {
|
||||
const token = localStorage.getItem(TOKEN_KEY);
|
||||
return token ? { Authorization: `Bearer ${token}` } : {};
|
||||
}, []);
|
||||
|
||||
return { getAuthHeaders };
|
||||
};
|
||||
Reference in New Issue
Block a user