import { HttpsProxyAgent } from 'https-proxy-agent'; export interface ProxyConfig { url: string; host: string; port: number; username?: string; password?: string; } export type ProxyStrategy = 'round-robin' | 'random'; function parseProxyList(raw: string): ProxyConfig[] { if (!raw.trim()) return []; const configs: ProxyConfig[] = []; const items = raw.split(',').map(s => s.trim()).filter(Boolean); for (const item of items) { try { const url = new URL(item); if (url.protocol !== 'http:' && url.protocol !== 'https:') { console.warn(`[ProxyManager] Protocolo no soportado, se omite: ${item}`); continue; } configs.push({ url: item, host: url.hostname, port: Number(url.port) || (url.protocol === 'https:' ? 443 : 80), username: url.username || undefined, password: url.password || undefined, }); } catch (err) { console.warn(`[ProxyManager] URL de proxy inválida, se omite: ${item}`); } } return configs; } export class ProxyManager { private proxies: ProxyConfig[]; private strategy: ProxyStrategy; private currentIndex = 0; constructor( proxyList = process.env.SAT_PROXY_LIST || '', strategy: ProxyStrategy = (process.env.SAT_PROXY_STRATEGY as ProxyStrategy) || 'round-robin', ) { this.proxies = parseProxyList(proxyList); this.strategy = ['round-robin', 'random'].includes(strategy) ? strategy : 'round-robin'; if (this.proxies.length > 0) { console.log(`[ProxyManager] ${this.proxies.length} proxy(s) configurados (estrategia: ${this.strategy})`); } else { console.log('[ProxyManager] No hay proxies configurados; se usará la IP directa del servidor'); } } hasProxies(): boolean { return this.proxies.length > 0; } getProxyCount(): number { return this.proxies.length; } getNextProxy(): ProxyConfig | null { if (this.proxies.length === 0) return null; if (this.strategy === 'random') { return this.proxies[Math.floor(Math.random() * this.proxies.length)]; } const proxy = this.proxies[this.currentIndex]; this.currentIndex = (this.currentIndex + 1) % this.proxies.length; return proxy; } createAgent(proxy: ProxyConfig): HttpsProxyAgent { return new HttpsProxyAgent(proxy.url); } /** * Crea un agente con el siguiente proxy del pool. * Útil cuando se quiere una nueva conexión por solicitud. */ createNextAgent(): HttpsProxyAgent | null { const proxy = this.getNextProxy(); if (!proxy) return null; console.log(`[ProxyManager] Usando proxy: ${proxy.host}:${proxy.port}`); return this.createAgent(proxy); } } /** * Instancia global del proxy manager. * Lee SAT_PROXY_LIST y SAT_PROXY_STRATEGY del entorno. */ export const proxyManager = new ProxyManager();