Files
service-finder/frontend/src/services/api.js

145 lines
5.5 KiB
JavaScript

import axios from 'axios'
// Create axios instance with base URL from environment variable
const api = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL || 'https://dev.servicefinder.hu',
headers: {
'Content-Type': 'application/json',
},
})
// Request interceptor to add auth token
api.interceptors.request.use(
(config) => {
console.group('📤 Axios Request Interceptor - Flight Recorder');
console.log('📊 Request Details:');
console.log(' • URL:', config.url);
console.log(' • Method:', config.method);
console.log(' • Base URL:', config.baseURL);
// Get token from localStorage (check both 'token' and 'access_token' for compatibility)
if (typeof window !== 'undefined') {
let token = localStorage.getItem('token');
if (!token) {
token = localStorage.getItem('access_token');
if (token) {
console.log('⚠️ Using access_token (legacy) instead of token');
}
}
if (token) {
console.log('🔐 Adding Authorization header with token');
console.log(' • Token present:', token.substring(0, 20) + '...');
config.headers.Authorization = `Bearer ${token}`;
} else {
console.log('⚠️ No auth token found in localStorage');
console.log(' • token key:', localStorage.getItem('token') ? 'PRESENT' : 'MISSING');
console.log(' • access_token key:', localStorage.getItem('access_token') ? 'PRESENT' : 'MISSING');
}
} else {
console.log('🌐 Window not available (SSR)');
}
console.groupEnd();
return config;
},
(error) => {
console.error('❌ Request interceptor error:', error);
return Promise.reject(error);
}
)
// Response interceptor for error handling
api.interceptors.response.use(
(response) => response,
(error) => {
console.group('🚨 Axios Response Interceptor - Flight Recorder');
console.log('📊 Interceptor triggered for error:', error);
if (error.response) {
console.log('📡 Response Details:');
console.log(' • Status:', error.response.status);
console.log(' • URL:', error.config?.url);
console.log(' • Method:', error.config?.method);
console.log(' • Headers:', error.config?.headers);
if (error.response.status === 401) {
console.warn('🔐 401 UNAUTHORIZED DETECTED!');
console.warn(' ↳ This will trigger logout and redirect');
// Log current auth state before clearing
const token = localStorage.getItem('token');
const accessToken = localStorage.getItem('access_token');
console.log(' ↳ Current localStorage state:');
console.log(' - token:', token ? `YES (${token.substring(0, 20)}...)` : 'NO');
console.log(' - access_token:', accessToken ? `YES (${accessToken.substring(0, 20)}...)` : 'NO');
// Handle unauthorized - clear ALL auth tokens and redirect to login
if (typeof window !== 'undefined') {
console.log(' ↳ Clearing auth tokens from localStorage...');
localStorage.removeItem('token');
localStorage.removeItem('access_token');
localStorage.removeItem('refresh_token');
localStorage.removeItem('is_admin');
localStorage.removeItem('user_email');
localStorage.removeItem('user_role');
// Also try to call auth store logout if available
try {
const authStore = window.__pinia?.state.value?.auth;
if (authStore && authStore.logout) {
console.log(' ↳ Calling auth store logout()...');
// We can't call the function directly from here, but we can dispatch an event
window.dispatchEvent(new CustomEvent('force-logout'));
}
} catch (e) {
console.warn(' ↳ Could not access auth store:', e.message);
}
console.warn(' ↳ Redirecting to /login');
window.location.href = '/login';
}
} else if (error.response.status === 403) {
console.warn('🔒 403 FORBIDDEN DETECTED');
console.warn(' ↳ User lacks permissions for this resource');
} else if (error.response.status === 404) {
console.warn('🔍 404 NOT FOUND DETECTED');
console.warn(' ↳ API endpoint or resource not found');
} else if (error.response.status >= 500) {
console.error('💥 SERVER ERROR DETECTED (5xx)');
console.error(' ↳ Backend server issue');
}
} else if (error.request) {
console.error('🌐 NETWORK ERROR: Request was made but no response received');
console.error(' ↳ Possible network issue or CORS problem');
} else {
console.error('⚙️ SETUP ERROR: Error in request configuration');
console.error(' ↳', error.message);
}
console.groupEnd();
return Promise.reject(error);
}
)
export default api
// Catalog API functions
export const catalogApi = {
async getMakes() {
const response = await api.get('/catalog/makes')
return response.data
},
async getModels(make) {
const response = await api.get('/catalog/models', { params: { make } })
return response.data
},
async getGenerations(make, model) {
const response = await api.get('/catalog/generations', { params: { make, model } })
return response.data
},
async getEngines(make, model, gen) {
const response = await api.get('/catalog/engines', { params: { make, model, gen } })
return response.data
}
}