Various Improvements.
- Testing Coverage - Image Handling - Session Handling - Library Switching
This commit is contained in:
@@ -28,29 +28,103 @@ export const setGlobalAuthFailureHandler = (handler: () => void) => {
|
||||
globalAuthFailureHandler = handler;
|
||||
};
|
||||
|
||||
// Response interceptor to handle auth errors
|
||||
// Flag to prevent multiple simultaneous refresh attempts
|
||||
let isRefreshing = false;
|
||||
let failedQueue: Array<{ resolve: (value?: any) => void; reject: (reason?: any) => void }> = [];
|
||||
|
||||
const processQueue = (error: any = null) => {
|
||||
failedQueue.forEach(prom => {
|
||||
if (error) {
|
||||
prom.reject(error);
|
||||
} else {
|
||||
prom.resolve();
|
||||
}
|
||||
});
|
||||
|
||||
failedQueue = [];
|
||||
};
|
||||
|
||||
// Response interceptor to handle auth errors and token refresh
|
||||
api.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
async (error) => {
|
||||
const originalRequest = error.config;
|
||||
|
||||
// Handle authentication failures
|
||||
if (error.response?.status === 401 || error.response?.status === 403) {
|
||||
console.warn('Authentication failed, token may be expired or invalid');
|
||||
|
||||
// Clear invalid token
|
||||
localStorage.removeItem('auth-token');
|
||||
|
||||
// Use global handler if available (from AuthContext), otherwise fallback to direct redirect
|
||||
if (globalAuthFailureHandler) {
|
||||
globalAuthFailureHandler();
|
||||
} else {
|
||||
// Fallback for cases where AuthContext isn't available
|
||||
window.location.href = '/login';
|
||||
// Don't attempt refresh for login or refresh endpoints
|
||||
if (originalRequest.url?.includes('/auth/login') || originalRequest.url?.includes('/auth/refresh')) {
|
||||
console.warn('Authentication failed on login/refresh endpoint');
|
||||
localStorage.removeItem('auth-token');
|
||||
|
||||
if (globalAuthFailureHandler) {
|
||||
globalAuthFailureHandler();
|
||||
} else {
|
||||
window.location.href = '/login';
|
||||
}
|
||||
|
||||
return Promise.reject(new Error('Authentication required'));
|
||||
}
|
||||
|
||||
// If already retried, don't try again
|
||||
if (originalRequest._retry) {
|
||||
console.warn('Token refresh failed, logging out');
|
||||
localStorage.removeItem('auth-token');
|
||||
|
||||
if (globalAuthFailureHandler) {
|
||||
globalAuthFailureHandler();
|
||||
} else {
|
||||
window.location.href = '/login';
|
||||
}
|
||||
|
||||
return Promise.reject(new Error('Authentication required'));
|
||||
}
|
||||
|
||||
// If already refreshing, queue this request
|
||||
if (isRefreshing) {
|
||||
return new Promise((resolve, reject) => {
|
||||
failedQueue.push({ resolve, reject });
|
||||
}).then(() => {
|
||||
return api(originalRequest);
|
||||
}).catch((err) => {
|
||||
return Promise.reject(err);
|
||||
});
|
||||
}
|
||||
|
||||
originalRequest._retry = true;
|
||||
isRefreshing = true;
|
||||
|
||||
try {
|
||||
// Attempt to refresh the token
|
||||
const response = await api.post('/auth/refresh');
|
||||
|
||||
if (response.data.token) {
|
||||
// Update stored token
|
||||
localStorage.setItem('auth-token', response.data.token);
|
||||
|
||||
// Process queued requests
|
||||
processQueue();
|
||||
|
||||
// Retry original request
|
||||
return api(originalRequest);
|
||||
}
|
||||
} catch (refreshError) {
|
||||
// Refresh failed, log out user
|
||||
processQueue(refreshError);
|
||||
localStorage.removeItem('auth-token');
|
||||
|
||||
if (globalAuthFailureHandler) {
|
||||
globalAuthFailureHandler();
|
||||
} else {
|
||||
window.location.href = '/login';
|
||||
}
|
||||
|
||||
return Promise.reject(new Error('Authentication required'));
|
||||
} finally {
|
||||
isRefreshing = false;
|
||||
}
|
||||
|
||||
// Return a more specific error for components to handle gracefully
|
||||
return Promise.reject(new Error('Authentication required'));
|
||||
}
|
||||
|
||||
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user