import axios from 'axios'; import { toast } from 'sonner'; import { useUserStore } from '../store/userStore'; const getBaseURL = () => { if (process.env.NEXT_PUBLIC_API_URL) return process.env.NEXT_PUBLIC_API_URL; if (typeof window === 'undefined') { return 'http://127.0.0.1:4001/api'; } return process.env.NODE_ENV === 'development' ? 'http://localhost:4001/api' : '/api'; }; const baseURL = getBaseURL(); export interface ApiErrorPayload { success: boolean; statusCode: number; message: string; code: string; details?: Array<{ field: string; message: string }> | Record; timestamp?: string; path?: string; } const api = axios.create({ baseURL, timeout: 10000, headers: { 'Content-Type': 'application/json', }, }); // Interceptor to add auth token in request headers api.interceptors.request.use( (config) => { let token: string | null = null; if (typeof window !== 'undefined' && window.localStorage) { try { token = localStorage.getItem('accessToken'); } catch (e) { console.warn('Storage access restricted:', e); } } if (token && config.headers) { config.headers.Authorization = `Bearer ${token}`; } return config; }, (error) => Promise.reject(error) ); // Interceptor to handle 401/403 and present standardized Farsi toasts api.interceptors.response.use( (response) => response, (error) => { if (typeof window !== 'undefined') { const responseData = error.response?.data as ApiErrorPayload | undefined; const status = error.response?.status; const farsiMessage = responseData?.message || 'خطایی در ارتباط با سرور رخ داده است.'; // Handle 401 Unauthorized globally - trigger Auth Modal if (status === 401) { if (window.localStorage) { try { localStorage.removeItem('accessToken'); localStorage.removeItem('refreshToken'); } catch {} } try { useUserStore.getState().logout(); } catch { // Ignore if store not ready } } else if (status === 403) { if (window.localStorage) { try { localStorage.removeItem('accessToken'); localStorage.removeItem('refreshToken'); } catch {} } try { useUserStore.getState().logout(); } catch { // Ignore if store not ready } } // Show formatted Farsi toast if not explicitly suppressed and not 401/403/404 if (!error.config?.hideErrorToast && status !== 401 && status !== 403 && status !== 404) { let fullMessage = farsiMessage; if (responseData?.details && Array.isArray(responseData.details) && responseData.details.length > 0) { const detailStrings = responseData.details .map((d: unknown) => typeof d === 'string' ? d : ((d as Record)?.message || (d as Record)?.field)) .filter(Boolean); if (detailStrings.length > 0) { const joinedDetails = detailStrings.join('، '); if (!farsiMessage.includes(joinedDetails)) { fullMessage = `${farsiMessage} (${joinedDetails})`; } } } toast.error(fullMessage, { id: fullMessage }); (error as unknown as Record)._toastShown = true; } } return Promise.reject(error); } ); export default api;