- Nuevo servicio apps/api/src/services/sat/proxy.service.ts. - Soporta estrategias round-robin y random. - Parsea SAT_PROXY_LIST y crea agentes HttpsProxyAgent. - Script scripts/test-proxy-rotation.ts para validar configuración.
101 lines
2.8 KiB
TypeScript
101 lines
2.8 KiB
TypeScript
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<string> {
|
|
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<string> | 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();
|