'use client'; import { toast } from "@/components/ui/use-toast"; type FetchMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'; interface FetchOptions { method?: FetchMethod; body?: any; cache?: RequestCache; headers?: HeadersInit; } // Helper function to get auth token from cookies function getAuthToken(): string | null { if (typeof document === 'undefined') return null; // Guard for SSR return document.cookie .split('; ') .find(row => row.startsWith('Authorization=')) ?.split('=')[1] || null; } /** * Gets the API base URL for client-side fetch calls */ function getClientApiBaseUrl(): string { // For client-side, we can access window.location if in browser if (typeof window !== 'undefined') { // Use the same origin (which includes the correct port) return `${window.location.origin}/api`; } // Fallback when window is not available // For development mode, use port 3001 to match our server if (process.env.NODE_ENV === 'development') { return 'http://localhost:3001/api'; } // Default fallback - relative URL return '/api'; } export async function fetchClient( endpoint: string, options: FetchOptions = {} ): Promise { const { method = 'GET', body, headers = {}, ...rest } = options; try { // Get the base API URL const baseUrl = getClientApiBaseUrl(); // Ensure endpoint doesn't start with a slash if baseUrl ends with one const normalizedEndpoint = endpoint.startsWith('/') ? endpoint.substring(1) : endpoint; // Ensure baseUrl ends with a slash if it doesn't already const normalizedBaseUrl = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`; // Combine them for the final URL const url = `${normalizedBaseUrl}${normalizedEndpoint}`; // Get auth token from cookies const authToken = getAuthToken(); // Prepare headers with authentication if token exists const requestHeaders: Record = { 'Content-Type': 'application/json', ...(headers as Record), }; if (authToken) { // Backend expects "Bearer TOKEN" format requestHeaders['Authorization'] = `Bearer ${authToken}`; console.log('Authorization header set to:', `Bearer ${authToken.substring(0, 10)}...`); } console.log('API Request:', { url, method, hasAuthToken: !!authToken }); const fetchOptions: RequestInit = { method, credentials: 'include', headers: requestHeaders, ...rest, }; if (body && method !== 'GET') { fetchOptions.body = JSON.stringify(body); } const response = await fetch(url, fetchOptions); if (!response.ok) { const errorData = await response.json().catch(() => ({})); const errorMessage = errorData.message || errorData.error || 'An error occurred'; throw new Error(errorMessage); } if (response.status === 204) { return {} as T; } const data = await response.json(); return data; } catch (error) { console.error('API request failed:', error); console.error('Error details:', error instanceof Error ? error.message : 'Unknown error'); // Only show toast if this is a client-side error (not during SSR) if (typeof window !== 'undefined') { const message = error instanceof Error ? error.message : 'Failed to connect to server'; toast({ title: 'Error', description: message, variant: 'destructive', }); } throw error; } }