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