| import axios, { AxiosError } from 'axios';
|
|
|
| export interface ApiError {
|
| message: string;
|
| details?: string;
|
| code?: string;
|
| }
|
|
|
| interface ApiErrorResponse {
|
| message?: string;
|
| error?: string;
|
| details?: string;
|
| }
|
|
|
| export const handleApiError = (error: unknown): ApiError => {
|
| if (axios.isAxiosError(error)) {
|
| const axiosError = error as AxiosError<ApiErrorResponse>;
|
| const responseData = axiosError.response?.data;
|
| return {
|
| message: axiosError.message,
|
| details: responseData?.message || responseData?.error || 'Unknown error occurred',
|
| code: axiosError.code,
|
| };
|
| }
|
|
|
| if (error instanceof Error) {
|
| return {
|
| message: error.message,
|
| details: error.stack,
|
| };
|
| }
|
|
|
| return {
|
| message: 'An unexpected error occurred',
|
| details: String(error),
|
| };
|
| };
|
|
|
| export const withRetry = async <T>(
|
| operation: () => Promise<T>,
|
| maxRetries: number = 3,
|
| delay: number = 1000
|
| ): Promise<T> => {
|
| let lastError: unknown;
|
|
|
| for (let i = 0; i < maxRetries; i++) {
|
| try {
|
| return await operation();
|
| } catch (error) {
|
| lastError = error;
|
| if (i < maxRetries - 1) {
|
| await new Promise(resolve => setTimeout(resolve, delay * Math.pow(2, i)));
|
| }
|
| }
|
| }
|
|
|
| throw lastError;
|
| };
|
|
|
| export const validateInput = (input: string, maxLength: number = 100): boolean => {
|
| if (!input || typeof input !== 'string') return false;
|
| if (input.trim().length === 0) return false;
|
| if (input.length > maxLength) return false;
|
| return true;
|
| };
|
|
|
| export const sanitizeInput = (input: string): string => {
|
| return input
|
| .trim()
|
| .replace(/[<>]/g, '')
|
| .replace(/javascript:/gi, '')
|
| .slice(0, 100);
|
| };
|
|
|
|
|
| export class RateLimiter {
|
| private timestamps: number[] = [];
|
| private readonly windowMs: number;
|
| private readonly maxRequests: number;
|
|
|
| constructor(windowMs: number = 60000, maxRequests: number = 100) {
|
| this.windowMs = windowMs;
|
| this.maxRequests = maxRequests;
|
| }
|
|
|
| canMakeRequest(): boolean {
|
| const now = Date.now();
|
| this.timestamps = this.timestamps.filter(time => now - time < this.windowMs);
|
|
|
| if (this.timestamps.length >= this.maxRequests) {
|
| return false;
|
| }
|
|
|
| this.timestamps.push(now);
|
| return true;
|
| }
|
| }
|
|
|
|
|
| export const rateLimiter = new RateLimiter();
|
|
|
|
|
| export const rateLimitedApiCall = async <T>(
|
| apiCall: () => Promise<T>,
|
| retryCount: number = 3
|
| ): Promise<T> => {
|
| if (!rateLimiter.canMakeRequest()) {
|
| throw new Error('Rate limit exceeded. Please try again later.');
|
| }
|
|
|
| return withRetry(apiCall, retryCount);
|
| }; |