71 lines
2.2 KiB
JavaScript
71 lines
2.2 KiB
JavaScript
import axios from 'axios';
|
||
import { API_BASE_URL } from '../utils/constants';
|
||
|
||
const api = axios.create({
|
||
baseURL: `${API_BASE_URL}/api`,
|
||
timeout: 10000, // 10s timeout to prevent hanging requests
|
||
withCredentials: true, // Send cookies with requests (httpOnly cookie authentication)
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
}
|
||
});
|
||
|
||
// Request interceptor - no longer needed for token handling (using httpOnly cookies)
|
||
api.interceptors.request.use(
|
||
(config) => {
|
||
// Cookies are automatically sent with each request
|
||
return config;
|
||
},
|
||
(error) => {
|
||
return Promise.reject(error);
|
||
}
|
||
);
|
||
|
||
// Response interceptor - handle errors with retry logic
|
||
api.interceptors.response.use(
|
||
(response) => {
|
||
return response;
|
||
},
|
||
async (error) => {
|
||
const config = error.config;
|
||
|
||
// Handle auth errors (don't retry)
|
||
if (error.response?.status === 401 || error.response?.status === 403) {
|
||
// Clear stored session data so React re-renders to login state
|
||
sessionStorage.removeItem('user');
|
||
// Notify the app without a hard page navigation (SPA-safe)
|
||
window.dispatchEvent(new CustomEvent('auth:unauthorized'));
|
||
return Promise.reject(error);
|
||
}
|
||
|
||
// Ohne Request-Config (z. B. Fehler schon beim Aufbau der Anfrage) gibt es
|
||
// nichts zu wiederholen – vorher lief das in einen TypeError auf undefined.
|
||
if (!config) {
|
||
return Promise.reject(error);
|
||
}
|
||
|
||
// Retry logic for network errors or 5xx errors
|
||
if (config.retry === undefined) {
|
||
config.retry = 2; // Default: 2 retries
|
||
config.retryCount = 0;
|
||
}
|
||
|
||
const shouldRetry = (
|
||
error.code === 'ECONNABORTED' || // Timeout
|
||
error.code === 'ERR_NETWORK' || // Network error
|
||
(error.response?.status >= 500 && error.response?.status <= 599) // Server errors
|
||
);
|
||
|
||
if (shouldRetry && config.retryCount < config.retry) {
|
||
config.retryCount += 1;
|
||
const backoffDelay = Math.min(1000 * Math.pow(2, config.retryCount - 1), 5000); // Exponential backoff
|
||
await new Promise(resolve => setTimeout(resolve, backoffDelay));
|
||
return api.request(config);
|
||
}
|
||
|
||
return Promise.reject(error);
|
||
}
|
||
);
|
||
|
||
export default api;
|