Files
RSSHub/lib/utils/proxy/multi-proxy.ts
T
dependabot[bot]andTony 417ab04f06 chore(deps-dev): bump eslint-plugin-unicorn from 66.0.0 to 68.0.0 (#22310)
* chore(deps-dev): bump eslint-plugin-unicorn from 66.0.0 to 68.0.0

Bumps [eslint-plugin-unicorn](https://github.com/sindresorhus/eslint-plugin-unicorn) from 66.0.0 to 68.0.0.
- [Release notes](https://github.com/sindresorhus/eslint-plugin-unicorn/releases)
- [Commits](https://github.com/sindresorhus/eslint-plugin-unicorn/compare/v66.0.0...v68.0.0)

---
updated-dependencies:
- dependency-name: eslint-plugin-unicorn
  dependency-version: 68.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* style: fix no-useless-coercion

* style: fix prefer-boolean-return

* style: fix no-useless-logical-operand

* style: fix prefer-single-replace

* style: fix consistent-conditional-object-spread

* style: fix no-useless-else

* style: fix prefer-unicode-code-point-escapes

* style: fix operator-assignment

* style: fix prefer-continue

* style: fix prefer-math-constants

* style: fix prefer-hoisting-branch-code

* style: fix prefer-array-slice

* style: fix consistent-class-member-order

* style: fix no-return-array-push

* style: fix prefer-promise-with-resolvers

* style: fix no-array-concat-in-loop

* style: fix no-loop-iterable-mutation

* style: fix no-invalid-argument-count

* style: fix no-duplicate-if-branches

* style: fix prefer-else-if

* style: fix no-unreadable-for-of-expression

* style: fix logical-assignment-operators

* style: fix default-export-style

* style: revert false positve in prefer-type-literal-last in #22274

* style: fix arrow-body-style

* style: add v68 recommended preset

* style: refactor GraphQL queries to use template literals for better readability

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Tony <TonyRL@users.noreply.github.com>
2026-06-20 23:11:27 +08:00

143 lines
4.3 KiB
TypeScript

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)) {
continue;
}
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;