feat(sat): agrega ProxyManager para rotación de proxies
- 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.
This commit is contained in:
20
apps/api/scripts/test-proxy-rotation.ts
Normal file
20
apps/api/scripts/test-proxy-rotation.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import 'dotenv/config';
|
||||
import { ProxyManager } from '../src/services/sat/proxy.service.js';
|
||||
|
||||
const manager = new ProxyManager(
|
||||
process.env.SAT_PROXY_LIST || '',
|
||||
(process.env.SAT_PROXY_STRATEGY as any) || 'round-robin',
|
||||
);
|
||||
|
||||
console.log(`Total de proxies: ${manager.getProxyCount()}`);
|
||||
console.log(`Estrategia: ${process.env.SAT_PROXY_STRATEGY || 'round-robin'}`);
|
||||
console.log('Próximos 10 proxies seleccionados:');
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const proxy = manager.getNextProxy();
|
||||
if (!proxy) {
|
||||
console.log(` ${i + 1}. (sin proxy configurado)`);
|
||||
} else {
|
||||
console.log(` ${i + 1}. ${proxy.host}:${proxy.port}`);
|
||||
}
|
||||
}
|
||||
100
apps/api/src/services/sat/proxy.service.ts
Normal file
100
apps/api/src/services/sat/proxy.service.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
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();
|
||||
Reference in New Issue
Block a user