export const useApi = () => { const config = useRuntimeConfig() const router = useRouter() const { toast } = useToast() const { isLoggedIn, logout } = useAuth() // Use current domain for API calls (same subdomain routing) const getApiBaseUrl = () => { if (import.meta.client) { // In browser, use current hostname but with port 3000 for API const currentHost = window.location.hostname const protocol = window.location.protocol return `${protocol}//${currentHost}:3000` } // Fallback for SSR return config.public.apiBaseUrl } const getHeaders = () => { const headers: Record = { 'Content-Type': 'application/json', } // Add tenant ID from localStorage or state if (import.meta.client) { const tenantId = localStorage.getItem('tenantId') if (tenantId) { headers['x-tenant-id'] = tenantId } const token = localStorage.getItem('token') if (token) { headers['Authorization'] = `Bearer ${token}` } } return headers } const handleResponse = async (response: Response) => { if (response.status === 401) { // Unauthorized - not authenticated if (import.meta.client) { logout() toast.error('Your session has expired. Please login again.') router.push('/login') } throw new Error('Unauthorized') } if (response.status === 403) { // Forbidden - not authorized if (import.meta.client) { toast.error('You do not have permission to perform this action.') // Redirect to home if logged in, otherwise to login if (isLoggedIn()) { router.push('/') } else { router.push('/login') } } throw new Error('Forbidden') } if (!response.ok) { // Try to get error details from response const text = await response.text() console.error('API Error Response:', { status: response.status, statusText: response.statusText, body: text }) let errorMessage = `HTTP error! status: ${response.status}` if (text) { try { const errorData = JSON.parse(text) errorMessage = errorData.message || errorData.error || errorMessage } catch (e) { // If not JSON, use the text directly if it's not too long if (text.length < 200) { errorMessage = text } } } throw new Error(errorMessage) } // Handle empty responses const text = await response.text() if (!text) { return {} } try { return JSON.parse(text) } catch (e) { console.error('Failed to parse JSON response:', text) throw new Error('Invalid JSON response from server') } } const api = { async get(path: string, options?: { params?: Record }) { let url = `${getApiBaseUrl()}/api${path}` // Add query parameters if provided if (options?.params) { const searchParams = new URLSearchParams() Object.entries(options.params).forEach(([key, value]) => { if (value !== undefined && value !== null) { searchParams.append(key, String(value)) } }) const queryString = searchParams.toString() if (queryString) { url += `?${queryString}` } } const response = await fetch(url, { headers: getHeaders(), }) return handleResponse(response) }, async post(path: string, data: any) { const response = await fetch(`${getApiBaseUrl()}/api${path}`, { method: 'POST', headers: getHeaders(), body: JSON.stringify(data), }) return handleResponse(response) }, async put(path: string, data: any) { const response = await fetch(`${getApiBaseUrl()}/api${path}`, { method: 'PUT', headers: getHeaders(), body: JSON.stringify(data), }) return handleResponse(response) }, async patch(path: string, data: any) { const response = await fetch(`${getApiBaseUrl()}/api${path}`, { method: 'PATCH', headers: getHeaders(), body: JSON.stringify(data), }) return handleResponse(response) }, async delete(path: string) { const response = await fetch(`${getApiBaseUrl()}/api${path}`, { method: 'DELETE', headers: getHeaders(), }) return handleResponse(response) }, } return { api } }