- Surse complete web (React/Vite) + mobil (React Native/Expo) + extensie (MV3) - Documentație de livrare: ghid utilizare, matrice trasabilitate cerințe, raport testare furnizor - Artefacte binare: imagine Docker didi-frontend:lot3-1.0, APK, extensie v3.2.6 + SHA256SUMS - Configurare adresă platformă externalizată (build args / .env / config.js) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
62 lines
1.9 KiB
TypeScript
62 lines
1.9 KiB
TypeScript
import axios from 'axios';
|
|
import { Platform } from 'react-native';
|
|
import { API_CONFIG } from './config';
|
|
import { useAuthStore } from '../store/authStore';
|
|
|
|
const USER_AGENT =
|
|
'Mozilla/5.0 (Linux; Android 14) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36 DidiApp/1.0';
|
|
|
|
// Browser forbids setting User-Agent — only set on native
|
|
const nativeHeaders = Platform.OS !== 'web' ? { 'User-Agent': USER_AGENT } : {};
|
|
|
|
if (Platform.OS !== 'web') {
|
|
axios.defaults.headers.common['User-Agent'] = USER_AGENT;
|
|
}
|
|
|
|
export const skillsApi = axios.create({
|
|
baseURL: API_CONFIG.SKILLS_API,
|
|
timeout: 300000,
|
|
headers: nativeHeaders,
|
|
});
|
|
|
|
export const mainApi = axios.create({
|
|
baseURL: API_CONFIG.MAIN_API,
|
|
timeout: 30000,
|
|
headers: nativeHeaders,
|
|
});
|
|
|
|
// Request interceptor - attach token & auto-refresh
|
|
const attachToken = async (config: any) => {
|
|
const { accessToken, expiresAt, refreshAccessToken } = useAuthStore.getState();
|
|
|
|
if (expiresAt && Date.now() > expiresAt - 30000) {
|
|
const newToken = await refreshAccessToken();
|
|
if (newToken) {
|
|
config.headers.Authorization = `Bearer ${newToken}`;
|
|
}
|
|
} else if (accessToken) {
|
|
config.headers.Authorization = `Bearer ${accessToken}`;
|
|
}
|
|
|
|
return config;
|
|
};
|
|
|
|
// Response interceptor - retry on 401 (max once to prevent infinite loops)
|
|
const handle401 = async (error: any) => {
|
|
const config = error.config;
|
|
if (error.response?.status === 401 && !config?._retried) {
|
|
config._retried = true;
|
|
const newToken = await useAuthStore.getState().refreshAccessToken();
|
|
if (newToken) {
|
|
config.headers.Authorization = `Bearer ${newToken}`;
|
|
return axios.request(config);
|
|
}
|
|
}
|
|
return Promise.reject(error);
|
|
};
|
|
|
|
skillsApi.interceptors.request.use(attachToken);
|
|
skillsApi.interceptors.response.use((r) => r, handle401);
|
|
|
|
mainApi.interceptors.request.use(attachToken);
|
|
mainApi.interceptors.response.use((r) => r, handle401);
|