Spaces:
Running
Running
| // src/api.js | |
| const configuredApiUrl = (import.meta.env.VITE_API_URL || '').trim(); | |
| const isLocalhost = typeof window !== 'undefined' && ['localhost', '127.0.0.1'].includes(window.location.hostname); | |
| const defaultApiUrl = isLocalhost ? 'http://localhost:4000/api' : '/api'; | |
| const API_URL = (configuredApiUrl || defaultApiUrl).replace(/\/$/, ''); | |
| const getHeaders = (options = {}) => { | |
| const headers = { | |
| ...options.headers, | |
| }; | |
| // Add auth token if exists | |
| const token = localStorage.getItem('token'); | |
| if (token) { | |
| headers['Authorization'] = `Bearer ${token}`; | |
| } | |
| // Only add Content-Type if we're not sending FormData | |
| if (!(options.body instanceof FormData)) { | |
| headers['Content-Type'] = 'application/json'; | |
| } | |
| return headers; | |
| }; | |
| export const fetchApi = async (endpoint, options = {}) => { | |
| try { | |
| const url = `${API_URL}${endpoint.startsWith('/') ? endpoint : `/${endpoint}`}`; | |
| // Default fetch options | |
| const fetchOptions = { | |
| ...options, | |
| headers: getHeaders(options), | |
| }; | |
| // Stringify body if it's an object (and not FormData) | |
| if (options.body && typeof options.body === 'object' && !(options.body instanceof FormData)) { | |
| fetchOptions.body = JSON.stringify(options.body); | |
| } | |
| const response = await fetch(url, fetchOptions); | |
| let data; | |
| const contentType = response.headers.get("content-type"); | |
| if (contentType && contentType.indexOf("application/json") !== -1) { | |
| data = await response.json(); | |
| } else { | |
| data = await response.text(); | |
| } | |
| if (!response.ok) { | |
| const errorMessage = data?.error || data?.message || data || 'An error occurred'; | |
| throw new Error(errorMessage); | |
| } | |
| return data; | |
| } catch (error) { | |
| console.error(`API Error (${endpoint}):`, error); | |
| throw error; | |
| } | |
| }; | |
| export const streamApi = async (endpoint, options = {}, onToken) => { | |
| const url = `${API_URL}${endpoint.startsWith('/') ? endpoint : `/${endpoint}`}`; | |
| const fetchOptions = { | |
| ...options, | |
| headers: getHeaders(options), | |
| }; | |
| if (options.body && typeof options.body === 'object' && !(options.body instanceof FormData)) { | |
| fetchOptions.body = JSON.stringify(options.body); | |
| } | |
| const response = await fetch(url, fetchOptions); | |
| if (!response.ok) { | |
| const data = await response.json().catch(() => ({})); | |
| throw new Error(data.error || 'Stream error'); | |
| } | |
| const reader = response.body.getReader(); | |
| const decoder = new TextDecoder(); | |
| let done = false; | |
| while (!done) { | |
| const { value, done: doneReading } = await reader.read(); | |
| done = doneReading; | |
| const chunkValue = decoder.decode(value); | |
| if (chunkValue) { | |
| onToken(chunkValue); | |
| } | |
| } | |
| }; | |