Various Improvements.

- Testing Coverage
- Image Handling
- Session Handling
- Library Switching
This commit is contained in:
Stefan Hardegger
2025-10-20 08:24:29 +02:00
parent 20d0652c85
commit 30c0132a92
26 changed files with 5810 additions and 75 deletions

View File

@@ -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);
}
);