From b7ae5f0ac97c3cf101e6fe0eca3a0c7ae74aafaa Mon Sep 17 00:00:00 2001 From: wonfen Date: Fri, 3 Apr 2026 06:26:24 +0800 Subject: [PATCH] fix: handle edge cases and add missing i18n --- crates/clash-verge-i18n/locales/ar.yml | 5 ++ crates/clash-verge-i18n/locales/de.yml | 5 ++ crates/clash-verge-i18n/locales/en.yml | 5 ++ crates/clash-verge-i18n/locales/es.yml | 5 ++ crates/clash-verge-i18n/locales/fa.yml | 5 ++ crates/clash-verge-i18n/locales/id.yml | 5 ++ crates/clash-verge-i18n/locales/jp.yml | 5 ++ crates/clash-verge-i18n/locales/ko.yml | 5 ++ crates/clash-verge-i18n/locales/ru.yml | 5 ++ crates/clash-verge-i18n/locales/tr.yml | 5 ++ crates/clash-verge-i18n/locales/tt.yml | 5 ++ crates/clash-verge-i18n/locales/zh.yml | 5 ++ crates/clash-verge-i18n/locales/zhtw.yml | 5 ++ src-tauri/src/core/updater.rs | 68 ++++++++++++++++++++---- 14 files changed, 123 insertions(+), 10 deletions(-) diff --git a/crates/clash-verge-i18n/locales/ar.yml b/crates/clash-verge-i18n/locales/ar.yml index 257ee47a4..3be3bbfe9 100644 --- a/crates/clash-verge-i18n/locales/ar.yml +++ b/crates/clash-verge-i18n/locales/ar.yml @@ -26,6 +26,11 @@ notifications: appHidden: title: تم إخفاء التطبيق body: Clash Verge يعمل في الخلفية. + updateReady: + title: Clash Verge Update + body: A new version (v{version}) has been downloaded and is ready to install. + installNow: Install Now + later: Later service: adminInstallPrompt: يتطلب تثبيت خدمة Clash Verge صلاحيات المسؤول. adminUninstallPrompt: يتطلب إلغاء تثبيت خدمة Clash Verge صلاحيات المسؤول. diff --git a/crates/clash-verge-i18n/locales/de.yml b/crates/clash-verge-i18n/locales/de.yml index c87d618dc..a345f0967 100644 --- a/crates/clash-verge-i18n/locales/de.yml +++ b/crates/clash-verge-i18n/locales/de.yml @@ -26,6 +26,11 @@ notifications: appHidden: title: Anwendung ausgeblendet body: Clash Verge läuft im Hintergrund. + updateReady: + title: Clash Verge Update + body: A new version (v{version}) has been downloaded and is ready to install. + installNow: Install Now + later: Later service: adminInstallPrompt: Für die Installation des Clash-Verge-Dienstes sind Administratorrechte erforderlich. adminUninstallPrompt: Für die Deinstallation des Clash-Verge-Dienstes sind Administratorrechte erforderlich. diff --git a/crates/clash-verge-i18n/locales/en.yml b/crates/clash-verge-i18n/locales/en.yml index 0e3e855b8..5d0f0e679 100644 --- a/crates/clash-verge-i18n/locales/en.yml +++ b/crates/clash-verge-i18n/locales/en.yml @@ -26,6 +26,11 @@ notifications: appHidden: title: Application Hidden body: Clash Verge is running in the background. + updateReady: + title: Clash Verge Update + body: A new version (v{version}) has been downloaded and is ready to install. + installNow: Install Now + later: Later service: adminInstallPrompt: Installing the Clash Verge service requires administrator privileges. adminUninstallPrompt: Uninstalling the Clash Verge service requires administrator privileges. diff --git a/crates/clash-verge-i18n/locales/es.yml b/crates/clash-verge-i18n/locales/es.yml index 52a251eec..f95ea264a 100644 --- a/crates/clash-verge-i18n/locales/es.yml +++ b/crates/clash-verge-i18n/locales/es.yml @@ -26,6 +26,11 @@ notifications: appHidden: title: Aplicación oculta body: Clash Verge se está ejecutando en segundo plano. + updateReady: + title: Clash Verge Update + body: A new version (v{version}) has been downloaded and is ready to install. + installNow: Install Now + later: Later service: adminInstallPrompt: Instalar el servicio de Clash Verge requiere privilegios de administrador. adminUninstallPrompt: Desinstalar el servicio de Clash Verge requiere privilegios de administrador. diff --git a/crates/clash-verge-i18n/locales/fa.yml b/crates/clash-verge-i18n/locales/fa.yml index 8e4954e5f..a521cdddd 100644 --- a/crates/clash-verge-i18n/locales/fa.yml +++ b/crates/clash-verge-i18n/locales/fa.yml @@ -26,6 +26,11 @@ notifications: appHidden: title: برنامه پنهان شد body: Clash Verge در پس‌زمینه در حال اجراست. + updateReady: + title: Clash Verge Update + body: A new version (v{version}) has been downloaded and is ready to install. + installNow: Install Now + later: Later service: adminInstallPrompt: نصب سرویس Clash Verge به دسترسی مدیر نیاز دارد. adminUninstallPrompt: حذف سرویس Clash Verge به دسترسی مدیر نیاز دارد. diff --git a/crates/clash-verge-i18n/locales/id.yml b/crates/clash-verge-i18n/locales/id.yml index 3d206babf..151246d2b 100644 --- a/crates/clash-verge-i18n/locales/id.yml +++ b/crates/clash-verge-i18n/locales/id.yml @@ -26,6 +26,11 @@ notifications: appHidden: title: Aplikasi Disembunyikan body: Clash Verge berjalan di latar belakang. + updateReady: + title: Clash Verge Update + body: A new version (v{version}) has been downloaded and is ready to install. + installNow: Install Now + later: Later service: adminInstallPrompt: Menginstal layanan Clash Verge memerlukan hak administrator. adminUninstallPrompt: Menghapus instalasi layanan Clash Verge memerlukan hak administrator. diff --git a/crates/clash-verge-i18n/locales/jp.yml b/crates/clash-verge-i18n/locales/jp.yml index 6612d49fe..965ffbd31 100644 --- a/crates/clash-verge-i18n/locales/jp.yml +++ b/crates/clash-verge-i18n/locales/jp.yml @@ -26,6 +26,11 @@ notifications: appHidden: title: アプリが非表示 body: Clash Verge はバックグラウンドで実行中です。 + updateReady: + title: Clash Verge Update + body: A new version (v{version}) has been downloaded and is ready to install. + installNow: Install Now + later: Later service: adminInstallPrompt: Clash Verge サービスのインストールには管理者権限が必要です。 adminUninstallPrompt: Clash Verge サービスのアンインストールには管理者権限が必要です。 diff --git a/crates/clash-verge-i18n/locales/ko.yml b/crates/clash-verge-i18n/locales/ko.yml index 4d50b4a7a..38d61818d 100644 --- a/crates/clash-verge-i18n/locales/ko.yml +++ b/crates/clash-verge-i18n/locales/ko.yml @@ -26,6 +26,11 @@ notifications: appHidden: title: 앱이 숨겨짐 body: Clash Verge가 백그라운드에서 실행 중입니다. + updateReady: + title: Clash Verge Update + body: A new version (v{version}) has been downloaded and is ready to install. + installNow: Install Now + later: Later service: adminInstallPrompt: Clash Verge 서비스 설치에는 관리자 권한이 필요합니다. adminUninstallPrompt: Clash Verge 서비스 제거에는 관리자 권한이 필요합니다. diff --git a/crates/clash-verge-i18n/locales/ru.yml b/crates/clash-verge-i18n/locales/ru.yml index 2c80376fe..77c7647f7 100644 --- a/crates/clash-verge-i18n/locales/ru.yml +++ b/crates/clash-verge-i18n/locales/ru.yml @@ -26,6 +26,11 @@ notifications: appHidden: title: Приложение скрыто body: Clash Verge работает в фоновом режиме. + updateReady: + title: Clash Verge Update + body: A new version (v{version}) has been downloaded and is ready to install. + installNow: Install Now + later: Later service: adminInstallPrompt: Для установки службы Clash Verge требуются права администратора. adminUninstallPrompt: Для удаления службы Clash Verge требуются права администратора. diff --git a/crates/clash-verge-i18n/locales/tr.yml b/crates/clash-verge-i18n/locales/tr.yml index da2cc2de6..b6804fde5 100644 --- a/crates/clash-verge-i18n/locales/tr.yml +++ b/crates/clash-verge-i18n/locales/tr.yml @@ -26,6 +26,11 @@ notifications: appHidden: title: Uygulama Gizlendi body: Clash Verge arka planda çalışıyor. + updateReady: + title: Clash Verge Update + body: A new version (v{version}) has been downloaded and is ready to install. + installNow: Install Now + later: Later service: adminInstallPrompt: Clash Verge hizmetini kurmak için yönetici ayrıcalıkları gerekir. adminUninstallPrompt: Clash Verge hizmetini kaldırmak için yönetici ayrıcalıkları gerekir. diff --git a/crates/clash-verge-i18n/locales/tt.yml b/crates/clash-verge-i18n/locales/tt.yml index aeafd27ee..6b0283e10 100644 --- a/crates/clash-verge-i18n/locales/tt.yml +++ b/crates/clash-verge-i18n/locales/tt.yml @@ -26,6 +26,11 @@ notifications: appHidden: title: Кушымта яшерелде body: Clash Verge фон режимында эшли. + updateReady: + title: Clash Verge Update + body: A new version (v{version}) has been downloaded and is ready to install. + installNow: Install Now + later: Later service: adminInstallPrompt: Clash Verge хезмәтен урнаштыру өчен администратор хокуклары кирәк. adminUninstallPrompt: Clash Verge хезмәтен бетерү өчен администратор хокуклары кирәк. diff --git a/crates/clash-verge-i18n/locales/zh.yml b/crates/clash-verge-i18n/locales/zh.yml index e5148bdf7..5a23340e0 100644 --- a/crates/clash-verge-i18n/locales/zh.yml +++ b/crates/clash-verge-i18n/locales/zh.yml @@ -26,6 +26,11 @@ notifications: appHidden: title: 应用已隐藏 body: Clash Verge 正在后台运行。 + updateReady: + title: Clash Verge 更新 + body: 新版本 (v{version}) 已下载完成,是否立即安装? + installNow: 立即安装 + later: 稍后 service: adminInstallPrompt: 安装 Clash Verge 服务需要管理员权限 adminUninstallPrompt: 卸载 Clash Verge 服务需要管理员权限 diff --git a/crates/clash-verge-i18n/locales/zhtw.yml b/crates/clash-verge-i18n/locales/zhtw.yml index 74e06f7a1..5c7d126a3 100644 --- a/crates/clash-verge-i18n/locales/zhtw.yml +++ b/crates/clash-verge-i18n/locales/zhtw.yml @@ -26,6 +26,11 @@ notifications: appHidden: title: 應用已隱藏 body: Clash Verge 正在背景執行。 + updateReady: + title: Clash Verge Update + body: A new version (v{version}) has been downloaded and is ready to install. + installNow: Install Now + later: Later service: adminInstallPrompt: 安裝 Clash Verge 服務需要管理員權限 adminUninstallPrompt: 卸载 Clash Verge 服務需要管理員權限 diff --git a/src-tauri/src/core/updater.rs b/src-tauri/src/core/updater.rs index 10446d3c5..21a90549c 100644 --- a/src-tauri/src/core/updater.rs +++ b/src-tauri/src/core/updater.rs @@ -158,11 +158,18 @@ impl SilentUpdater { logging!( info, Type::System, - "Update cache version ({}) > current ({}), attempting startup install", + "Update cache version ({}) > current ({}), asking user to install", cached_version, current_version ); + // Ask user for confirmation — they can skip and use the app normally. + // The cache is preserved so next launch will ask again. + if !Self::ask_user_to_install(app_handle, cached_version).await { + logging!(info, Type::System, "User skipped update install, starting normally"); + return false; + } + // Read cached bytes let bytes = match Self::read_cache_bytes() { Ok(b) => b, @@ -177,8 +184,8 @@ impl SilentUpdater { } }; - // Need a fresh Update object from the server to call install() - // Network should be available at startup (user just booted) + // Need a fresh Update object from the server to call install(). + // This is a lightweight HTTP request (< 1s), not a re-download. let update = match app_handle.updater() { Ok(updater) => match updater.check().await { Ok(Some(u)) => u, @@ -210,6 +217,20 @@ impl SilentUpdater { } }; + // Verify the server's version matches the cached version. + // If server now has a newer version, our cached bytes are stale. + if update.version != *cached_version { + logging!( + info, + Type::System, + "Server version ({}) != cached version ({}), cache is stale, cleaning up", + update.version, + cached_version + ); + Self::delete_cache(); + return false; + } + let version = update.version.clone(); logging!(info, Type::System, "Installing cached update v{version} at startup..."); @@ -231,18 +252,15 @@ impl SilentUpdater { true } Ok(Ok(Err(e))) => { - logging!(warn, Type::System, "Startup install failed: {e}, cleaning up"); - Self::delete_cache(); + logging!(warn, Type::System, "Startup install failed: {e}, will retry next launch"); false } Ok(Err(e)) => { - logging!(warn, Type::System, "Startup install task panicked: {e}, cleaning up"); - Self::delete_cache(); + logging!(warn, Type::System, "Startup install task panicked: {e}, will retry next launch"); false } Err(_) => { - logging!(warn, Type::System, "Startup install timed out (30s), cleaning up"); - Self::delete_cache(); + logging!(warn, Type::System, "Startup install timed out (30s), will retry next launch"); false } }; @@ -256,6 +274,36 @@ impl SilentUpdater { } } +// ─── User Confirmation Dialog ──────────────────────────────────────────────── + +impl SilentUpdater { + /// Show a native dialog asking the user to install or skip the update. + /// Returns true if user chose to install, false if they chose to skip. + async fn ask_user_to_install(app_handle: &tauri::AppHandle, version: &str) -> bool { + use tauri_plugin_dialog::{DialogExt as _, MessageDialogButtons, MessageDialogKind}; + + let title = clash_verge_i18n::t!("notifications.updateReady.title").to_string(); + let body = clash_verge_i18n::t!("notifications.updateReady.body") + .replace("{version}", version); + let install_now = clash_verge_i18n::t!("notifications.updateReady.installNow").to_string(); + let later = clash_verge_i18n::t!("notifications.updateReady.later").to_string(); + + let (tx, rx) = tokio::sync::oneshot::channel(); + + app_handle + .dialog() + .message(body) + .title(title) + .buttons(MessageDialogButtons::OkCancelCustom(install_now, later)) + .kind(MessageDialogKind::Info) + .show(move |confirmed| { + let _ = tx.send(confirmed); + }); + + rx.await.unwrap_or(false) + } +} + // ─── Update Splash Window ──────────────────────────────────────────────────── impl SilentUpdater { @@ -513,7 +561,7 @@ mod tests { } #[test] - fn test_cache_meta_missing_field() { + fn test_cache_meta_missing_required_field() { let result = serde_json::from_str::(r#"{"version":"2.5.0"}"#); assert!(result.is_err()); // missing downloaded_at }