Lin / frontend /src /services /apiClient.js
Zelyanoth's picture
feat: enhance CORS and auth handling for production
baaf93b
raw
history blame
4.93 kB
import axios from 'axios';
import cookieService from '../services/cookieService';
// Create axios instance with default config
const API_BASE_URL = import.meta.env.VITE_API_URL ||
(import.meta.env.VITE_NODE_ENV === 'production' ?
'https://zelyanoth-lin-cbfcff2.hf.space' :
'http://localhost:5000');
console.log('API_BASE_URL:', API_BASE_URL);
// Ensure API_BASE_URL ends with /api for consistency
const normalizedBaseUrl = API_BASE_URL.endsWith('/api') ? API_BASE_URL : `${API_BASE_URL}/api`;
const apiClient = axios.create({
baseURL: normalizedBaseUrl,
timeout: 30000,
headers: {
'Content-Type': 'application/json',
},
withCredentials: true, // Send cookies with requests
});
// Request interceptor to add auth token
apiClient.interceptors.request.use(
async (config) => {
// Enhanced logging for debugging
const isDevelopment = import.meta.env.VITE_NODE_ENV === 'development';
if (isDevelopment) {
console.log('πŸš€ [API Request]', {
method: config.method?.toUpperCase(),
url: config.baseURL + config.url,
headers: config.headers,
data: config.data
});
}
// Get token from cookie service with fallback to localStorage
let token = null;
try {
const tokens = await cookieService.getAuthTokens();
token = tokens?.accessToken;
} catch (error) {
console.warn('πŸͺ [Cookie] Error getting auth tokens, trying localStorage:', error.message);
token = localStorage.getItem('token');
}
if (token) {
config.headers.Authorization = `Bearer ${token}`;
if (isDevelopment) {
console.log('πŸ”‘ [Token] Added token to request headers');
}
} else {
if (isDevelopment) {
console.log('πŸ”‘ [Token] No token found for request');
}
}
return config;
},
(error) => {
console.error('❌ [API Request Error]', error);
return Promise.reject(error);
}
);
// Response interceptor to handle token refresh and errors
apiClient.interceptors.response.use(
(response) => {
// Enhanced logging for debugging
const isDevelopment = import.meta.env.VITE_NODE_ENV === 'development';
if (isDevelopment) {
console.log('βœ… [API Response]', {
status: response.status,
method: response.config.method?.toUpperCase(),
url: response.config.baseURL + response.config.url,
data: response.data
});
}
return response;
},
async (error) => {
const isDevelopment = import.meta.env.VITE_NODE_ENV === 'development';
const originalRequest = error.config;
// Enhanced error logging
if (isDevelopment) {
console.error('❌ [API Response Error]', {
status: error.response?.status,
method: originalRequest?.method?.toUpperCase(),
url: originalRequest ? originalRequest.baseURL + originalRequest.url : 'unknown',
message: error.message,
response: error.response?.data,
headers: error.response?.headers
});
}
// Handle 401 Unauthorized errors
if (error.response?.status === 401) {
if (isDevelopment) {
console.log('πŸ” [Auth] 401 error detected, attempting token refresh');
}
// If we haven't retried this request yet
if (!originalRequest._retry) {
originalRequest._retry = true;
try {
// Clear all authentication data
await cookieService.clearAuthTokens();
localStorage.removeItem('token');
if (isDevelopment) {
console.log('πŸ” [Auth] Cleared all authentication data');
}
// Redirect to login page
const currentPath = window.location.pathname;
if (currentPath !== '/login' && currentPath !== '/register') {
if (isDevelopment) {
console.log('πŸ” [Auth] Redirecting to login page');
}
window.location.href = '/login';
}
} catch (refreshError) {
if (isDevelopment) {
console.error('πŸ” [Auth] Error during token refresh:', refreshError);
}
// Even if refresh fails, clear auth data
await cookieService.clearAuthTokens();
localStorage.removeItem('token');
window.location.href = '/login';
return Promise.reject(refreshError);
}
}
}
// Handle network errors
if (!error.response) {
if (isDevelopment) {
console.error('🌐 [Network] No response received:', {
url: originalRequest?.baseURL + originalRequest?.url,
message: error.message
});
}
// You might want to implement offline mode here
// For now, just reject the error
return Promise.reject(error);
}
return Promise.reject(error);
}
);
export default apiClient;