Files
Pennyzo-Frontend-corrected/src/lib/http.ts
Hardik Sankhla b7247f21a3 feat: Complete Pennyzo frontend with live waitlist tracking
- Live waitlist integration with backend API
- Real-time polling every 60 seconds
- Comprehensive API logging and error handling
- WaitlistContext for global state management
- Responsive UI with live data updates
- Registration form with automatic waitlist refresh
- Modern React/TypeScript architecture
- Tailwind CSS styling with luxury theme
2025-09-26 04:25:47 +05:30

96 lines
3.2 KiB
TypeScript

import { API_BASE_URL } from '../config';
import { logger } from './logger';
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
let tokenGetter: (() => string | null) | null = null;
export const setAuthTokenGetter = (getter: () => string | null) => {
tokenGetter = getter;
};
export interface HttpError extends Error {
status: number;
data?: any;
}
async function request<T>(
path: string,
options: {
method?: HttpMethod;
body?: any;
headers?: Record<string, string>;
contentType?: 'json' | 'form' | 'none';
signal?: AbortSignal;
} = {}
): Promise<T> {
const { method = 'GET', body, headers = {}, contentType = 'json', signal } = options;
const finalHeaders: Record<string, string> = { ...headers };
const token = tokenGetter?.() || null;
if (token) finalHeaders['Authorization'] = `Bearer ${token}`;
if (contentType === 'json') finalHeaders['Content-Type'] = 'application/json';
if (contentType === 'form') finalHeaders['Content-Type'] = 'application/x-www-form-urlencoded';
let payload: BodyInit | undefined;
if (body != null) {
if (contentType === 'json') payload = JSON.stringify(body);
else if (contentType === 'form') payload = new URLSearchParams(body).toString();
else payload = body as BodyInit;
}
const startTime = Date.now();
const url = `${API_BASE_URL}${path}`;
// Log outgoing request
logger.apiRequest(method, url, {
headers: { ...finalHeaders, Authorization: finalHeaders.Authorization ? '[REDACTED]' : undefined },
body: payload ? (typeof payload === 'string' ? payload : '[BLOB]') : undefined
});
const res = await fetch(url, {
method,
headers: finalHeaders,
body: payload,
signal,
});
const duration = Date.now() - startTime;
const isJson = res.headers.get('content-type')?.includes('application/json');
const data = isJson ? await res.json().catch(() => undefined) : await res.text();
if (!res.ok) {
// Log error response
logger.apiError(res.status, method, url, {
status: res.status,
statusText: res.statusText,
data,
duration
});
const err: HttpError = Object.assign(new Error('HTTP Error'), { status: res.status, data });
throw err;
}
// Log successful response
logger.apiResponse(res.status, method, url, data, duration);
return data as T;
}
export const http = {
get: <T>(path: string, options: { headers?: Record<string, string>; signal?: AbortSignal } = {}) =>
request<T>(path, { method: 'GET', ...options }),
post: <T>(
path: string,
body?: any,
options: { headers?: Record<string, string>; contentType?: 'json' | 'form' | 'none'; signal?: AbortSignal } = {}
) => request<T>(path, { method: 'POST', body, ...options }),
put: <T>(path: string, body?: any, options: { headers?: Record<string, string>; signal?: AbortSignal } = {}) =>
request<T>(path, { method: 'PUT', body, ...options }),
patch: <T>(path: string, body?: any, options: { headers?: Record<string, string>; signal?: AbortSignal } = {}) =>
request<T>(path, { method: 'PATCH', body, ...options }),
delete: <T>(path: string, options: { headers?: Record<string, string>; signal?: AbortSignal } = {}) =>
request<T>(path, { method: 'DELETE', ...options }),
};