抽离公共模块
Some checks failed
Build Linux / Build Linux x86_64 (push) Has been cancelled
Build Test / Build Test (Linux) (push) Has been cancelled
Build Test / Build Test (Windows) (push) Has been cancelled
Build Test / Build Test (macOS) (push) Has been cancelled
Build Test / Build Test Summary (push) Has been cancelled

This commit is contained in:
2025-10-27 01:19:14 +08:00
parent 11aac96b53
commit bdf2e499bc
14 changed files with 1188 additions and 380 deletions

View File

@@ -1,9 +1,11 @@
use anyhow::Result;
use reqwest::Client;
use serde::{Deserialize, Serialize};
use std::time::{Duration, Instant};
use tauri::command;
// 导入公共模块
use crate::types::node_test::{NodeTestResult, TestStatus};
use crate::utils::node_tester;
/// PackyCode 节点类型
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
@@ -24,15 +26,6 @@ pub struct PackycodeNode {
pub available: Option<bool>, // 是否可用
}
/// 节点测速结果
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NodeSpeedTestResult {
pub node: PackycodeNode,
pub response_time: u64,
pub success: bool,
pub error: Option<String>,
}
/// 获取所有 PackyCode 节点
pub fn get_all_nodes() -> Vec<PackycodeNode> {
vec![
@@ -122,100 +115,37 @@ pub fn get_all_nodes() -> Vec<PackycodeNode> {
}
/// 测试单个节点速度(仅测试网络延时,不需要认证)
async fn test_node_speed(node: &PackycodeNode) -> NodeSpeedTestResult {
let client = Client::builder()
.timeout(Duration::from_secs(3)) // 减少超时时间
.danger_accept_invalid_certs(true) // 接受自签名证书
.build()
.unwrap_or_else(|_| Client::new());
let start_time = Instant::now();
// 使用 GET 请求到根路径,这是最简单的 ping 测试
// 不需要 token只测试网络延迟
async fn test_node_speed(node: &PackycodeNode) -> NodeTestResult {
let url = format!("{}/", node.url.trim_end_matches('/'));
let mut result = node_tester::test_node_connectivity(&url, 3000).await;
match client
.get(&url)
.timeout(Duration::from_secs(3))
.send()
.await
{
Ok(_response) => {
let response_time = start_time.elapsed().as_millis() as u64;
// 添加节点名称
result.node_name = Some(node.name.clone());
// 只要能连接到服务器就算成功(不管状态码)
// 因为我们只是测试延迟,不是测试 API 功能
let success = response_time < 3000; // 小于 3 秒就算成功
NodeSpeedTestResult {
node: PackycodeNode {
response_time: Some(response_time),
available: Some(success),
..node.clone()
},
response_time,
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),
available: Some(false),
..node.clone()
},
response_time,
success: false,
error: Some(error_msg),
}
}
}
result
}
/// 测试所有节点速度(不需要 token只测试延迟
#[command]
pub async fn test_all_packycode_nodes() -> Result<Vec<NodeSpeedTestResult>, String> {
pub async fn test_all_packycode_nodes() -> Result<Vec<NodeTestResult>, String> {
let nodes = get_all_nodes();
let mut results = Vec::new();
let urls: Vec<String> = nodes
.iter()
.map(|n| format!("{}/", n.url.trim_end_matches('/')))
.collect();
// 并发测试所有节点
let futures: Vec<_> = nodes.iter().map(|node| test_node_speed(node)).collect();
// 使用公共批量测试
let mut results = node_tester::test_nodes_batch(urls, 3000).await;
// 等待所有测试完成
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);
// 添加节点名称
for (i, result) in results.iter_mut().enumerate() {
if let Some(node) = nodes.get(i) {
result.node_name = Some(node.name.clone());
}
}
// 按响应时间排序(成功的节点优先,然后按延迟排序
results.sort_by(|a, b| match (a.success, b.success) {
(true, false) => std::cmp::Ordering::Less,
(false, true) => std::cmp::Ordering::Greater,
_ => a.response_time.cmp(&b.response_time),
});
// 按响应时间排序(成功的节点优先)
node_tester::sort_by_response_time(&mut results);
Ok(results)
}
@@ -224,7 +154,6 @@ pub async fn test_all_packycode_nodes() -> Result<Vec<NodeSpeedTestResult>, Stri
#[command]
pub async fn auto_select_best_node() -> Result<PackycodeNode, String> {
let nodes = get_all_nodes();
let mut best_node: Option<(PackycodeNode, u64)> = None;
// 只测试直连和备用节点,过滤掉紧急节点
let test_nodes: Vec<_> = nodes
@@ -234,52 +163,36 @@ pub async fn auto_select_best_node() -> Result<PackycodeNode, String> {
log::info!("开始测试 {} 个节点...", test_nodes.len());
// 并发测试所有节点
let futures: Vec<_> = test_nodes
// 提取 URL 列表
let urls: Vec<String> = test_nodes
.iter()
.map(|node| test_node_speed(node))
.map(|n| format!("{}/", n.url.trim_end_matches('/')))
.collect();
// 收集结果并找出最佳节点
for (i, future) in futures.into_iter().enumerate() {
let result = future.await;
// 使用公共批量测试
let results = node_tester::test_nodes_batch(urls, 3000).await;
// 查找最快的节点
if let Some(fastest) = node_tester::find_fastest_node(&results) {
// 根据 URL 找到对应的节点
let best_node = test_nodes
.into_iter()
.find(|n| {
let node_url = format!("{}/", n.url.trim_end_matches('/'));
node_url == fastest.url
})
.ok_or_else(|| "未找到匹配的节点".to_string())?;
log::info!(
"节点 {} - 延迟: {}ms, 可用: {}",
test_nodes[i].name,
result.response_time,
result.success
"最佳节点选择: {} (延迟: {}ms)",
best_node.name,
fastest.response_time_ms.unwrap_or(0)
);
if result.success {
match &best_node {
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));
}
_ => {}
}
}
}
match best_node {
Some((node, time)) => {
log::info!("最佳节点选择: {} (延迟: {}ms)", node.name, time);
Ok(node)
}
None => {
log::error!("没有找到可用的节点");
Err("没有找到可用的节点".to_string())
}
Ok(best_node)
} else {
log::error!("没有找到可用的节点");
Err("没有找到可用的节点".to_string())
}
}