|
|
|
|
|
|
|
|
|
|
|
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; |
|
|
|
constructor(config: ProxyConfig) { |
|
this.config = config; |
|
} |
|
|
|
|
|
|
|
|
|
async fetchWithFallback<T>(url: string, options: RequestInit = {}): Promise<T> { |
|
|
|
const cached = this.getFromCache(url); |
|
if (cached) { |
|
console.log('📦 Dados obtidos do cache'); |
|
return cached; |
|
} |
|
|
|
|
|
try { |
|
const response = await this.directFetch<T>(url, options); |
|
this.setCache(url, response); |
|
return response; |
|
} catch (directError) { |
|
console.warn('❌ Requisição direta falhou:', directError); |
|
|
|
|
|
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); |
|
} |
|
} |
|
|
|
|
|
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() }); |
|
} |
|
|
|
|
|
|
|
|
|
clearCache(): void { |
|
this.cache.clear(); |
|
} |
|
|
|
|
|
|
|
|
|
getCacheInfo(): { size: number; keys: string[] } { |
|
return { |
|
size: this.cache.size, |
|
keys: Array.from(this.cache.keys()) |
|
}; |
|
} |
|
} |
|
|
|
|
|
export const apiProxy = new ProxyService({ |
|
useProxy: false, |
|
timeout: 10000, |
|
retries: 3 |
|
}); |
|
|
|
|
|
export const PUBLIC_PROXIES = [ |
|
'https://cors-anywhere.herokuapp.com/', |
|
'https://api.allorigins.win/get?url=', |
|
'https://thingproxy.freeboard.io/fetch/' |
|
]; |
|
|