From 562828e14d0b4ab95a465655e6b749eaeee2de38 Mon Sep 17 00:00:00 2001 From: oomeow Date: Sat, 11 Jul 2026 13:24:41 +0800 Subject: [PATCH] fix: provider proxy delay check (#7456) * refactor(proxy): support delay testing for provider-based proxies * style: remove debug console logs * docs: update changelog * feat(services): merge provider nodes into records in calcuProxies --------- Co-authored-by: Tunglies --- Changelog.md | 1 + src/components/home/current-proxy-card.tsx | 48 +++++++--------------- src/components/proxy/proxy-groups.tsx | 21 +--------- src/components/proxy/proxy-item-mini.tsx | 10 ++--- src/components/proxy/proxy-item.tsx | 16 +++----- src/components/proxy/use-render-list.ts | 5 +-- src/hooks/use-proxy-delay-state.ts | 13 ++++-- src/services/cmds.ts | 7 +++- src/services/delay.ts | 39 ++++++++++++++---- 9 files changed, 73 insertions(+), 87 deletions(-) diff --git a/Changelog.md b/Changelog.md index 5d513ab9f..c2f7f5491 100644 --- a/Changelog.md +++ b/Changelog.md @@ -38,5 +38,6 @@ - 改进切换订阅后激活选中节点的逻辑 - 实现代理组粘性滚动列表 - 完善了配置覆写的相关逻辑 +- 支持 provider 节点延迟检测 diff --git a/src/components/home/current-proxy-card.tsx b/src/components/home/current-proxy-card.tsx index 4a81e52ec..a5090fe08 100644 --- a/src/components/home/current-proxy-card.tsx +++ b/src/components/home/current-proxy-card.tsx @@ -21,7 +21,7 @@ import { InputLabel, MenuItem, Select, - SelectChangeEvent, + type SelectChangeEvent, Tooltip, Typography, alpha, @@ -31,7 +31,7 @@ import { useLockFn } from 'ahooks' import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { useNavigate } from 'react-router' -import { delayGroup, healthcheckProxyProvider } from 'tauri-plugin-mihomo-api' +import { delayGroup } from 'tauri-plugin-mihomo-api' import { EnhancedCard } from '@/components/home/enhanced-card' import { useProfiles } from '@/hooks/use-profiles' @@ -558,11 +558,12 @@ export const CurrentProxyCard = () => { debugLog( `[CurrentProxyCard] 自动检测当前节点延迟,组: ${groupName}, 节点: ${proxyName}`, ) - if (proxyRecord.provider) { - await healthcheckProxyProvider(proxyRecord.provider) - } else { - await delayManager.checkDelay(proxyName, groupName, timeout) - } + await delayManager.checkDelay( + proxyRecord.name, + groupName, + timeout, + proxyRecord.provider, + ) } catch (error) { console.error( `[CurrentProxyCard] 自动检测当前节点延迟失败,组: ${groupName}, 节点: ${proxyName}`, @@ -658,8 +659,7 @@ export const CurrentProxyCard = () => { const timeout = verge?.default_latency_timeout || 10000 // 获取当前组的所有代理 - const proxyNames: string[] = [] - const providers: Set = new Set() + const delayProxies: IProxyItem[] = [] if (isGlobalMode && proxies?.global) { // 全局模式 @@ -672,11 +672,7 @@ export const CurrentProxyCard = () => { allProxies.forEach((name: string) => { const proxy = state.proxyData.records[name] - if (proxy?.provider) { - providers.add(proxy.provider) - } else { - proxyNames.push(name) - } + delayProxies.push(proxy) }) } else { // 规则模式 @@ -684,35 +680,19 @@ export const CurrentProxyCard = () => { if (group) { group.all.forEach((name: string) => { const proxy = state.proxyData.records[name] - if (proxy?.provider) { - providers.add(proxy.provider) - } else { - proxyNames.push(name) - } + delayProxies.push(proxy) }) } } - debugLog( - `[CurrentProxyCard] 找到代理数量: ${proxyNames.length}, 提供者数量: ${providers.size}`, - ) - - // 测试提供者的节点 - if (providers.size > 0) { - debugLog(`[CurrentProxyCard] 开始测试提供者节点`) - await Promise.allSettled( - [...providers].map((p) => healthcheckProxyProvider(p)), - ) - } - - // 测试非提供者的节点 - if (proxyNames.length > 0) { + // 测试全部节点 + if (delayProxies.length > 0) { const url = delayManager.getUrl(groupName) debugLog(`[CurrentProxyCard] 测试URL: ${url}, 超时: ${timeout}ms`) try { await Promise.race([ - delayManager.checkListDelay(proxyNames, groupName, timeout), + delayManager.checkListDelay(delayProxies, groupName, timeout), delayGroup(groupName, url, timeout), ]) debugLog(`[CurrentProxyCard] 延迟测试完成,组: ${groupName}`) diff --git a/src/components/proxy/proxy-groups.tsx b/src/components/proxy/proxy-groups.tsx index 5316fb844..af5e767a5 100644 --- a/src/components/proxy/proxy-groups.tsx +++ b/src/components/proxy/proxy-groups.tsx @@ -11,7 +11,7 @@ import { useRef, useState, } from 'react' -import { delayGroup, healthcheckProxyProvider } from 'tauri-plugin-mihomo-api' +import { delayGroup } from 'tauri-plugin-mihomo-api' import { BaseEmpty, @@ -97,29 +97,12 @@ function useProxyRenderState( debugLog(`[ProxyGroups] 找到代理数量: ${proxies.length}`) - const providers = new Set( - proxies.map((p) => p!.provider!).filter(Boolean), - ) - - if (providers.size) { - debugLog(`[ProxyGroups] 发现提供者,数量: ${providers.size}`) - Promise.allSettled( - [...providers].map((p) => healthcheckProxyProvider(p)), - ).then(() => { - debugLog(`[ProxyGroups] 提供者健康检查完成`) - onProxies() - }) - } - - const names = proxies.filter((p) => !p!.provider).map((p) => p!.name) - debugLog(`[ProxyGroups] 过滤后需要测试的代理数量: ${names.length}`) - const url = delayManager.getUrl(groupName) debugLog(`[ProxyGroups] 测试URL: ${url}, 超时: ${timeout}ms`) try { await Promise.race([ - delayManager.checkListDelay(names, groupName, timeout), + delayManager.checkListDelay(proxies, groupName, timeout), delayGroup(groupName, url, timeout).then((result) => { debugLog( `[ProxyGroups] getGroupProxyDelays返回结果数量:`, diff --git a/src/components/proxy/proxy-item-mini.tsx b/src/components/proxy/proxy-item-mini.tsx index 525ec1d16..447685252 100644 --- a/src/components/proxy/proxy-item-mini.tsx +++ b/src/components/proxy/proxy-item-mini.tsx @@ -159,8 +159,7 @@ export const ProxyItemMini = (props: Props) => { )} - {!proxy.provider && delayValue !== -2 && ( - // provider 的节点不支持检测 + {delayValue !== -2 && ( { @@ -182,16 +181,13 @@ export const ProxyItemMini = (props: Props) => { { - if (proxy.provider) return e.preventDefault() e.stopPropagation() - onDelay() + onDelay(proxy.provider) }} sx={({ palette }) => ({ color: delayManager.formatDelayColor(delayValue, timeout), - ...(!proxy.provider - ? { ':hover': { bgcolor: alpha(palette.primary.main, 0.15) } } - : {}), + ':hover': { bgcolor: alpha(palette.primary.main, 0.15) }, })} > {delayManager.formatDelay(delayValue, timeout)} diff --git a/src/components/proxy/proxy-item.tsx b/src/components/proxy/proxy-item.tsx index f3a013c0a..1cf20671d 100644 --- a/src/components/proxy/proxy-item.tsx +++ b/src/components/proxy/proxy-item.tsx @@ -7,8 +7,8 @@ import { ListItemIcon, ListItemText, styled, - SxProps, - Theme, + type SxProps, + type Theme, } from '@mui/material' import { BaseLoading } from '@/components/base' @@ -125,14 +125,13 @@ export const ProxyItem = (props: Props) => { )} - {!proxy.provider && delayValue !== -2 && ( - // provider 的节点不支持检测 + {delayValue !== -2 && ( { e.preventDefault() e.stopPropagation() - onDelay() + onDelay(proxy.provider) }} sx={({ palette }) => ({ display: 'none', // hover 时显示 @@ -148,16 +147,13 @@ export const ProxyItem = (props: Props) => { { - if (proxy.provider) return e.preventDefault() e.stopPropagation() - onDelay() + onDelay(proxy.provider) }} sx={({ palette }) => ({ color: delayManager.formatDelayColor(delayValue, timeout), - ...(!proxy.provider - ? { ':hover': { bgcolor: alpha(palette.primary.main, 0.15) } } - : {}), + ':hover': { bgcolor: alpha(palette.primary.main, 0.15) }, })} > {delayManager.formatDelay(delayValue, timeout)} diff --git a/src/components/proxy/use-render-list.ts b/src/components/proxy/use-render-list.ts index da8b1f057..60811e937 100644 --- a/src/components/proxy/use-render-list.ts +++ b/src/components/proxy/use-render-list.ts @@ -158,12 +158,11 @@ export const useRenderList = ( const calculateDelays = async () => { try { const timeout = verge?.default_latency_timeout || 10000 - const proxyNames = allProxies.map((proxy) => proxy.name) - debugLog(`[ChainMode] 开始计算 ${proxyNames.length} 个节点的延迟`) + debugLog(`[ChainMode] 开始计算 ${allProxies.length} 个节点的延迟`) // 使用 delayManager 计算延迟,每个节点计算完成后会自动触发监听器刷新界面 - delayManager.checkListDelay(proxyNames, 'chain-mode', timeout) + delayManager.checkListDelay(allProxies, 'chain-mode', timeout) } catch (error) { console.error('Failed to calculate delays for chain mode:', error) } diff --git a/src/hooks/use-proxy-delay-state.ts b/src/hooks/use-proxy-delay-state.ts index 783a2f5d2..cce0ce76f 100644 --- a/src/hooks/use-proxy-delay-state.ts +++ b/src/hooks/use-proxy-delay-state.ts @@ -21,7 +21,7 @@ export interface UseProxyDelayState { delayValue: number isPreset: boolean timeout: number - onDelay: () => Promise + onDelay: (providerName?: string) => Promise } export function useProxyDelayState( @@ -72,9 +72,16 @@ export function useProxyDelayState( updateDelay() }, [updateDelay]) - const onDelay = useLockFn(async () => { + const onDelay = useLockFn(async (providerName?: string) => { setDelayState({ delay: -2, updatedAt: Date.now() }) - setDelayState(await delayManager.checkDelay(proxy.name, groupName, timeout)) + setDelayState( + await delayManager.checkDelay( + proxy.name, + groupName, + timeout, + providerName, + ), + ) }) return { diff --git a/src/services/cmds.ts b/src/services/cmds.ts index abd4f4b7d..0596302df 100644 --- a/src/services/cmds.ts +++ b/src/services/cmds.ts @@ -217,11 +217,14 @@ export async function calcuProxies(): Promise<{ all: global?.all?.map((item) => generateItem(item)) || [], } + // 原本的 records 拥有所有节点信息,新版本内核需要将 provider 的节点信息合并到 records 中, 同时兼容旧版本数据 + const records = { ...proxyRecord, ...providerMap } + return { global: _global as IProxyGroupItem, direct: direct as IProxyItem, groups, - records: proxyRecord as Record, + records: records as Record, proxies: (proxies as IProxyItem[]) ?? [], } } @@ -505,7 +508,7 @@ export async function saveWebdavConfig( export async function listWebDavBackup() { const list: IWebDavFile[] = await invoke('list_webdav_backup') - list.map((item) => { + list.forEach((item) => { item.filename = item.href.split('/').pop() as string }) return list diff --git a/src/services/delay.ts b/src/services/delay.ts index a6653e287..3adb47c84 100644 --- a/src/services/delay.ts +++ b/src/services/delay.ts @@ -1,4 +1,8 @@ -import { delayProxyByName, ProxyDelay } from 'tauri-plugin-mihomo-api' +import { + delayProxyByName, + healthcheckNodeInProvider, + type ProxyDelay, +} from 'tauri-plugin-mihomo-api' import { debugLog } from '@/utils/debug' @@ -198,10 +202,23 @@ class DelayManager { return -1 } + // 统一延迟测试检测 + async unifiedDelayCheck( + name: string, + url: string, + timeout: number, + providerName?: string, + ) { + if (providerName) + return healthcheckNodeInProvider(providerName, name, url, timeout) + return delayProxyByName(name, url, timeout) + } + async checkDelay( name: string, group: string, timeout: number, + providerName?: string, ): Promise { debugLog( `[DelayManager] 开始测试延迟,代理: ${name}, 组: ${group}, 超时: ${timeout}ms`, @@ -223,7 +240,7 @@ class DelayManager { // 使用Promise.race来实现超时控制 const result = await Promise.race([ - delayProxyByName(name, url, timeout), + this.unifiedDelayCheck(name, url, timeout, providerName), timeoutPromise, ]) @@ -250,25 +267,29 @@ class DelayManager { } async checkListDelay( - nameList: string[], + proxies: IProxyItem[], group: string, timeout: number, concurrency = 36, ) { debugLog( - `[DelayManager] 批量测试延迟开始,组: ${group}, 数量: ${nameList.length}, 并发数: ${concurrency}`, + `[DelayManager] 批量测试延迟开始,组: ${group}, 数量: ${proxies.length}, 并发数: ${concurrency}`, ) - const names = nameList.filter(Boolean) + const names = proxies.map((p) => p.name) // 设置正在延迟测试中 - names.forEach((name) => this.setDelay(name, group, -2)) + names.forEach((name) => { + this.setDelay(name, group, -2) + }) let index = 0 const startTime = Date.now() const listener = this.groupListenerMap.get(group) const help = async (): Promise => { - const currName = names[index++] - if (!currName) return + const currProxy = proxies[index++] + if (!currProxy) return + const currName = currProxy.name + const currProviderName = currProxy.provider try { // 确保API调用前状态为测试中 @@ -282,7 +303,7 @@ class DelayManager { ) } - await this.checkDelay(currName, group, timeout) + await this.checkDelay(currName, group, timeout, currProviderName) if (listener) { this.queueGroupNotification(group) }