Loto / src /utils /ProxyService.ts
Raí Santos
oi
4c1e4ec
/**
* Serviço Proxy para contornar limitações de CORS
* Implementa fallbacks e cache para melhorar a experiência do usuário
*/
export interface ProxyConfig {
useProxy: boolean;
proxyUrl?: string;
timeout: number;
retries: number;
}
export class ProxyService {
private config: ProxyConfig;
private cache = new Map<string, { data: any; timestamp: number }>();
private readonly CACHE_DURATION = 5 * 60 * 1000; // 5 minutos
constructor(config: ProxyConfig) {
this.config = config;
}
/**
* Método principal para fazer requisições com fallback
*/
async fetchWithFallback<T>(url: string, options: RequestInit = {}): Promise<T> {
// Verificar cache primeiro
const cached = this.getFromCache(url);
if (cached) {
console.log('📦 Dados obtidos do cache');
return cached;
}
// Tentar requisição direta primeiro
try {
const response = await this.directFetch<T>(url, options);
this.setCache(url, response);
return response;
} catch (directError) {
console.warn('❌ Requisição direta falhou:', directError);
// Se configurado, tentar através de proxy
if (this.config.useProxy && this.config.proxyUrl) {
try {
const response = await this.proxyFetch<T>(url, options);
this.setCache(url, response);
return response;
} catch (proxyError) {
console.error('❌ Proxy também falhou:', proxyError);
}
}
// Se tudo falhar, lançar erro original
throw directError;
}
}
private async directFetch<T>(url: string, options: RequestInit): Promise<T> {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
try {
const response = await fetch(url, {
...options,
signal: controller.signal,
mode: 'cors',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
...options.headers,
},
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
} finally {
clearTimeout(timeoutId);
}
}
private async proxyFetch<T>(url: string, options: RequestInit): Promise<T> {
const proxyUrl = `${this.config.proxyUrl}?url=${encodeURIComponent(url)}`;
return this.directFetch<T>(proxyUrl, options);
}
private getFromCache(url: string): any | null {
const cached = this.cache.get(url);
if (cached && (Date.now() - cached.timestamp) < this.CACHE_DURATION) {
return cached.data;
}
return null;
}
private setCache(url: string, data: any): void {
this.cache.set(url, { data, timestamp: Date.now() });
}
/**
* Limpar cache manualmente
*/
clearCache(): void {
this.cache.clear();
}
/**
* Obter informações sobre o cache
*/
getCacheInfo(): { size: number; keys: string[] } {
return {
size: this.cache.size,
keys: Array.from(this.cache.keys())
};
}
}
// Instância singleton configurada para a aplicação
export const apiProxy = new ProxyService({
useProxy: false, // Definir como true se tiver um proxy configurado
timeout: 10000,
retries: 3
});
// Proxies públicos populares (usar com cuidado em produção)
export const PUBLIC_PROXIES = [
'https://cors-anywhere.herokuapp.com/',
'https://api.allorigins.win/get?url=',
'https://thingproxy.freeboard.io/fetch/'
];