Files
ember-market-frontend/lib/client-utils.ts
NotII 39c349509c ugh
2025-03-24 01:46:11 +00:00

98 lines
2.7 KiB
TypeScript

/**
* Normalizes a URL to ensure it has the correct /api prefix
* This prevents double prefixing which causes API errors
*/
function normalizeApiUrl(url: string): string {
// Remove any existing /api or api prefix
const cleanPath = url.replace(/^\/?(api\/)+/, '');
// Add a single /api prefix
return `/api/${cleanPath.replace(/^\//, '')}`;
}
/**
* Get the authentication token from cookies or localStorage
*/
function getAuthToken(): string | null {
if (typeof document === 'undefined') return null; // Guard for SSR
return document.cookie
.split('; ')
.find(row => row.startsWith('Authorization='))
?.split('=')[1] || localStorage.getItem('Authorization');
}
/**
* Creates standard API request headers with authentication
*
* @param token Optional auth token (fetched automatically if not provided)
* @param customHeaders Additional headers to include
* @returns Headers object ready for fetch requests
*/
function createApiHeaders(token?: string | null, customHeaders: Record<string, string> = {}): Headers {
const headers = new Headers({
'Content-Type': 'application/json',
...customHeaders
});
const authToken = token || getAuthToken();
if (authToken) {
headers.set('Authorization', `Bearer ${authToken}`);
}
return headers;
}
/**
* Simple client-side fetch function for making API calls with Authorization header.
* Ensures all requests go through the Next.js API proxy.
*/
export async function clientFetch<T = any>(url: string, options: RequestInit = {}): Promise<T> {
try {
// Create headers with authentication
const headers = createApiHeaders(null, options.headers as Record<string, string>);
// Normalize URL to ensure it uses the Next.js API proxy
const fullUrl = normalizeApiUrl(url);
console.log(`Fetching URL: ${fullUrl}`);
const res = await fetch(fullUrl, {
...options,
headers,
credentials: 'include',
});
if (!res.ok) {
const errorData = await res.json().catch(() => ({}));
const errorMessage = errorData.message || errorData.error || `Request failed: ${res.status} ${res.statusText}`;
console.error('API Error:', {
status: res.status,
url: fullUrl,
response: errorData,
method: options.method || 'GET'
});
throw new Error(errorMessage);
}
// Handle 204 No Content responses
if (res.status === 204) {
return {} as T;
}
return await res.json();
} catch (error) {
console.error(`Client fetch error at ${url}:`, error);
throw error;
}
}
/**
* Get a cookie value by name
*/
export function getCookie(name: string): string | undefined {
return document.cookie
.split('; ')
.find(row => row.startsWith(`${name}=`))
?.split('=')[1];
}