feat: Support custom delay timeout (#397)

This commit is contained in:
MystiPanda 2024-02-18 11:11:22 +08:00 committed by GitHub
parent 5106d77c77
commit d1d9620a61
12 changed files with 76 additions and 21 deletions

View File

@ -249,8 +249,9 @@ pub mod uwp {
pub async fn clash_api_get_proxy_delay( pub async fn clash_api_get_proxy_delay(
name: String, name: String,
url: Option<String>, url: Option<String>,
timeout: i32,
) -> CmdResult<clash_api::DelayRes> { ) -> CmdResult<clash_api::DelayRes> {
match clash_api::get_proxy_delay(name, url).await { match clash_api::get_proxy_delay(name, url, timeout).await {
Ok(res) => Ok(res), Ok(res) => Ok(res),
Err(err) => Err(err.to_string()), Err(err) => Err(err.to_string()),
} }

View File

@ -81,6 +81,9 @@ pub struct IVerge {
/// 默认的延迟测试连接 /// 默认的延迟测试连接
pub default_latency_test: Option<String>, pub default_latency_test: Option<String>,
/// 默认的延迟测试超时时间
pub default_latency_timeout: Option<i32>,
/// 是否使用内部的脚本支持,默认为真 /// 是否使用内部的脚本支持,默认为真
pub enable_builtin_enhanced: Option<bool>, pub enable_builtin_enhanced: Option<bool>,
@ -222,6 +225,7 @@ impl IVerge {
patch!(auto_close_connection); patch!(auto_close_connection);
patch!(default_latency_test); patch!(default_latency_test);
patch!(default_latency_timeout);
patch!(enable_builtin_enhanced); patch!(enable_builtin_enhanced);
patch!(proxy_layout_column); patch!(proxy_layout_column);
patch!(test_list); patch!(test_list);

View File

@ -44,7 +44,11 @@ pub struct DelayRes {
/// GET /proxies/{name}/delay /// GET /proxies/{name}/delay
/// 获取代理延迟 /// 获取代理延迟
pub async fn get_proxy_delay(name: String, test_url: Option<String>) -> Result<DelayRes> { pub async fn get_proxy_delay(
name: String,
test_url: Option<String>,
timeout: i32,
) -> Result<DelayRes> {
let (url, headers) = clash_client_info()?; let (url, headers) = clash_client_info()?;
let url = format!("{url}/proxies/{name}/delay"); let url = format!("{url}/proxies/{name}/delay");
@ -57,7 +61,7 @@ pub async fn get_proxy_delay(name: String, test_url: Option<String>) -> Result<D
let builder = client let builder = client
.get(&url) .get(&url)
.headers(headers) .headers(headers)
.query(&[("timeout", "10000"), ("url", &test_url)]); .query(&[("timeout", &format!("{timeout}")), ("url", &test_url)]);
let response = builder.send().await?; let response = builder.send().await?;
Ok(response.json::<DelayRes>().await?) Ok(response.json::<DelayRes>().await?)

View File

@ -25,6 +25,7 @@ export const ProxyGroups = (props: Props) => {
const { verge } = useVerge(); const { verge } = useVerge();
const { current, patchCurrent } = useProfiles(); const { current, patchCurrent } = useProfiles();
const timeout = verge?.default_latency_timeout || 10000;
const virtuosoRef = useRef<VirtuosoHandle>(null); const virtuosoRef = useRef<VirtuosoHandle>(null);
@ -83,7 +84,7 @@ export const ProxyGroups = (props: Props) => {
} }
const names = proxies.filter((p) => !p!.provider).map((p) => p!.name); const names = proxies.filter((p) => !p!.provider).map((p) => p!.name);
await delayManager.checkListDelay(names, groupName); await delayManager.checkListDelay(names, groupName, timeout);
onProxies(); onProxies();
}); });

View File

@ -4,6 +4,7 @@ import { CheckCircleOutlineRounded } from "@mui/icons-material";
import { alpha, Box, ListItemButton, styled, Typography } from "@mui/material"; import { alpha, Box, ListItemButton, styled, Typography } from "@mui/material";
import { BaseLoading } from "@/components/base"; import { BaseLoading } from "@/components/base";
import delayManager from "@/services/delay"; import delayManager from "@/services/delay";
import { useVerge } from "@/hooks/use-verge";
interface Props { interface Props {
groupName: string; groupName: string;
@ -20,6 +21,8 @@ export const ProxyItemMini = (props: Props) => {
// -1/<=0 为 不显示 // -1/<=0 为 不显示
// -2 为 loading // -2 为 loading
const [delay, setDelay] = useState(-1); const [delay, setDelay] = useState(-1);
const { verge } = useVerge();
const timeout = verge?.default_latency_timeout || 10000;
useEffect(() => { useEffect(() => {
delayManager.setListener(proxy.name, groupName, setDelay); delayManager.setListener(proxy.name, groupName, setDelay);
@ -36,7 +39,7 @@ export const ProxyItemMini = (props: Props) => {
const onDelay = useLockFn(async () => { const onDelay = useLockFn(async () => {
setDelay(-2); setDelay(-2);
setDelay(await delayManager.checkDelay(proxy.name, groupName)); setDelay(await delayManager.checkDelay(proxy.name, groupName, timeout));
}); });
return ( return (
@ -139,14 +142,14 @@ export const ProxyItemMini = (props: Props) => {
e.stopPropagation(); e.stopPropagation();
onDelay(); onDelay();
}} }}
color={delayManager.formatDelayColor(delay)} color={delayManager.formatDelayColor(delay, timeout)}
sx={({ palette }) => sx={({ palette }) =>
!proxy.provider !proxy.provider
? { ":hover": { bgcolor: alpha(palette.primary.main, 0.15) } } ? { ":hover": { bgcolor: alpha(palette.primary.main, 0.15) } }
: {} : {}
} }
> >
{delayManager.formatDelay(delay)} {delayManager.formatDelay(delay, timeout)}
</Widget> </Widget>
)} )}

View File

@ -14,6 +14,7 @@ import {
} from "@mui/material"; } from "@mui/material";
import { BaseLoading } from "@/components/base"; import { BaseLoading } from "@/components/base";
import delayManager from "@/services/delay"; import delayManager from "@/services/delay";
import { useVerge } from "@/hooks/use-verge";
interface Props { interface Props {
groupName: string; groupName: string;
@ -48,7 +49,8 @@ export const ProxyItem = (props: Props) => {
// -1/<=0 为 不显示 // -1/<=0 为 不显示
// -2 为 loading // -2 为 loading
const [delay, setDelay] = useState(-1); const [delay, setDelay] = useState(-1);
const { verge } = useVerge();
const timeout = verge?.default_latency_timeout || 10000;
useEffect(() => { useEffect(() => {
delayManager.setListener(proxy.name, groupName, setDelay); delayManager.setListener(proxy.name, groupName, setDelay);
@ -64,7 +66,7 @@ export const ProxyItem = (props: Props) => {
const onDelay = useLockFn(async () => { const onDelay = useLockFn(async () => {
setDelay(-2); setDelay(-2);
setDelay(await delayManager.checkDelay(proxy.name, groupName)); setDelay(await delayManager.checkDelay(proxy.name, groupName, timeout));
}); });
return ( return (
@ -149,14 +151,14 @@ export const ProxyItem = (props: Props) => {
e.stopPropagation(); e.stopPropagation();
onDelay(); onDelay();
}} }}
color={delayManager.formatDelayColor(delay)} color={delayManager.formatDelayColor(delay, timeout)}
sx={({ palette }) => sx={({ palette }) =>
!proxy.provider !proxy.provider
? { ":hover": { bgcolor: alpha(palette.primary.main, 0.15) } } ? { ":hover": { bgcolor: alpha(palette.primary.main, 0.15) } }
: {} : {}
} }
> >
{delayManager.formatDelay(delay)} {delayManager.formatDelay(delay, timeout)}
</Widget> </Widget>
)} )}

View File

@ -25,6 +25,7 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
proxyLayoutColumn: 6, proxyLayoutColumn: 6,
defaultLatencyTest: "", defaultLatencyTest: "",
autoLogClean: 0, autoLogClean: 0,
defaultLatencyTimeout: 10000,
}); });
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
@ -37,6 +38,7 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
proxyLayoutColumn: verge?.proxy_layout_column || 6, proxyLayoutColumn: verge?.proxy_layout_column || 6,
defaultLatencyTest: verge?.default_latency_test || "", defaultLatencyTest: verge?.default_latency_test || "",
autoLogClean: verge?.auto_log_clean || 0, autoLogClean: verge?.auto_log_clean || 0,
defaultLatencyTimeout: verge?.default_latency_timeout || 10000,
}); });
}, },
close: () => setOpen(false), close: () => setOpen(false),
@ -50,6 +52,7 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
enable_builtin_enhanced: values.enableBuiltinEnhanced, enable_builtin_enhanced: values.enableBuiltinEnhanced,
proxy_layout_column: values.proxyLayoutColumn, proxy_layout_column: values.proxyLayoutColumn,
default_latency_test: values.defaultLatencyTest, default_latency_test: values.defaultLatencyTest,
default_latency_timeout: values.defaultLatencyTimeout,
auto_log_clean: values.autoLogClean as any, auto_log_clean: values.autoLogClean as any,
}); });
setOpen(false); setOpen(false);
@ -179,6 +182,27 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
} }
/> />
</ListItem> </ListItem>
<ListItem sx={{ padding: "5px 2px" }}>
<ListItemText primary={t("Default Latency Timeout")} />
<TextField
size="small"
type="number"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
sx={{ width: 250 }}
value={values.defaultLatencyTimeout}
placeholder="http://1.1.1.1"
onChange={(e) =>
setValues((v) => ({
...v,
defaultLatencyTimeout: parseInt(e.target.value),
}))
}
/>
</ListItem>
</List> </List>
</BaseDialog> </BaseDialog>
); );

