Zelyanoth's picture
fff
25f22bf
raw
history blame
4.76 kB
import axios from 'axios'
// Create axios instance
const api = axios.create({
baseURL: import.meta.env.VITE_API_URL || 'http://localhost:5000/api',
headers: {
'Content-Type': 'application/json'
},
// Disable caching for API requests to ensure we always get fresh data
adapter: 'xhr', // Use XHR adapter to avoid service worker caching issues
withCredentials: true // Send cookies with requests
})
// Add request timeout to prevent hanging requests
api.defaults.timeout = 10000 // 10 seconds timeout
// Environment-specific configuration
const isDevelopment = import.meta.env.VITE_NODE_ENV === 'development'
const isProduction = import.meta.env.VITE_NODE_ENV === 'production'
// Debug logging function for development
const logApiCall = (config) => {
if (isDevelopment) {
console.log(`πŸš€ [API] ${config.method?.toUpperCase()} ${config.url}`, {
data: config.data,
params: config.params,
headers: config.headers
})
}
}
// Error logging function for development
const logApiError = (error) => {
if (isDevelopment) {
console.error(`❌ [API Error] ${error.config?.url || 'Unknown URL'}`, {
status: error.response?.status,
statusText: error.response?.statusText,
data: error.response?.data,
message: error.message
})
}
}
// Request interceptor to add auth token and logging
api.interceptors.request.use(
(config) => {
// Add debug logging
logApiCall(config)
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
(error) => {
logApiError(error)
return Promise.reject(error)
}
)
// Add token validation and refresh logic
api.interceptors.request.use(
async (config) => {
const token = localStorage.getItem('token')
if (token) {
try {
// Check if token is expired
const tokenData = JSON.parse(atob(token.split('.')[1]))
const isExpired = tokenData.exp * 1000 < Date.now()
if (isExpired) {
// Token is expired, remove it
localStorage.removeItem('token')
if (isDevelopment) {
console.log('πŸ”‘ [Token] Token expired, removed from storage')
}
// If we're not on the login page, redirect there
if (!window.location.pathname.includes('/login')) {
window.location.href = '/login'
}
} else {
// Token is valid, add to headers
config.headers.Authorization = `Bearer ${token}`
}
} catch (error) {
// Invalid token format, remove it
localStorage.removeItem('token')
if (isDevelopment) {
console.log('πŸ”‘ [Token] Invalid token format, removed from storage')
}
// If we're not on the login page, redirect there
if (!window.location.pathname.includes('/login')) {
window.location.href = '/login'
}
}
}
return config
},
(error) => {
logApiError(error)
return Promise.reject(error)
}
)
// Response interceptor to handle errors and logging
api.interceptors.response.use(
(response) => {
if (isDevelopment) {
console.log(`βœ… [API Response] ${response.config.url}`, {
status: response.status,
data: response.data
})
}
return response
},
(error) => {
logApiError(error)
if (error.response?.status === 401) {
// Only redirect if we're not already on the login page
if (!window.location.pathname.includes('/login')) {
localStorage.removeItem('token')
window.location.href = '/login'
}
}
// Add more detailed error handling for different status codes
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
const status = error.response.status
const data = error.response.data
if (isDevelopment) {
console.error(`🚨 [API Error ${status}]`, {
url: error.config.url,
method: error.config.method,
data: data,
headers: error.response.headers
})
}
} else if (error.request) {
// The request was made but no response was received
if (isDevelopment) {
console.error('🚨 [Network Error] No response received:', {
url: error.config?.url,
message: error.message
})
}
} else {
// Something happened in setting up the request that triggered an Error
if (isDevelopment) {
console.error('🚨 [Request Error]', error.message)
}
}
return Promise.reject(error)
}
)
export default api