import type { Config } from '@/config'; import logger from '@/utils/logger'; import unifyProxy from './unify-proxy'; export interface ProxyState { uri: string; isActive: boolean; failureCount: number; lastFailureTime?: number; agent?: any; dispatcher?: any; urlHandler?: URL | null; } export interface MultiProxyResult { currentProxy?: ProxyState | null; allProxies: ProxyState[]; proxyObj: Config['proxy']; getNextProxy: () => ProxyState | null; markProxyFailed: (proxyUri: string) => void; resetProxy: (proxyUri: string) => void; } const createMultiProxy = (proxyUris: string[], proxyObj: Config['proxy']): MultiProxyResult => { const proxies: ProxyState[] = []; let currentProxyIndex = 0; for (const uri of proxyUris) { const unifiedProxy = unifyProxy(uri, proxyObj); if (unifiedProxy.proxyUri) { proxies.push({ uri: unifiedProxy.proxyUri, isActive: true, failureCount: 0, urlHandler: unifiedProxy.proxyUrlHandler, }); } } if (proxies.length === 0) { logger.warn('No valid proxies found in the provided list'); return { allProxies: [], proxyObj: proxyObj || {}, getNextProxy: () => null, markProxyFailed: () => {}, resetProxy: () => {}, }; } const healthCheckInterval = proxyObj?.healthCheckInterval || 60000; const maxFailures = 3; const healthCheck = () => { const now = Date.now(); for (const proxy of proxies) { if (!proxy.isActive && proxy.lastFailureTime && now - proxy.lastFailureTime > healthCheckInterval) { proxy.isActive = true; proxy.failureCount = 0; delete proxy.lastFailureTime; logger.info(`Proxy ${proxy.uri} marked as active again after health check`); } } }; setInterval(healthCheck, healthCheckInterval); const getNextProxy = (): ProxyState | null => { const activeProxies = proxies.filter((p) => p.isActive); if (activeProxies.length === 0) { logger.warn('No active proxies available'); return null; } let nextProxy = activeProxies[currentProxyIndex % activeProxies.length]; let attempts = 0; while (!nextProxy.isActive && attempts < activeProxies.length) { currentProxyIndex = (currentProxyIndex + 1) % activeProxies.length; nextProxy = activeProxies[currentProxyIndex]; attempts++; } if (!nextProxy.isActive) { return null; } return nextProxy; }; const markProxyFailed = (proxyUri: string) => { const proxy = proxies.find((p) => p.uri === proxyUri); if (proxy) { proxy.failureCount++; proxy.lastFailureTime = Date.now(); if (proxy.failureCount >= maxFailures) { proxy.isActive = false; logger.warn(`Proxy ${proxyUri} marked as inactive after ${maxFailures} failures`); } else { logger.warn(`Proxy ${proxyUri} failed (${proxy.failureCount}/${maxFailures})`); } const activeProxies = proxies.filter((p) => p.isActive); if (activeProxies.length > 0) { currentProxyIndex = (currentProxyIndex + 1) % activeProxies.length; const nextProxy = getNextProxy(); if (nextProxy) { logger.info(`Switching to proxy: ${nextProxy.uri}`); } } } }; const resetProxy = (proxyUri: string) => { const proxy = proxies.find((p) => p.uri === proxyUri); if (proxy) { proxy.isActive = true; proxy.failureCount = 0; delete proxy.lastFailureTime; logger.info(`Proxy ${proxyUri} manually reset`); } }; const currentProxy = getNextProxy(); if (currentProxy) { logger.info(`Initial proxy selected: ${currentProxy.uri}`); } return { currentProxy, allProxies: proxies, proxyObj: proxyObj || {}, getNextProxy, markProxyFailed, resetProxy, }; }; export default createMultiProxy;