View File

@ -152,6 +152,7 @@
"Enable Builtin Enhanced": "Enable Builtin Enhanced", "Enable Builtin Enhanced": "Enable Builtin Enhanced",
"Proxy Layout Column": "Proxy Layout Column", "Proxy Layout Column": "Proxy Layout Column",
"Default Latency Test": "Default Latency Test", "Default Latency Test": "Default Latency Test",
"Defaule Latency Timeout": "Defaule Latency Timeout",
"Auto Log Clean": "Auto Log Clean", "Auto Log Clean": "Auto Log Clean",
"Never Clean": "Never Clean", "Never Clean": "Never Clean",

View File

@ -152,6 +152,7 @@
"Enable Builtin Enhanced": "开启内建增强功能", "Enable Builtin Enhanced": "开启内建增强功能",
"Proxy Layout Column": "代理页布局列数", "Proxy Layout Column": "代理页布局列数",
"Default Latency Test": "默认测试链接", "Default Latency Test": "默认测试链接",
"Default Latency Timeout": "测试超时时间",
"Auto Log Clean": "自动清理日志", "Auto Log Clean": "自动清理日志",
"Never Clean": "不清理", "Never Clean": "不清理",

View File

@ -160,9 +160,17 @@ export async function openWebUrl(url: string) {
return invoke<void>("open_web_url", { url }); return invoke<void>("open_web_url", { url });
} }
export async function cmdGetProxyDelay(name: string, url?: string) { export async function cmdGetProxyDelay(
name: string,
timeout: number,
url?: string
) {
name = encodeURIComponent(name); name = encodeURIComponent(name);
return invoke<{ delay: number }>("clash_api_get_proxy_delay", { name, url }); return invoke<{ delay: number }>("clash_api_get_proxy_delay", {
name,
url,
timeout,
});
} }
export async function cmdTestDelay(url: string) { export async function cmdTestDelay(url: string) {

View File

@ -69,12 +69,12 @@ class DelayManager {
return -1; return -1;
} }
async checkDelay(name: string, group: string) { async checkDelay(name: string, group: string, timeout: number) {
let delay = -1; let delay = -1;
try { try {
const url = this.getUrl(group); const url = this.getUrl(group);
const result = await cmdGetProxyDelay(name, url); const result = await cmdGetProxyDelay(name, timeout, url);
delay = result.delay; delay = result.delay;
} catch { } catch {
delay = 1e6; // error delay = 1e6; // error
@ -84,7 +84,12 @@ class DelayManager {
return delay; return delay;
} }
async checkListDelay(nameList: string[], group: string, concurrency = 36) { async checkListDelay(
nameList: string[],
group: string,
timeout: number,
concurrency = 36
) {
const names = nameList.filter(Boolean); const names = nameList.filter(Boolean);
// 设置正在延迟测试中 // 设置正在延迟测试中
names.forEach((name) => this.setDelay(name, group, -2)); names.forEach((name) => this.setDelay(name, group, -2));
@ -98,7 +103,7 @@ class DelayManager {
const task = names.shift(); const task = names.shift();
if (!task) return; if (!task) return;
current += 1; current += 1;
await this.checkDelay(task, group); await this.checkDelay(task, group, timeout);
current -= 1; current -= 1;
total -= 1; total -= 1;
if (total <= 0) resolve(null); if (total <= 0) resolve(null);
@ -108,15 +113,15 @@ class DelayManager {
}); });
} }
formatDelay(delay: number) { formatDelay(delay: number, timeout = 10000) {
if (delay <= 0) return "Error"; if (delay <= 0) return "Error";
if (delay > 1e5) return "Error"; if (delay > 1e5) return "Error";
if (delay >= 10000) return "Timeout"; // 10s if (delay >= timeout) return "Timeout"; // 10s
return `${delay}`; return `${delay}`;
} }
formatDelayColor(delay: number) { formatDelayColor(delay: number, timeout = 10000) {
if (delay >= 10000) return "error.main"; if (delay >= timeout) return "error.main";
if (delay <= 0) return "error.main"; if (delay <= 0) return "error.main";
if (delay > 500) return "warning.main"; if (delay > 500) return "warning.main";
return "success.main"; return "success.main";

View File

@ -219,6 +219,7 @@ interface IVergeConfig {
}; };
auto_close_connection?: boolean; auto_close_connection?: boolean;
default_latency_test?: string; default_latency_test?: string;
default_latency_timeout?: number;
enable_builtin_enhanced?: boolean; enable_builtin_enhanced?: boolean;
auto_log_clean?: 0 | 1 | 2 | 3; auto_log_clean?: 0 | 1 | 2 | 3;
proxy_layout_column?: number; proxy_layout_column?: number;