diff --git a/UPDATELOG.md b/UPDATELOG.md index b8c7bf07..6a4c9411 100644 --- a/UPDATELOG.md +++ b/UPDATELOG.md @@ -41,6 +41,7 @@ - 改进核心启动/停止/重启后的状态刷新机制 - 修复 `Windows` 安装器删除用户自启问题 - 修复 `Windows` 安装器参数使用错误问题 +- 修复 `IPC` 迁移后测速功能异常 ### 🔧 技术改进 diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 06df2440..11749073 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -888,21 +888,11 @@ dependencies = [ [[package]] name = "bzip2" -version = "0.5.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49ecfb22d906f800d4fe833b6282cf4dc1c298f5057ca0b5445e5c209735ca47" +checksum = "bea8dcd42434048e4f7a304411d9273a411f647446c1234a65ce0554923f4cff" dependencies = [ - "bzip2-sys", -] - -[[package]] -name = "bzip2-sys" -version = "0.1.13+1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" -dependencies = [ - "cc", - "pkg-config", + "libbz2-rs-sys", ] [[package]] @@ -2833,28 +2823,13 @@ checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" dependencies = [ "base64 0.21.7", "bytes", - "headers-core 0.2.0", + "headers-core", "http 0.2.12", "httpdate", "mime", "sha1", ] -[[package]] -name = "headers" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb" -dependencies = [ - "base64 0.22.1", - "bytes", - "headers-core 0.3.0", - "http 1.3.1", - "httpdate", - "mime", - "sha1", -] - [[package]] name = "headers-core" version = "0.2.0" @@ -2864,15 +2839,6 @@ dependencies = [ "http 0.2.12", ] -[[package]] -name = "headers-core" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" -dependencies = [ - "http 1.3.1", -] - [[package]] name = "heck" version = "0.4.1" @@ -3745,22 +3711,18 @@ dependencies = [ [[package]] name = "kode-bridge" -version = "0.1.5" +version = "0.1.6-rc" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "971cfb2bdf5db3721fc822240b4e6e05b5d3aa8c85eb5f7ad4dc25ed0a3ad7e0" +checksum = "11bf66a2690fdac4a30e3e60c5bb7e4479db318f2cd6eb95a353acf79d35855a" dependencies = [ "bytes", "futures", - "headers 0.4.1", "http 1.3.1", - "http-body 1.0.1", - "http-body-util", "httparse", - "hyper 1.6.0", "interprocess", - "once_cell", "parking_lot", "pin-project-lite", + "rand 0.8.5", "serde", "serde_json", "thiserror 2.0.12", @@ -3819,6 +3781,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "libbz2-rs-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775bf80d5878ab7c2b1080b5351a48b2f737d9f6f8b383574eebcc22be0dfccb" + [[package]] name = "libc" version = "0.2.174" @@ -5381,6 +5349,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "ppmd-rust" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c834641d8ad1b348c9ee86dec3b9840d805acd5f24daa5f90c788951a52ff59b" + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -6940,9 +6914,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.35.2" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3ffa3e4ff2b324a57f7aeb3c349656c7b127c3c189520251a648102a92496e" +checksum = "252800745060e7b9ffb7b2badbd8b31cfa4aa2e61af879d0a3bf2a317c20217d" dependencies = [ "libc", "memchr", @@ -8549,7 +8523,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "headers 0.3.9", + "headers", "http 0.2.12", "hyper 0.14.32", "log", @@ -9839,9 +9813,9 @@ dependencies = [ [[package]] name = "zip" -version = "4.2.0" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ab361742de920c5535880f89bbd611ee62002bf11341d16a5f057bb8ba6899" +checksum = "9aed4ac33e8eb078c89e6cbb1d5c4c7703ec6d299fc3e7c3695af8f8b423468b" dependencies = [ "aes", "arbitrary", @@ -9856,6 +9830,7 @@ dependencies = [ "liblzma", "memchr", "pbkdf2", + "ppmd-rust", "sha1", "time", "zeroize", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index d9506f42..d1e00707 100755 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -13,7 +13,7 @@ build = "build.rs" identifier = "io.github.clash-verge-rev.clash-verge-rev" [build-dependencies] -tauri-build = { version = "2.3.0", features = [] } +tauri-build = { version = "2.3.1", features = [] } [dependencies] warp = "0.3.7" @@ -25,9 +25,9 @@ dunce = "1.0.5" log4rs = "1.3.0" nanoid = "0.4" chrono = "0.4.41" -sysinfo = "=0.35.2" +sysinfo = { version = "0.36.1", features = ["network", "system"] } boa_engine = "0.20.0" -serde_json = "1.0.140" +serde_json = "1.0.141" serde_yaml = "0.9.34" once_cell = "1.21.3" lazy_static = "1.5.0" @@ -47,23 +47,23 @@ reqwest = { version = "0.12.22", features = ["json", "rustls-tls", "cookies"] } regex = "1.11.1" sysproxy = { git = "https://github.com/clash-verge-rev/sysproxy-rs" } image = "0.25.6" -tauri = { version = "2.6.2", features = [ +tauri = { version = "2.7.0", features = [ "protocol-asset", "devtools", "tray-icon", "image-ico", "image-png", ] } -network-interface = { version = "2.0.1", features = ["serde"] } +network-interface = { version = "2.0.2", features = ["serde"] } tauri-plugin-shell = "2.3.0" -tauri-plugin-dialog = "2.3.0" -tauri-plugin-fs = "2.4.0" +tauri-plugin-dialog = "2.3.1" +tauri-plugin-fs = "2.4.1" tauri-plugin-process = "2.3.0" tauri-plugin-clipboard-manager = "2.3.0" -tauri-plugin-deep-link = "2.4.0" +tauri-plugin-deep-link = "2.4.1" tauri-plugin-devtools = "2.0.0" -tauri-plugin-window-state = "2.3.0" -zip = "=4.2.0" +tauri-plugin-window-state = "2.4.0" +zip = "4.3.0" reqwest_dav = "0.2.1" aes-gcm = { version = "0.10.3", features = ["std"] } base64 = "0.22.1" @@ -77,7 +77,7 @@ hmac = "0.12.1" sha2 = "0.10.9" hex = "0.4.3" scopeguard = "1.2.0" -kode-bridge = "0.1.5" +kode-bridge = "0.1.6-rc" dashmap = "6.1.0" tauri-plugin-notification = "2.3.0" diff --git a/src-tauri/src/ipc/general.rs b/src-tauri/src/ipc/general.rs index feff490d..647edca1 100644 --- a/src-tauri/src/ipc/general.rs +++ b/src-tauri/src/ipc/general.rs @@ -2,9 +2,18 @@ use kode_bridge::{ errors::{AnyError, AnyResult}, IpcHttpClient, LegacyResponse, }; -use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; +use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS}; use std::sync::OnceLock; +// 定义用于URL路径的编码集合,只编码真正必要的字符 +const URL_PATH_ENCODE_SET: &AsciiSet = &CONTROLS + .add(b' ') // 空格 + .add(b'/') // 斜杠 + .add(b'?') // 问号 + .add(b'#') // 井号 + .add(b'&') // 和号 + .add(b'%'); // 百分号 + use crate::{ logging, utils::{dirs::ipc_path, logging::Type}, @@ -108,7 +117,7 @@ impl IpcManager { } pub async fn delete_connection(&self, id: &str) -> AnyResult<()> { - let encoded_id = utf8_percent_encode(id, NON_ALPHANUMERIC).to_string(); + let encoded_id = utf8_percent_encode(id, URL_PATH_ENCODE_SET).to_string(); let url = format!("/connections/{encoded_id}"); let response = self.send_request("DELETE", &url, None).await?; if response["code"] == 204 { @@ -176,11 +185,13 @@ impl IpcManager { ) -> AnyResult { let test_url = test_url.unwrap_or_else(|| "https://cp.cloudflare.com/generate_204".to_string()); - let encoded_name = utf8_percent_encode(name, NON_ALPHANUMERIC).to_string(); - let encoded_test_url = utf8_percent_encode(&test_url, NON_ALPHANUMERIC).to_string(); - let url = format!("/proxies/{encoded_name}/delay?url={encoded_test_url}&timeout={timeout}"); - let response = self.send_request("GET", &url, None).await?; - Ok(response) + + let encoded_name = utf8_percent_encode(name, URL_PATH_ENCODE_SET).to_string(); + // 测速URL不再编码,直接传递 + let url = format!("/proxies/{encoded_name}/delay?url={test_url}&timeout={timeout}"); + + let response = self.send_request("GET", &url, None).await; + response } // 版本和配置相关 @@ -236,7 +247,7 @@ impl IpcManager { } pub async fn update_rule_provider(&self, name: &str) -> AnyResult<()> { - let encoded_name = utf8_percent_encode(name, NON_ALPHANUMERIC).to_string(); + let encoded_name = utf8_percent_encode(name, URL_PATH_ENCODE_SET).to_string(); let url = format!("/providers/rules/{encoded_name}"); let response = self.send_request("PUT", &url, None).await?; if response["code"] == 204 { @@ -254,7 +265,7 @@ impl IpcManager { // 代理相关 pub async fn update_proxy(&self, group: &str, proxy: &str) -> AnyResult<()> { // 使用 percent-encoding 进行正确的 URL 编码 - let encoded_group = utf8_percent_encode(group, NON_ALPHANUMERIC).to_string(); + let encoded_group = utf8_percent_encode(group, URL_PATH_ENCODE_SET).to_string(); let url = format!("/proxies/{encoded_group}"); let payload = serde_json::json!({ "name": proxy @@ -299,7 +310,7 @@ impl IpcManager { } pub async fn proxy_provider_health_check(&self, name: &str) -> AnyResult<()> { - let encoded_name = utf8_percent_encode(name, NON_ALPHANUMERIC).to_string(); + let encoded_name = utf8_percent_encode(name, URL_PATH_ENCODE_SET).to_string(); let url = format!("/providers/proxies/{encoded_name}/healthcheck"); let response = self.send_request("GET", &url, None).await?; if response["code"] == 204 { @@ -315,7 +326,7 @@ impl IpcManager { } pub async fn update_proxy_provider(&self, name: &str) -> AnyResult<()> { - let encoded_name = utf8_percent_encode(name, NON_ALPHANUMERIC).to_string(); + let encoded_name = utf8_percent_encode(name, URL_PATH_ENCODE_SET).to_string(); let url = format!("/providers/proxies/{encoded_name}"); let response = self.send_request("PUT", &url, None).await?; if response["code"] == 204 { @@ -338,11 +349,13 @@ impl IpcManager { timeout: i32, ) -> AnyResult { let test_url = url.unwrap_or_else(|| "https://cp.cloudflare.com/generate_204".to_string()); - let encoded_group_name = utf8_percent_encode(group_name, NON_ALPHANUMERIC).to_string(); - let encoded_test_url = utf8_percent_encode(&test_url, NON_ALPHANUMERIC).to_string(); - let url = - format!("/group/{encoded_group_name}/delay?url={encoded_test_url}&timeout={timeout}"); - self.send_request("GET", &url, None).await + + let encoded_group_name = utf8_percent_encode(group_name, URL_PATH_ENCODE_SET).to_string(); + // 测速URL不再编码,直接传递 + let url = format!("/group/{encoded_group_name}/delay?url={test_url}&timeout={timeout}"); + + let response = self.send_request("GET", &url, None).await; + response } // 调试相关 diff --git a/src/services/cmds.ts b/src/services/cmds.ts index b3a5b594..d4021c3e 100644 --- a/src/services/cmds.ts +++ b/src/services/cmds.ts @@ -512,12 +512,9 @@ export async function cmdGetProxyDelay( ) { // 确保URL不为空 const testUrl = url || "https://cp.cloudflare.com/generate_204"; - console.log( - `[API] 调用延迟测试API,代理: ${name}, 超时: ${timeout}ms, URL: ${testUrl}`, - ); try { - name = encodeURIComponent(name); + // 不再在前端编码代理名称,由后端统一处理编码 const result = await invoke<{ delay: number }>( "clash_api_get_proxy_delay", { @@ -529,20 +526,12 @@ export async function cmdGetProxyDelay( // 验证返回结果中是否有delay字段,并且值是一个有效的数字 if (result && typeof result.delay === "number") { - console.log( - `[API] 延迟测试API调用成功,代理: ${name}, 延迟: ${result.delay}ms`, - ); return result; } else { - console.error( - `[API] 延迟测试API返回无效结果,代理: ${name}, 结果:`, - result, - ); // 返回一个有效的结果对象,但标记为超时 return { delay: 1e6 }; } } catch (error) { - console.error(`[API] 延迟测试API调用失败,代理: ${name}`, error); // 返回一个有效的结果对象,但标记为错误 return { delay: 1e6 }; } diff --git a/src/services/types.d.ts b/src/services/types.d.ts index 2fcf0ada..b0c27d9c 100644 --- a/src/services/types.d.ts +++ b/src/services/types.d.ts @@ -181,7 +181,7 @@ interface ISystemMonitorOverview { }; is_fresh: boolean; }; - overall_status: "active" | "inactive" | "error" | "unknown"; + overall_status: "active" | "inactive" | "error" | "unknown" | "healthy"; } // 类型安全的数据验证器 diff --git a/src/utils/data-validator.ts b/src/utils/data-validator.ts index 219d94e4..4bdbd12b 100644 --- a/src/utils/data-validator.ts +++ b/src/utils/data-validator.ts @@ -138,7 +138,7 @@ export class SystemMonitorValidator implements ISystemMonitorOverviewValidator { private validateOverallStatus(status: any): boolean { return ( typeof status === "string" && - ["active", "inactive", "error", "unknown"].includes(status) + ["active", "inactive", "error", "unknown", "healthy"].includes(status) ); } @@ -190,12 +190,12 @@ export class SystemMonitorValidator implements ISystemMonitorOverviewValidator { private sanitizeOverallStatus( status: any, - ): "active" | "inactive" | "error" | "unknown" { + ): "active" | "inactive" | "error" | "unknown" | "healthy" { if ( typeof status === "string" && - ["active", "inactive", "error", "unknown"].includes(status) + ["active", "inactive", "error", "unknown", "healthy"].includes(status) ) { - return status as "active" | "inactive" | "error" | "unknown"; + return status as "active" | "inactive" | "error" | "unknown" | "healthy"; } return "unknown"; }