From af9c206d7544f718685b4a60fe7ad8941d5bce50 Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Mon, 4 May 2026 18:14:40 +0800 Subject: [PATCH] feat: add auto-update support via tauri-plugin-updater --- .github/workflows/release.yml | 2 + package.json | 2 + pnpm-lock.yaml | 20 ++++ src-tauri/Cargo.lock | 168 +++++++++++++++++++++++++++++++++- src-tauri/Cargo.toml | 2 + src-tauri/src/lib.rs | 2 + src-tauri/tauri.conf.json | 9 ++ src/App.vue | 48 +++++++++- src/i18n/locales/en.ts | 4 + src/i18n/locales/zh-CN.ts | 4 + 10 files changed, 255 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c6f2c77d4c..a225a95431 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -81,6 +81,8 @@ jobs: APPLE_ID: ${{ startsWith(matrix.platform, 'macos') && secrets.APPLE_ID || '' }} APPLE_PASSWORD: ${{ startsWith(matrix.platform, 'macos') && secrets.APPLE_PASSWORD || '' }} APPLE_TEAM_ID: ${{ startsWith(matrix.platform, 'macos') && secrets.APPLE_TEAM_ID || '' }} + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} with: tagName: ${{ github.ref_name }} releaseName: 'DBX ${{ github.ref_name }}' diff --git a/package.json b/package.json index e9aac120e5..5b2d1968b0 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,9 @@ "@tauri-apps/api": "^2.11.0", "@tauri-apps/plugin-dialog": "^2.7.1", "@tauri-apps/plugin-fs": "^2.5.1", + "@tauri-apps/plugin-process": "^2.3.1", "@tauri-apps/plugin-shell": "^2.3.5", + "@tauri-apps/plugin-updater": "^2.10.1", "@uiw/codemirror-theme-duotone": "^4.25.9", "@uiw/codemirror-theme-material": "^4.25.9", "@uiw/codemirror-theme-nord": "^4.25.9", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 831d2e4a55..1aea8ee8cc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,9 +35,15 @@ importers: '@tauri-apps/plugin-fs': specifier: ^2.5.1 version: 2.5.1 + '@tauri-apps/plugin-process': + specifier: ^2.3.1 + version: 2.3.1 '@tauri-apps/plugin-shell': specifier: ^2.3.5 version: 2.3.5 + '@tauri-apps/plugin-updater': + specifier: ^2.10.1 + version: 2.10.1 '@uiw/codemirror-theme-duotone': specifier: ^4.25.9 version: 4.25.9(@codemirror/language@6.12.3)(@codemirror/state@6.6.0)(@codemirror/view@6.41.1) @@ -925,9 +931,15 @@ packages: '@tauri-apps/plugin-fs@2.5.1': resolution: {integrity: sha512-9Lz+Jopp6QyeEWhlpkMx4R/+P9HgR+AVAI4vOZhlT8Xaymtz8iVI/Ov984/XTqgJz/5gz5NretqPB/XEMS3NhQ==} + '@tauri-apps/plugin-process@2.3.1': + resolution: {integrity: sha512-nCa4fGVaDL/B9ai03VyPOjfAHRHSBz5v6F/ObsB73r/dA3MHHhZtldaDMIc0V/pnUw9ehzr2iEG+XkSEyC0JJA==} + '@tauri-apps/plugin-shell@2.3.5': resolution: {integrity: sha512-jewtULhiQ7lI7+owCKAjc8tYLJr92U16bPOeAa472LHJdgaibLP83NcfAF2e+wkEcA53FxKQAZ7byDzs2eeizg==} + '@tauri-apps/plugin-updater@2.10.1': + resolution: {integrity: sha512-NFYMg+tWOZPJdzE/PpFj2qfqwAWwNS3kXrb1tm1gnBJ9mYzZ4WDRrwy8udzWoAnfGCHLuePNLY1WVCNHnh3eRA==} + '@ts-morph/common@0.28.1': resolution: {integrity: sha512-W74iWf7ILp1ZKNYXY5qbddNaml7e9Sedv5lvU1V8lftlitkc9Pq1A+jlH23ltDgWYeZFFEqGCD1Ies9hqu3O+g==} @@ -3455,10 +3467,18 @@ snapshots: dependencies: '@tauri-apps/api': 2.11.0 + '@tauri-apps/plugin-process@2.3.1': + dependencies: + '@tauri-apps/api': 2.11.0 + '@tauri-apps/plugin-shell@2.3.5': dependencies: '@tauri-apps/api': 2.11.0 + '@tauri-apps/plugin-updater@2.10.1': + dependencies: + '@tauri-apps/api': 2.11.0 + '@ts-morph/common@0.28.1': dependencies: minimatch: 10.2.5 diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 765ba65822..526d6063e1 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -1614,7 +1614,9 @@ dependencies = [ "tauri-plugin-dialog", "tauri-plugin-fs", "tauri-plugin-log", + "tauri-plugin-process", "tauri-plugin-shell", + "tauri-plugin-updater", "tiberius", "tokio", "tokio-util", @@ -3430,6 +3432,36 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys 0.4.1", + "log", + "simd_cesu8", + "thiserror 2.0.18", + "walkdir", + "windows-link 0.2.1", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn 2.0.117", +] + [[package]] name = "jni-sys" version = "0.3.1" @@ -3871,6 +3903,12 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minisign-verify" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22f9645cb765ea72b8111f36c522475d2daa0d22c957a9826437e97534bc4e9e" + [[package]] name = "miniz_oxide" version = "0.8.9" @@ -4369,6 +4407,18 @@ dependencies = [ "objc2-core-foundation", ] +[[package]] +name = "objc2-osa-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f112d1746737b0da274ef79a23aac283376f335f4095a083a267a082f21db0c0" +dependencies = [ + "bitflags 2.11.1", + "objc2", + "objc2-app-kit", + "objc2-foundation", +] + [[package]] name = "objc2-quartz-core" version = "0.3.2" @@ -4557,6 +4607,20 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "osakit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "732c71caeaa72c065bb69d7ea08717bd3f4863a4f451402fc9513e29dbd5261b" +dependencies = [ + "objc2", + "objc2-foundation", + "objc2-osa-kit", + "serde", + "serde_json", + "thiserror 2.0.18", +] + [[package]] name = "p256" version = "0.14.0-rc.7" @@ -5584,15 +5648,20 @@ dependencies = [ "http-body", "http-body-util", "hyper", + "hyper-rustls", "hyper-util", "js-sys", "log", "percent-encoding", "pin-project-lite", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", "serde", "serde_json", "sync_wrapper", "tokio", + "tokio-rustls", "tokio-util", "tower", "tower-http", @@ -5968,6 +6037,33 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-platform-verifier" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni 0.22.4", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework 3.7.0", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + [[package]] name = "rustls-webpki" version = "0.103.13" @@ -6537,6 +6633,16 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +[[package]] +name = "simd_cesu8" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +dependencies = [ + "rustc_version", + "simdutf8", +] + [[package]] name = "simdutf8" version = "0.1.5" @@ -7077,7 +7183,7 @@ dependencies = [ "gdkwayland-sys", "gdkx11-sys", "gtk", - "jni", + "jni 0.21.1", "libc", "log", "ndk", @@ -7150,7 +7256,7 @@ dependencies = [ "gtk", "heck 0.5.0", "http", - "jni", + "jni 0.21.1", "libc", "log", "mime", @@ -7326,6 +7432,16 @@ dependencies = [ "time", ] +[[package]] +name = "tauri-plugin-process" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d55511a7bf6cd70c8767b02c97bf8134fa434daf3926cfc1be0a0f94132d165a" +dependencies = [ + "tauri", + "tauri-plugin", +] + [[package]] name = "tauri-plugin-shell" version = "2.3.5" @@ -7347,6 +7463,39 @@ dependencies = [ "tokio", ] +[[package]] +name = "tauri-plugin-updater" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806d9dac662c2e4594ff03c647a552f2c9bd544e7d0f683ec58f872f952ce4af" +dependencies = [ + "base64 0.22.1", + "dirs", + "flate2", + "futures-util", + "http", + "infer", + "log", + "minisign-verify", + "osakit", + "percent-encoding", + "reqwest 0.13.3", + "rustls", + "semver", + "serde", + "serde_json", + "tar", + "tauri", + "tauri-plugin", + "tempfile", + "thiserror 2.0.18", + "time", + "tokio", + "url", + "windows-sys 0.60.2", + "zip 4.6.1", +] + [[package]] name = "tauri-runtime" version = "2.11.0" @@ -7357,7 +7506,7 @@ dependencies = [ "dpi", "gtk", "http", - "jni", + "jni 0.21.1", "objc2", "objc2-ui-kit", "objc2-web-kit", @@ -7380,7 +7529,7 @@ checksum = "2cadb13dad0c681e1e0a2c49ae488f0e2906ded3d57e7a0017f4aaf46e387117" dependencies = [ "gtk", "http", - "jni", + "jni 0.21.1", "log", "objc2", "objc2-app-kit", @@ -8409,6 +8558,15 @@ dependencies = [ "system-deps", ] +[[package]] +name = "webpki-root-certs" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "webpki-roots" version = "0.26.11" @@ -9206,7 +9364,7 @@ dependencies = [ "gtk", "http", "javascriptcore-rs", - "jni", + "jni 0.21.1", "libc", "ndk", "objc2", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index e89dab0982..480425d27d 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -37,6 +37,8 @@ chrono = { version = "0.4", features = ["serde"] } tauri-plugin-dialog = "2.7.0" tauri-plugin-fs = "2.5.0" tauri-plugin-shell = "2" +tauri-plugin-updater = "2" +tauri-plugin-process = "2" redis = { version = "0.32.2", features = ["tokio-comp", "tls-rustls", "tokio-rustls-comp"] } rustls = { version = "0.23", features = ["aws-lc-rs"] } portpicker = "0.1.1" diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 58a0fafb99..4208ff6548 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -18,6 +18,8 @@ pub fn run() { .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_fs::init()) .plugin(tauri_plugin_shell::init()) + .plugin(tauri_plugin_updater::Builder::new().build()) + .plugin(tauri_plugin_process::init()) .manage(state) .setup(|app| { if cfg!(debug_assertions) { diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 57730bccd9..9b114b4648 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -28,6 +28,7 @@ "bundle": { "active": true, "targets": "all", + "createUpdaterArtifacts": true, "icon": [ "icons/32x32.png", "icons/128x128.png", @@ -35,5 +36,13 @@ "icons/icon.icns", "icons/icon.ico" ] + }, + "plugins": { + "updater": { + "pubkey": "RWSCMKdQ+0yVxDVHblYBpu9cqAj8cSIaMVVhmeXzZNtBxRDuDvJEiuQ2", + "endpoints": [ + "https://github.com/t8y2/dbx/releases/latest/download/latest.json" + ] + } } } diff --git a/src/App.vue b/src/App.vue index c53ae7642b..675937125c 100644 --- a/src/App.vue +++ b/src/App.vue @@ -918,6 +918,43 @@ function openLatestRelease() { open(url); } +const isDownloadingUpdate = ref(false); +const downloadProgress = ref(0); +const updateReady = ref(false); + +async function downloadAndInstallUpdate() { + if (isDownloadingUpdate.value) return; + isDownloadingUpdate.value = true; + downloadProgress.value = 0; + try { + const { check } = await import("@tauri-apps/plugin-updater"); + const update = await check(); + if (!update) return; + let totalBytes = 0; + let downloadedBytes = 0; + await update.downloadAndInstall((event) => { + if (event.event === "Started" && event.data.contentLength) { + totalBytes = event.data.contentLength; + } else if (event.event === "Progress") { + downloadedBytes += event.data.chunkLength; + downloadProgress.value = totalBytes > 0 ? Math.round((downloadedBytes / totalBytes) * 100) : 0; + } else if (event.event === "Finished") { + downloadProgress.value = 100; + } + }); + updateReady.value = true; + } catch (e: any) { + toast(t("updates.downloadFailed", { error: e?.message || String(e) }), 5000); + } finally { + isDownloadingUpdate.value = false; + } +} + +async function restartApp() { + const { relaunch } = await import("@tauri-apps/plugin-process"); + await relaunch(); +} + function isQueryEditorTarget(target: EventTarget | null): boolean { return target instanceof Element && !!target.closest("[data-query-editor-root]"); } @@ -1806,7 +1843,16 @@ async function setupFileDrop() { - + + diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts index 1ae5d2f543..f2731311a0 100644 --- a/src/i18n/locales/en.ts +++ b/src/i18n/locales/en.ts @@ -22,6 +22,10 @@ export default { failed: "Failed to check updates: {error}", rateLimited: "GitHub update checks are temporarily rate limited. You can still open the release page to check manually.", openRelease: "Open Release", + downloadAndInstall: "Download & Install", + downloading: "Downloading {progress}%", + downloadFailed: "Update download failed: {error}", + restart: "Restart Now", }, sidebar: { connections: "CONNECTIONS", diff --git a/src/i18n/locales/zh-CN.ts b/src/i18n/locales/zh-CN.ts index 46e65b5736..2bcfce9ddc 100644 --- a/src/i18n/locales/zh-CN.ts +++ b/src/i18n/locales/zh-CN.ts @@ -22,6 +22,10 @@ export default { failed: "检查更新失败:{error}", rateLimited: "GitHub 更新检查暂时触发频率限制。你仍然可以打开下载页手动查看。", openRelease: "打开下载页", + downloadAndInstall: "下载并安装", + downloading: "下载中 {progress}%", + downloadFailed: "更新下载失败:{error}", + restart: "立即重启", }, sidebar: { connections: "连接",