From f5e326bde7e445d5429ee77ac0f8fd340e79c378 Mon Sep 17 00:00:00 2001 From: YoVinchen Date: Fri, 8 Aug 2025 21:03:01 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B5=8B=E9=80=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-tauri/src/commands/packycode_nodes.rs | 111 +++++++++++++++++----- src/components/RelayStationManager.tsx | 40 ++++++++ src/lib/api.ts | 10 +- 3 files changed, 132 insertions(+), 29 deletions(-) diff --git a/src-tauri/src/commands/packycode_nodes.rs b/src-tauri/src/commands/packycode_nodes.rs index 93ee292..fb26afc 100644 --- a/src-tauri/src/commands/packycode_nodes.rs +++ b/src-tauri/src/commands/packycode_nodes.rs @@ -115,37 +115,55 @@ pub fn get_all_nodes() -> Vec { } /// 测试单个节点速度(仅测试网络延时,不需要认证) -async fn test_node_speed(node: &PackycodeNode, _token: &str) -> NodeSpeedTestResult { +async fn test_node_speed(node: &PackycodeNode) -> NodeSpeedTestResult { let client = Client::builder() - .timeout(Duration::from_secs(5)) + .timeout(Duration::from_secs(3)) // 减少超时时间 + .danger_accept_invalid_certs(true) // 接受自签名证书 .build() .unwrap_or_else(|_| Client::new()); let start_time = Instant::now(); - // 只需要测试服务器的可达性和延时,使用简单的 HEAD 请求 + // 使用 GET 请求到根路径,这是最简单的 ping 测试 + // 不需要 token,只测试网络延迟 + let url = format!("{}/", node.url.trim_end_matches('/')); + match client - .head(&node.url) + .get(&url) + .timeout(Duration::from_secs(3)) .send() .await { Ok(_response) => { let response_time = start_time.elapsed().as_millis() as u64; - // 只要能连接到服务器就算成功,不管返回什么状态码 + // 只要能连接到服务器就算成功(不管状态码) + // 因为我们只是测试延迟,不是测试 API 功能 + let success = response_time < 3000; // 小于 3 秒就算成功 + NodeSpeedTestResult { node: PackycodeNode { response_time: Some(response_time), - available: Some(true), + available: Some(success), ..node.clone() }, response_time, - success: true, - error: None, + success, + error: if success { None } else { Some("响应时间过长".to_string()) }, } } Err(e) => { let response_time = start_time.elapsed().as_millis() as u64; + + // 如果是超时错误,特别标记 + let error_msg = if e.is_timeout() { + "连接超时".to_string() + } else if e.is_connect() { + "无法连接".to_string() + } else { + format!("网络错误: {}", e) + }; + NodeSpeedTestResult { node: PackycodeNode { response_time: Some(response_time), @@ -154,24 +172,36 @@ async fn test_node_speed(node: &PackycodeNode, _token: &str) -> NodeSpeedTestRes }, response_time, success: false, - error: Some(format!("连接失败: {}", e.to_string())), + error: Some(error_msg), } } } } -/// 测试所有节点速度 +/// 测试所有节点速度(不需要 token,只测试延迟) #[command] -pub async fn test_all_packycode_nodes(token: String) -> Result, String> { +pub async fn test_all_packycode_nodes() -> Result, String> { let nodes = get_all_nodes(); let mut results = Vec::new(); - for node in nodes { - let result = test_node_speed(&node, &token).await; + // 并发测试所有节点 + let futures: Vec<_> = nodes + .iter() + .map(|node| test_node_speed(node)) + .collect(); + + // 等待所有测试完成 + for (i, future) in futures.into_iter().enumerate() { + let result = future.await; + log::info!("节点 {} 测速结果: {}ms, 成功: {}", + nodes[i].name, + result.response_time, + result.success + ); results.push(result); } - // 按响应时间排序 + // 按响应时间排序(成功的节点优先,然后按延迟排序) results.sort_by(|a, b| { match (a.success, b.success) { (true, false) => std::cmp::Ordering::Less, @@ -183,20 +213,48 @@ pub async fn test_all_packycode_nodes(token: String) -> Result Result { +pub async fn auto_select_best_node() -> Result { let nodes = get_all_nodes(); let mut best_node: Option<(PackycodeNode, u64)> = None; - // 只测试直连和备用节点 - for node in nodes.iter().filter(|n| matches!(n.node_type, NodeType::Direct | NodeType::Backup)) { - let result = test_node_speed(node, &token).await; + // 只测试直连和备用节点,过滤掉紧急节点 + let test_nodes: Vec<_> = nodes + .into_iter() + .filter(|n| matches!(n.node_type, NodeType::Direct | NodeType::Backup)) + .collect(); + + log::info!("开始测试 {} 个节点...", test_nodes.len()); + + // 并发测试所有节点 + let futures: Vec<_> = test_nodes + .iter() + .map(|node| test_node_speed(node)) + .collect(); + + // 收集结果并找出最佳节点 + for (i, future) in futures.into_iter().enumerate() { + let result = future.await; + + log::info!("节点 {} - 延迟: {}ms, 可用: {}", + test_nodes[i].name, + result.response_time, + result.success + ); if result.success { match &best_node { - None => best_node = Some((result.node, result.response_time)), + None => { + log::info!("初始最佳节点: {}", result.node.name); + best_node = Some((result.node, result.response_time)); + }, Some((_, best_time)) if result.response_time < *best_time => { + log::info!("发现更快节点: {} ({}ms < {}ms)", + result.node.name, + result.response_time, + best_time + ); best_node = Some((result.node, result.response_time)); } _ => {} @@ -204,9 +262,16 @@ pub async fn auto_select_best_node(token: String) -> Result { + log::info!("最佳节点选择: {} (延迟: {}ms)", node.name, time); + Ok(node) + }, + None => { + log::error!("没有找到可用的节点"); + Err("没有找到可用的节点".to_string()) + } + } } /// 获取节点列表(不测速) diff --git a/src/components/RelayStationManager.tsx b/src/components/RelayStationManager.tsx index e643d63..211cad3 100644 --- a/src/components/RelayStationManager.tsx +++ b/src/components/RelayStationManager.tsx @@ -786,6 +786,26 @@ const CreateStationDialog: React.FC<{ +

@@ -1163,6 +1183,26 @@ const EditStationDialog: React.FC<{ +

diff --git a/src/lib/api.ts b/src/lib/api.ts index 845ea7d..9f77899 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -2349,12 +2349,11 @@ export const api = { /** * Tests all PackyCode nodes and returns speed test results - * @param token - API token for authentication * @returns Promise resolving to array of node speed test results */ - async testAllPackycodeNodes(token: string): Promise { + async testAllPackycodeNodes(): Promise { try { - return await invoke("test_all_packycode_nodes", { token }); + return await invoke("test_all_packycode_nodes"); } catch (error) { console.error("Failed to test PackyCode nodes:", error); throw error; @@ -2363,12 +2362,11 @@ export const api = { /** * Automatically selects the best PackyCode node based on speed - * @param token - API token for authentication * @returns Promise resolving to the best node */ - async autoSelectBestNode(token: string): Promise { + async autoSelectBestNode(): Promise { try { - return await invoke("auto_select_best_node", { token }); + return await invoke("auto_select_best_node"); } catch (error) { console.error("Failed to auto-select best node:", error); throw error;