| 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; | |