Files
ember-market-frontend/lib/client-service.ts
2025-03-10 17:39:37 +00:00

127 lines
3.5 KiB
TypeScript

'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<T>(
endpoint: string,
options: FetchOptions = {}
): Promise<T> {
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<string, string> = {
'Content-Type': 'application/json',
...(headers as Record<string, string>),
};
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;
}
}