feat: add auto-update support via tauri-plugin-updater

This commit is contained in:
t8y2 2026-05-04 18:14:40 +08:00
parent 8d3c14c24a
commit af9c206d75
10 changed files with 255 additions and 6 deletions

View File

@ -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 }}'

View File

@ -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",

View File

@ -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

168
src-tauri/Cargo.lock generated
View File

@ -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",

View File

@ -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"

View File

@ -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) {

View File

@ -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"
]
}
}
}

View File

@ -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() {
</div>
<DialogFooter>
<Button variant="outline" @click="showUpdateDialog = false">{{ t('dangerDialog.cancel') }}</Button>
<Button v-if="updateInfo?.update_available || updateCheckMessage" @click="openLatestRelease">{{ t('updates.openRelease') }}</Button>
<template v-if="updateInfo?.update_available">
<Button variant="outline" @click="openLatestRelease">{{ t('updates.openRelease') }}</Button>
<Button v-if="updateReady" @click="restartApp">{{ t('updates.restart') }}</Button>
<Button v-else-if="isDownloadingUpdate" disabled>
<Loader2 class="h-4 w-4 animate-spin" />
{{ t('updates.downloading', { progress: downloadProgress }) }}
</Button>
<Button v-else @click="downloadAndInstallUpdate">{{ t('updates.downloadAndInstall') }}</Button>
</template>
<Button v-else-if="updateCheckMessage" @click="openLatestRelease">{{ t('updates.openRelease') }}</Button>
</DialogFooter>
</DialogContent>
</Dialog>

View File

@ -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",

View File

@ -22,6 +22,10 @@ export default {
failed: "检查更新失败:{error}",
rateLimited: "GitHub 更新检查暂时触发频率限制。你仍然可以打开下载页手动查看。",
openRelease: "打开下载页",
downloadAndInstall: "下载并安装",
downloading: "下载中 {progress}%",
downloadFailed: "更新下载失败:{error}",
restart: "立即重启",
},
sidebar: {
connections: "连接",