File size: 2,888 Bytes
b0c3c39 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
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, '') // Remove potential HTML tags
.replace(/javascript:/gi, '') // Remove potential JavaScript protocol
.slice(0, 100); // Limit length
};
// Rate limiting utility
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;
}
}
// Create a rate limiter instance
export const rateLimiter = new RateLimiter();
// Wrapper for API calls with rate limiting
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);
}; |