import axios from 'axios'; import { getToken, clearAuth } from './auth'; // Cached config to avoid repeated fetches let cachedConfig: any = null; /** * Fetches the network configuration from the public/network.json file. * This file is generated at startup by start_server.sh or docker-compose. */ export const getNetworkConfig = async () => { if (cachedConfig) return cachedConfig; if (typeof window === 'undefined') { return { SERVER_IP: 'localhost', BACKEND_PORT: 8000, BACKEND_SSL_PORT: 8908 }; } try { const response = await fetch('/network.json'); if (!response.ok) throw new Error("Config not found"); cachedConfig = await response.json(); return cachedConfig; } catch (e) { console.warn("Network config not found, using compiled defaults."); // Defaults matching the initial reserve ports in case network.json is missing return { SERVER_IP: 'localhost', BACKEND_PORT: 8906, BACKEND_SSL_PORT: 8908 }; } }; export const getBackendUrl = async () => { const config = await getNetworkConfig(); if (typeof window === 'undefined') return `http://localhost:${config.BACKEND_PORT}`; const host = window.location.hostname; // If we are on HTTPS (Proxy/Mobile mode), we use the SSL port for the backend if (window.location.protocol === 'https:') { if (host.includes('.loca.lt')) { return 'https://inventory-ai-api.loca.lt'; } return `https://${host}:${config.BACKEND_SSL_PORT}`; } return `http://${host}:${config.BACKEND_PORT}`; }; /** * [C-01] Axios instance cu JWT Bearer token în header * și interceptor pentru 401 Unauthorized (token expired) */ const axiosInstance = axios.create({}); axiosInstance.interceptors.request.use(async (config) => { if (!config.baseURL) { config.baseURL = await getBackendUrl(); // called at request time — always correct } const token = getToken(); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }, (error) => Promise.reject(error)); axiosInstance.interceptors.response.use( (response) => response, (error) => { // [L-01] Handle 401 Unauthorized — token expired if (error.response?.status === 401) { clearAuth(); if (typeof window !== 'undefined' && !window.location.pathname.includes('/login')) { window.location.href = '/login'; } } return Promise.reject(error); } ); export const inventoryApi = { getItems: async () => { const res = await axiosInstance.get('/items/'); return res.data; }, getStats: async () => { const res = await axiosInstance.get('/items/stats'); return res.data; }, syncBulkOperations: async (userId: number, operations: any[]) => { try { const res = await axiosInstance.post('/operations/bulk-sync', { user_id: userId, operations: operations }); return res.data; } catch (err: any) { console.error("Sync API Error:", err); if (err.response?.status === 404) { throw new Error(`404: Endpoint not found`); } throw err; } }, analyzeLabel: async (formData: FormData, mode: string = "item") => { const res = await axiosInstance.post(`/items/extract-label?mode=${mode}`, formData, { headers: { 'Content-Type': 'multipart/form-data' } }); return res.data; }, createItem: async (userId: number, itemData: any) => { const res = await axiosInstance.post('/items/', itemData); return res.data; }, updateItem: async (itemId: number, itemData: any) => { const res = await axiosInstance.put(`/items/${itemId}`, itemData); return res.data; }, adjustStock: async (endpoint: string, data: any) => { const res = await axiosInstance.post(`/operations/${endpoint}`, data); return res.data; }, deleteItem: async (itemId: number) => { const res = await axiosInstance.delete(`/items/${itemId}`); return res.data; }, getAuditLogs: async (limit: number = 50) => { const res = await axiosInstance.get('/operations/logs', { params: { limit } }); return res.data; }, // Users getUsers: async () => { // [C-01] Public endpoint — use plain axios to avoid JWT interceptor const baseUrl = await getBackendUrl(); const res = await axios.get(`${baseUrl}/users/`); return res.data; }, createUser: async (userData: any) => { const res = await axiosInstance.post('/users/', userData); return res.data; }, login: async (credentials: any) => { // [C-01] Login endpoint — NU adaug token header (login e public) const baseUrl = await getBackendUrl(); const res = await axios.post(`${baseUrl}/users/login`, credentials); return res.data; }, deleteUser: async (userId: number) => { const res = await axiosInstance.delete(`/users/${userId}`); return res.data; }, updateUser: async (userId: number, data: any) => { const res = await axiosInstance.put(`/users/${userId}`, data); return res.data; }, getLdapConfig: async () => { const res = await axiosInstance.get('/users/ldap-config'); return res.data; }, updateLdapConfig: async (config: any) => { const res = await axiosInstance.post('/users/ldap-config', config); return res.data; }, testLdapConnection: async (config: any) => { const res = await axiosInstance.post('/users/test-ldap', config); return res.data; }, // Categories getCategories: async () => { const res = await axiosInstance.get('/categories/'); return res.data; }, createCategory: async (data: any) => { const res = await axiosInstance.post('/categories/', data); return res.data; }, updateCategory: async (id: number, data: any) => { const res = await axiosInstance.put(`/categories/${id}`, data); return res.data; }, deleteCategory: async (id: number) => { const res = await axiosInstance.delete(`/categories/${id}`); return res.data; }, // Database Management getDbBackups: async () => { const res = await axiosInstance.get('/admin/db/backups'); return res.data; }, getDbStats: async () => { const res = await axiosInstance.get('/admin/db/stats'); return res.data; }, triggerBackup: async () => { const res = await axiosInstance.post('/admin/db/backup'); return res.data; }, restoreDatabase: async (filename: string) => { const res = await axiosInstance.post('/admin/db/restore', { filename, confirm: true }); return res.data; }, getDbSettings: async () => { const res = await axiosInstance.get('/admin/db/settings'); return res.data; }, updateDbSettings: async (settings: any) => { const res = await axiosInstance.patch('/admin/db/settings', settings); return res.data; } };