[NOT FULLY WORKING] Added frontend react native interface.

This commit is contained in:
c-d-p
2025-04-17 17:28:19 +02:00
parent 4f3946d1c3
commit bf7eb8275c
36 changed files with 12230 additions and 1 deletions

View File

@@ -0,0 +1,139 @@
// src/api/client.ts
import axios, { AxiosError } from 'axios'; // Import AxiosError
import { Platform } from 'react-native';
import * as SecureStore from 'expo-secure-store';
import AsyncStorage from '@react-native-async-storage/async-storage';
const API_BASE_URL = process.env.EXPO_PUBLIC_API_URL || 'http://localhost:8000/api';
const TOKEN_KEY = 'maia_access_token';
console.log("Using API Base URL:", API_BASE_URL);
// Helper functions for storage (assuming they are defined above or imported)
// const getToken = async (): Promise<string | null> => { ... };
// const deleteToken = async () => { ... };
const apiClient = axios.create({
baseURL: API_BASE_URL,
headers: {
'Content-Type': 'application/json',
},
timeout: 10000,
...(Platform.OS === 'web' ? { withCredentials: true } : {}),
});
// --- Request Interceptor remains the same ---
apiClient.interceptors.request.use(
async (config) => {
// Using AsyncStorage for web token retrieval here too for consistency
const token = Platform.OS === 'web'
? await AsyncStorage.getItem(TOKEN_KEY)
: await SecureStore.getItemAsync(TOKEN_KEY).catch(() => null); // Handle potential SecureStore error
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
console.log('[API Client] Starting Request', config.method?.toUpperCase(), config.url);
return config;
},
(error) => {
console.error('[API Client] Request Setup Error:', error);
return Promise.reject(error);
}
);
// --- Modified Response Interceptor ---
apiClient.interceptors.response.use(
(response) => {
// Success case
return response;
},
async (error: AxiosError) => { // Explicitly type error as AxiosError
const originalRequest = error.config;
// Check if the error has a response object (i.e., server responded with error status)
if (error.response) {
// Server responded with an error status code (4xx, 5xx)
console.error('[API Client] Response Error Status:', error.response.status);
console.error('[API Client] Response Error Data:', error.response.data);
// Handle 401 specifically
if (error.response.status === 401) {
console.warn('[API Client] Unauthorized (401). Token might be expired or invalid.');
if (!originalRequest?._retry) {
originalRequest._retry = true; // Mark the request as retried to avoid infinite loops
try {
console.log('[API Client] Attempting token refresh...');
const refreshResponse = await apiClient.post('/auth/refresh', {}, {
headers: {
'Content-Type': 'application/json',
},
});
if (refreshResponse.status === 200) {
const newToken = refreshResponse.data?.accessToken;
if (newToken) {
console.log('[API Client] Token refreshed successfully.');
// Save the new token
if (Platform.OS === 'web') {
await AsyncStorage.setItem(TOKEN_KEY, newToken);
} else {
await SecureStore.setItemAsync(TOKEN_KEY, newToken);
}
// Update the Authorization header for future requests
apiClient.defaults.headers.common['Authorization'] = `Bearer ${newToken}`;
originalRequest.headers['Authorization'] = `Bearer ${newToken}`;
// Retry the original request with the new token
return apiClient(originalRequest);
}
}
} catch (refreshError) {
console.error('[API Client] Token refresh failed:', refreshError);
}
}
// Clear potentially invalid token due to 401
console.log('[API Client] Clearing potentially invalid token due to 401.');
if (Platform.OS === 'web') {
await AsyncStorage.removeItem(TOKEN_KEY);
} else {
await SecureStore.deleteItemAsync(TOKEN_KEY).catch(() => {}); // Ignore delete error
}
delete apiClient.defaults.headers.common['Authorization'];
// How to trigger logout? Propagating error is simplest for now.
}
} else if (error.request) {
// The request was made but no response was received
// (e.g., network error, CORS block preventing response reading, server timeout)
console.error('[API Client] Network Error or No Response:', error.message);
// Log the request object for debugging if needed
// console.error('[API Client] Error Request Object:', error.request);
// If CORS is suspected, this is often where the error ends up.
if (error.message.toLowerCase().includes('network error') && Platform.OS === 'web') {
console.warn('[API Client] Hint: A "Network Error" on web often masks a CORS issue. Check browser console & backend CORS config.');
}
} else {
// Something happened in setting up the request that triggered an Error
console.error('[API Client] Request Setup Error (Interceptor):', error.message);
}
// Log the config that failed (optional, can be verbose)
// console.error("[API Client] Failing Request Config:", error.config);
// Always reject the promise to propagate the error
return Promise.reject(error);
}
);
export default apiClient;