diff --git a/src-tauri/src/launcher_config/commands.rs b/src-tauri/src/launcher_config/commands.rs index 06cc85ae..7587ed66 100644 --- a/src-tauri/src/launcher_config/commands.rs +++ b/src-tauri/src/launcher_config/commands.rs @@ -279,6 +279,11 @@ pub async fn check_launcher_update(app: AppHandle) -> XMCLResult XMCLResult { + crate::launcher_config::helpers::proxy::test_proxy_connectivity(&app, test_url).await +} + #[tauri::command] pub async fn download_launcher_update(app: AppHandle, version: VersionMetaInfo) -> XMCLResult<()> { if version.version.is_empty() || version.version == "up2date" { diff --git a/src-tauri/src/launcher_config/helpers/mod.rs b/src-tauri/src/launcher_config/helpers/mod.rs index 68167046..7b2ad8d4 100644 --- a/src-tauri/src/launcher_config/helpers/mod.rs +++ b/src-tauri/src/launcher_config/helpers/mod.rs @@ -1,3 +1,4 @@ pub mod java; pub mod misc; +pub mod proxy; pub mod updater; diff --git a/src-tauri/src/launcher_config/helpers/proxy.rs b/src-tauri/src/launcher_config/helpers/proxy.rs new file mode 100644 index 00000000..d2646155 --- /dev/null +++ b/src-tauri/src/launcher_config/helpers/proxy.rs @@ -0,0 +1,41 @@ +use crate::error::{XMCLError, XMCLResult}; +use crate::launcher_config::models::{LauncherConfig, ProxyType}; +use std::sync::Mutex; +use std::time::Duration; +use tauri::{AppHandle, Manager}; +use tauri_plugin_http::reqwest; + +pub async fn test_proxy_connectivity(app: &AppHandle, test_url: String) -> XMCLResult { + let config_binding = app.state::>(); + let (proxy_enabled, proxy_type, proxy_host, proxy_port) = { + let config = config_binding.lock()?; + ( + config.download.proxy.enabled, + config.download.proxy.selected_type.clone(), + config.download.proxy.host.clone(), + config.download.proxy.port, + ) + }; + + if !proxy_enabled || proxy_host.is_empty() || proxy_port == 0 { + return Ok(false); + } + + let proxy_url = match proxy_type { + ProxyType::Http => format!("http://{}:{}", proxy_host, proxy_port), + ProxyType::Socks => format!("socks5://{}:{}", proxy_host, proxy_port), + }; + + let proxy = reqwest::Proxy::all(&proxy_url).map_err(|e| XMCLError::from(e))?; + + let client = reqwest::Client::builder() + .proxy(proxy) + .timeout(Duration::from_secs(10)) + .build() + .map_err(|e| XMCLError::from(e))?; + + match client.get(&test_url).send().await { + Ok(response) => Ok(response.status().is_success()), + Err(_) => Ok(false), + } +} diff --git a/src-tauri/src/launcher_config/helpers/updater.rs b/src-tauri/src/launcher_config/helpers/updater.rs index 4cd203b5..e268c721 100644 --- a/src-tauri/src/launcher_config/helpers/updater.rs +++ b/src-tauri/src/launcher_config/helpers/updater.rs @@ -1,14 +1,10 @@ -use crate::error::{XMCLError, XMCLResult}; +use crate::error::XMCLResult; use crate::launcher_config::models::{LauncherConfig, LauncherConfigError}; use crate::tasks::commands::schedule_progressive_task_group; use crate::tasks::download::DownloadParam; use crate::tasks::PTaskParam; use serde_json::Value; -use std::fs; -use std::path::PathBuf; -use std::process::Command; use std::sync::Mutex; -use tauri::path::BaseDirectory; use tauri::{AppHandle, Manager}; use tauri_plugin_http::reqwest; diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index fb4d7c27..e9989c6b 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -74,6 +74,7 @@ pub async fn run() { launcher_config::commands::check_game_directory, launcher_config::commands::clear_download_cache, launcher_config::commands::check_launcher_update, + launcher_config::commands::test_proxy_connection, launcher_config::commands::download_launcher_update, launcher_config::commands::install_launcher_update, account::commands::retrieve_player_list, diff --git a/src/locales/en.json b/src/locales/en.json index 7f42eade..e814f225 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -543,7 +543,18 @@ }, "port": { "title": "Port" + }, + "test": { + "title": "Test Proxy Connection", + "placeholder": "https://www.baidu.com", + "button": "Test", + "success": "Connection successful", + "failed": "Connection failed" } + }, + "test": { + "success": "Proxy connection successful", + "failed": "Proxy connection failed" } } }, diff --git a/src/locales/fr.json b/src/locales/fr.json index 45bdfaa5..e45f3b88 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -462,7 +462,18 @@ }, "port": { "title": "Port" + }, + "test": { + "title": "Tester la connexion du proxy", + "placeholder": "https://www.baidu.com", + "button": "Tester", + "success": "Connexion réussie", + "failed": "Connexion échouée" } + }, + "test": { + "success": "Connexion au proxy réussie", + "failed": "Connexion au proxy échouée" } } }, diff --git a/src/locales/ja.json b/src/locales/ja.json index e6325aa8..4bfd7584 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -459,7 +459,18 @@ }, "port": { "title": "ポート" + }, + "test": { + "title": "プロキシ接続をテスト", + "placeholder": "https://www.baidu.com", + "button": "テスト", + "success": "接続成功", + "failed": "接続失敗" } + }, + "test": { + "success": "プロキシ接続成功", + "failed": "プロキシ接続失敗" } } }, diff --git a/src/locales/zh-Hans.json b/src/locales/zh-Hans.json index fa4a3f0a..10e03681 100644 --- a/src/locales/zh-Hans.json +++ b/src/locales/zh-Hans.json @@ -543,7 +543,18 @@ }, "port": { "title": "端口" + }, + "test": { + "title": "测试代理连接", + "placeholder": "https://www.baidu.com", + "button": "测试", + "success": "连接成功", + "failed": "连接失败" } + }, + "test": { + "success": "代理连接成功", + "failed": "代理连接失败" } } }, diff --git a/src/locales/zh-Hant.json b/src/locales/zh-Hant.json index 9b9fd8ff..bda06385 100644 --- a/src/locales/zh-Hant.json +++ b/src/locales/zh-Hant.json @@ -543,7 +543,18 @@ }, "port": { "title": "埠" + }, + "test": { + "title": "測試代理連線", + "placeholder": "https://www.baidu.com", + "button": "測試", + "success": "連線成功", + "failed": "連線失敗" } + }, + "test": { + "success": "代理連線成功", + "failed": "代理連線失敗" } } }, diff --git a/src/pages/settings/download.tsx b/src/pages/settings/download.tsx index c9cdce3c..ce85b27f 100644 --- a/src/pages/settings/download.tsx +++ b/src/pages/settings/download.tsx @@ -71,6 +71,8 @@ const DownloadSettingsPage = () => { ); const [isClearingDownloadCache, setIsClearingDownloadCache] = useState(false); + const [isTestingProxy, setIsTestingProxy] = useState(false); + const [testUrl, setTestUrl] = useState(""); const sourceStrategyTypes = ["auto", "official", "mirror"]; const proxyTypeOptions = [ @@ -123,6 +125,33 @@ const DownloadSettingsPage = () => { closeSharedModal("generic-confirm"); }; + const handleTestProxy = async () => { + if (isTestingProxy) return; + + const url = testUrl.trim() || "https://www.baidu.com"; + setIsTestingProxy(true); + + try { + const response = await ConfigService.testProxyConnection(url); + if (response.status === "success") { + toast({ + title: response.data + ? t("DownloadSettingPage.proxy.test.success") + : t("DownloadSettingPage.proxy.test.failed"), + status: response.data ? "success" : "error", + }); + } else { + toast({ + title: t("DownloadSettingPage.proxy.test.failed"), + description: response.message, + status: "error", + }); + } + } finally { + setIsTestingProxy(false); + } + }; + const downloadSettingGroups: OptionItemGroupProps[] = [ { items: [ @@ -446,6 +475,31 @@ const DownloadSettingsPage = () => { ), }, + { + title: t("DownloadSettingPage.proxy.settings.test.title"), + children: ( + + setTestUrl(event.target.value)} + /> + + + ), + }, ] : []), ], diff --git a/src/services/config.ts b/src/services/config.ts index 7e71ce60..29fd77ce 100644 --- a/src/services/config.ts +++ b/src/services/config.ts @@ -166,8 +166,18 @@ export class ConfigService { return await invoke("download_launcher_update", { version }); } - /** - * INSTALL the launcher update. + /** * TEST the proxy connection. + * @param {string} testUrl URL to test the proxy connection. + * @returns {Promise>} Returns true if the connection is successful. + */ + @responseHandler("config") + static async testProxyConnection( + testUrl: string + ): Promise> { + return await invoke("test_proxy_connection", { testUrl }); + } + + /** * INSTALL the launcher update. * @param {string} downloadedFilename The name of the downloaded new version file. * @param {boolean} restart Whether to restart the launcher now. * @returns {Promise>} Returns void if the update process is started successfully.