diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1ecabc2819..dcff8fe449 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1194,6 +1194,11 @@ jobs: working-directory: agents steps: - uses: actions/checkout@v7 + - name: Install system dependencies + if: matrix.driver == 'duckdb' + run: | + sudo apt-get update + sudo apt-get install -y libdbus-1-dev pkg-config - name: Setup Rust uses: dtolnay/rust-toolchain@1.97.1 # Pin mold because runner image updates can change bundled DuckDB C++ links. diff --git a/Cargo.lock b/Cargo.lock index 42b27fffb8..3d3ad96b84 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2017,6 +2017,24 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "dbus-secret-service" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "708b509edf7889e53d7efb0ffadd994cc6c2345ccb62f55cfd6b0682165e4fa6" +dependencies = [ + "aes 0.8.4", + "block-padding 0.3.3", + "cbc 0.1.2", + "dbus", + "fastrand", + "hkdf 0.12.4", + "num", + "once_cell", + "sha2 0.10.9", + "zeroize", +] + [[package]] name = "dbx" version = "0.6.20" @@ -2141,11 +2159,12 @@ dependencies = [ "font-kit", "futures", "jsonwebtoken", + "keyring", "log", "minijinja", "mongodb", "mysql_async", - "nix", + "nix 0.31.3", "notify", "percent-encoding", "portpicker", @@ -2202,7 +2221,7 @@ dependencies = [ "mongodb", "mysql_async", "mysql_common", - "nix", + "nix 0.31.3", "openssl", "pageant", "percent-encoding", @@ -2283,7 +2302,7 @@ name = "dbx-platform" version = "0.1.0" dependencies = [ "futures", - "nix", + "nix 0.31.3", "reqwest 0.12.28", "serde", "tokio", @@ -2831,7 +2850,7 @@ dependencies = [ "crypto-bigint", "crypto-common 0.2.2", "digest 0.11.3", - "hkdf", + "hkdf 0.13.0", "hybrid-array", "once_cell", "pem-rfc7468 1.0.0", @@ -4001,6 +4020,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac 0.12.1", +] + [[package]] name = "hkdf" version = "0.13.0" @@ -4814,6 +4842,23 @@ dependencies = [ "indexmap 2.14.0", ] +[[package]] +name = "keyring" +version = "3.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eebcc3aff044e5944a8fbaf69eb277d11986064cba30c468730e8b9909fb551c" +dependencies = [ + "byteorder", + "dbus-secret-service", + "linux-keyutils", + "log", + "secret-service", + "security-framework 2.11.1", + "security-framework 3.7.0", + "windows-sys 0.60.2", + "zeroize", +] + [[package]] name = "kqueue" version = "1.2.0" @@ -4963,6 +5008,16 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linux-keyutils" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83270a18e9f90d0707c41e9f35efada77b64c0e6f3f1810e71c8368a864d5590" +dependencies = [ + "bitflags 2.13.1", + "libc", +] + [[package]] name = "linux-raw-sys" version = "0.12.1" @@ -5512,6 +5567,19 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.13.1", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + [[package]] name = "nix" version = "0.31.3" @@ -5578,6 +5646,20 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.4.8" @@ -5604,6 +5686,15 @@ dependencies = [ "smallvec", ] +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + [[package]] name = "num-conv" version = "0.2.2" @@ -5629,6 +5720,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -7462,7 +7564,7 @@ dependencies = [ "getrandom 0.2.17", "ghash 0.6.0", "hex-literal", - "hkdf", + "hkdf 0.13.0", "hmac 0.12.1", "hmac 0.13.0", "inout 0.1.4", @@ -7515,7 +7617,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37cb4d0360bdd8935392a306d8b5edb539cc455b30e8bf13dd213a0cf7879b40" dependencies = [ "log", - "nix", + "nix 0.31.3", "ssh-encoding", "windows-sys 0.61.2", ] @@ -7945,6 +8047,25 @@ dependencies = [ "zeroize", ] +[[package]] +name = "secret-service" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4d35ad99a181be0a60ffcbe85d680d98f87bdc4d7644ade319b87076b9dbfd4" +dependencies = [ + "aes 0.8.4", + "cbc 0.1.2", + "futures-util", + "generic-array 0.14.7", + "hkdf 0.12.4", + "num", + "once_cell", + "rand 0.8.7", + "serde", + "sha2 0.10.9", + "zbus 4.4.0", +] + [[package]] name = "security-framework" version = "2.11.1" @@ -8633,6 +8754,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "string_cache" version = "0.9.0" @@ -9135,7 +9262,7 @@ dependencies = [ "thiserror 2.0.19", "url", "windows 0.61.3", - "zbus", + "zbus 5.18.0", ] [[package]] @@ -9182,7 +9309,7 @@ dependencies = [ "tokio", "tracing", "windows-sys 0.60.2", - "zbus", + "zbus 5.18.0", ] [[package]] @@ -11453,6 +11580,16 @@ dependencies = [ "rustix", ] +[[package]] +name = "xdg-home" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "yeslogic-fontconfig-sys" version = "6.0.1" @@ -11487,6 +11624,38 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zbus" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725" +dependencies = [ + "async-broadcast", + "async-process", + "async-recursion", + "async-trait", + "enumflags2", + "event-listener", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix 0.29.0", + "ordered-stream", + "rand 0.8.7", + "serde", + "serde_repr", + "sha1 0.10.7", + "static_assertions", + "tracing", + "uds_windows", + "windows-sys 0.52.0", + "xdg-home", + "zbus_macros 4.4.0", + "zbus_names 3.0.0", + "zvariant 4.2.0", +] + [[package]] name = "zbus" version = "5.18.0" @@ -11517,9 +11686,22 @@ dependencies = [ "uuid", "windows-sys 0.61.2", "winnow 1.0.4", - "zbus_macros", - "zbus_names", - "zvariant", + "zbus_macros 5.18.0", + "zbus_names 4.3.4", + "zvariant 5.13.1", +] + +[[package]] +name = "zbus_macros" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" +dependencies = [ + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 2.0.119", + "zvariant_utils 2.1.0", ] [[package]] @@ -11532,9 +11714,20 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.119", - "zbus_names", - "zvariant", - "zvariant_utils", + "zbus_names 4.3.4", + "zvariant 5.13.1", + "zvariant_utils 3.5.0", +] + +[[package]] +name = "zbus_names" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" +dependencies = [ + "serde", + "static_assertions", + "zvariant 4.2.0", ] [[package]] @@ -11545,7 +11738,7 @@ checksum = "d8bf88b4a3ff53e883001e0e0115b297a9d53c31b9c1edd2bfdd853e3428624e" dependencies = [ "serde", "winnow 1.0.4", - "zvariant", + "zvariant 5.13.1", ] [[package]] @@ -11736,6 +11929,19 @@ dependencies = [ "zune-core", ] +[[package]] +name = "zvariant" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "zvariant_derive 4.2.0", +] + [[package]] name = "zvariant" version = "5.13.1" @@ -11746,8 +11952,21 @@ dependencies = [ "enumflags2", "serde", "winnow 1.0.4", - "zvariant_derive", - "zvariant_utils", + "zvariant_derive 5.13.1", + "zvariant_utils 3.5.0", +] + +[[package]] +name = "zvariant_derive" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" +dependencies = [ + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 2.0.119", + "zvariant_utils 2.1.0", ] [[package]] @@ -11760,7 +11979,18 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.119", - "zvariant_utils", + "zvariant_utils 3.5.0", +] + +[[package]] +name = "zvariant_utils" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", ] [[package]] diff --git a/README.md b/README.md index 5a2c4faf02..058b24d65e 100644 --- a/README.md +++ b/README.md @@ -346,7 +346,10 @@ DBX provides a web version that can be deployed via Docker. The examples use the `latest` tag to pull the current release. ```bash -docker run -d --pull=always --name dbx -p 4224:4224 -v dbx-data:/app/data t8y2/dbx:latest +# The default keeps the key in the persistent /app/data volume. +docker run -d --pull=always --name dbx -p 4224:4224 \ + -v dbx-data:/app/data \ + t8y2/dbx:latest ``` This uses the cross-platform `dbx-data` named volume. Users in China can use @@ -375,10 +378,58 @@ services: volumes: dbx-data: + ``` Open `http://localhost:4224` in your browser. Multi-arch images (amd64 / arm64) are available. +Connection, plugin, AI, and tunnel credentials are encrypted before they are +written to `dbx.db`. Desktop builds use the local platform credential store +(macOS Keychain, Windows Credential Manager, or Linux Secret Service). +Web/Docker and directly running `dbx-web` use the same managed data-directory +key by default: `${DBX_DATA_DIR}/.dbx/secret.key`. The key is created only when +migration starts or the first sensitive value is written, and must be backed up +together with `dbx.db`. Persisting `/app/data` is therefore sufficient for a +normal Docker deployment. This key protects the database contents, but cannot +protect the whole data volume if the volume itself is copied or exposed. + +For production deployments, replace the managed key with a Docker/Kubernetes +Secret by setting `DBX_SECRET_KEY_FILE`, or provide `DBX_SECRET_KEY` through a +secret manager. Explicit keys take precedence and must never be rotated while +encrypted data is in use. Without a usable key, business APIs remain blocked +and the browser displays the data security upgrade screen. + +When running the binary directly, set `DBX_DATA_DIR=/var/lib/dbx` to use +`/var/lib/dbx/.dbx/secret.key` with the same lifecycle and backup rules. + +When upgrading from a release that stored credentials in plain text, Desktop +and Web display a **Data Security Upgrade** wizard before opening the main +application. Choose **Start upgrade** to create a restricted backup, migrate +legacy database/JSON credentials, and verify that encrypted values can be +read. Failures retain the original data and backup; fix the issue shown in +the wizard and choose **Retry**. The backup path is shown after success. +Once you have verified your connections, **Delete migration backups** asks +for confirmation and removes the migration backup directory and the legacy +JSON `.bak` files created by that migration. Unrelated backup files are kept. +Users with no legacy data proceed directly after the initial check. + +Local CLI and standalone MCP can reuse the existing platform credential +store on the same device, or read an explicitly configured +`DBX_SECRET_KEY_FILE`/`DBX_SECRET_KEY`. They do not create keys during the +startup check or automatically migrate legacy data. A +`DATA_MIGRATION_REQUIRED` error means that you must first open the same data +directory in Desktop or Web and complete its upgrade wizard. For headless +hosts without a platform credential store, configure the persistent key. + +Cross-device exports use a separate sync passphrase and never contain the +local storage key. A direct `dbx.db` copy is not a cross-platform sync method: +platform keys do not move with the database. Use encrypted export/import so +the target device stores credentials using its own local key. + +For the complete design, migration state machine, implementation map, +troubleshooting, and test plan, see +[DBX Data Security Upgrade and Migration](docs/data-security-migration.md). + To publish DBX under a reverse-proxy context path such as `/dbx`, set the runtime base path and proxy the same prefix to the container: diff --git a/README.zh-CN.md b/README.zh-CN.md index 96c828d539..cac1e50cd9 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -353,7 +353,10 @@ flatpak install flatpark com.dbxio.dbx DBX 提供 Web 版本,可通过 Docker 部署。示例使用 `latest` 标签以拉取当前发布版本。 ```bash -docker run -d --pull=always --name dbx -p 4224:4224 -v dbx-data:/app/data t8y2/dbx:latest +# 默认将密钥保存在持久化的 /app/data 数据卷中。 +docker run -d --pull=always --name dbx -p 4224:4224 \ + -v dbx-data:/app/data \ + t8y2/dbx:latest ``` 这里使用跨平台的 `dbx-data` 命名卷。中国大陆用户可选用 CNB 镜像 @@ -381,8 +384,21 @@ services: volumes: dbx-data: + ``` +连接、插件、AI 和 Tunnel 凭据写入 `dbx.db` 前会加密。桌面端使用本机凭据存储(macOS Keychain、Windows Credential Manager 或 Linux Secret Service)。Web/Docker 与直接运行 `dbx-web` 默认使用同一套数据目录托管密钥:`${DBX_DATA_DIR}/.dbx/secret.key`。只有在开始迁移或第一次写入敏感字段时才创建密钥;普通 Docker 部署只需持久化 `/app/data`,并且必须将 `.dbx/secret.key` 与 `dbx.db` 一起备份。该密钥不能防护整个数据卷被复制或泄露。 + +生产环境可以使用 Docker/Kubernetes Secret 覆盖托管策略:设置 `DBX_SECRET_KEY_FILE`,或由密钥管理系统提供 `DBX_SECRET_KEY`。显式密钥优先,已有密文使用期间不能轮换。密钥不可用时,业务 API 保持阻塞,浏览器显示数据安全升级页面。直接运行二进制时设置 `DBX_DATA_DIR=/var/lib/dbx`,即可使用 `/var/lib/dbx/.dbx/secret.key`。 + +升级包含历史明文凭据的版本时,桌面端和 Web 会在进入主界面前显示 **数据安全升级向导**。点击 **开始迁移** 后,软件会创建权限受限的备份,迁移旧数据库和 JSON 凭据,并验证密文可读取。失败时保留原始数据和备份,根据向导提示修复后点击 **重试**。成功页面会显示备份路径。确认连接可用后,可点击 **删除迁移备份**,二次确认后删除迁移备份目录和本次迁移生成的旧 JSON `.bak` 文件;其他备份不会删除。没有历史数据的新用户检查后直接进入主界面。 + +本机 CLI 和独立 MCP 可以复用同一设备已有的平台凭据存储,也可以读取显式配置的 `DBX_SECRET_KEY_FILE` 或 `DBX_SECRET_KEY`。它们不会在启动检查时创建密钥,也不会自动迁移历史数据。遇到 `DATA_MIGRATION_REQUIRED` 时,请先使用桌面端或 Web 打开同一数据目录,完成升级向导。没有平台凭据存储的无界面主机应配置持久化密钥。 + +跨设备导出使用独立的同步口令,导出包不包含本地存储密钥。直接复制 `dbx.db` 不能作为跨平台同步方式,因为本机平台密钥不会随数据库移动。请使用加密导出/导入,让目标设备使用自己的本地密钥保存凭据。 + +完整的设计、迁移状态、实现模块、排障和测试说明请参阅:[DBX 数据安全升级与迁移](docs/data-security-migration.zh-CN.md)。 + 如需通过 nginx 等反向代理发布到 `/dbx` 这类子路径下,设置运行时上下文路径,并将同一前缀代理到容器: ```yaml diff --git a/agents/drivers/duckdb/Cargo.lock b/agents/drivers/duckdb/Cargo.lock index f0da685a78..4870c15b38 100644 --- a/agents/drivers/duckdb/Cargo.lock +++ b/agents/drivers/duckdb/Cargo.lock @@ -341,6 +341,112 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix 1.1.4", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix 1.1.4", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "async-signal" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix 1.1.4", + "signal-hook-registry", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + [[package]] name = "async-trait" version = "0.1.91" @@ -529,6 +635,19 @@ dependencies = [ "hybrid-array", ] +[[package]] +name = "blocking" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a70e4329df6cb94385eed412ec92375c3cdd8a6e502493d1229b6414e4036dfa" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + [[package]] name = "blowfish" version = "0.9.1" @@ -807,6 +926,15 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "connection-string" version = "0.2.0" @@ -1222,6 +1350,35 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" +[[package]] +name = "dbus" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab69f03cc8c4340c9c8e315114e1658e6775a9b16a04357973aa21cec22b32e" +dependencies = [ + "libc", + "libdbus-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "dbus-secret-service" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "708b509edf7889e53d7efb0ffadd994cc6c2345ccb62f55cfd6b0682165e4fa6" +dependencies = [ + "aes 0.8.4", + "block-padding 0.3.3", + "cbc 0.1.2", + "dbus", + "fastrand", + "hkdf 0.12.4", + "num", + "once_cell", + "sha2 0.10.9", + "zeroize", +] + [[package]] name = "dbx-ai-provider" version = "0.1.0" @@ -1268,11 +1425,12 @@ dependencies = [ "flate2", "futures", "jsonwebtoken", + "keyring", "log", "minijinja", "mongodb", "mysql_async", - "nix", + "nix 0.31.3", "notify", "percent-encoding", "portpicker", @@ -1388,7 +1546,7 @@ name = "dbx-platform" version = "0.1.0" dependencies = [ "futures", - "nix", + "nix 0.31.3", "reqwest", "serde", "tokio", @@ -1755,7 +1913,7 @@ dependencies = [ "crypto-bigint", "crypto-common 0.2.2", "digest 0.11.3", - "hkdf", + "hkdf 0.13.0", "hybrid-array", "once_cell", "pem-rfc7468 1.0.0", @@ -1777,6 +1935,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "endi" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" + [[package]] name = "enum_dispatch" version = "0.3.13" @@ -1796,6 +1960,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" dependencies = [ "enumflags2_derive", + "serde", ] [[package]] @@ -1825,6 +1990,26 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "event-listener" +version = "5.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a23add41df1562121a9393cb065eab5146a1242410f23a644851e90cfd669d2" +dependencies = [ + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + [[package]] name = "fallible-iterator" version = "0.2.0" @@ -2020,6 +2205,19 @@ version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a" +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + [[package]] name = "futures-macro" version = "0.3.33" @@ -2314,6 +2512,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac 0.12.1", +] + [[package]] name = "hkdf" version = "0.13.0" @@ -2852,6 +3059,23 @@ dependencies = [ "indexmap", ] +[[package]] +name = "keyring" +version = "3.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eebcc3aff044e5944a8fbaf69eb277d11986064cba30c468730e8b9909fb551c" +dependencies = [ + "byteorder", + "dbus-secret-service", + "linux-keyutils", + "log", + "secret-service", + "security-framework 2.11.1", + "security-framework 3.7.0", + "windows-sys 0.60.2", + "zeroize", +] + [[package]] name = "kqueue" version = "1.2.0" @@ -2944,6 +3168,15 @@ version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" +[[package]] +name = "libdbus-sys" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "328c4789d42200f1eeec05bd86c9c13c7f091d2ba9a6ea35acdf51f31bc0f043" +dependencies = [ + "pkg-config", +] + [[package]] name = "libduckdb-sys" version = "1.10505.0" @@ -2986,6 +3219,16 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linux-keyutils" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83270a18e9f90d0707c41e9f35efada77b64c0e6f3f1810e71c8368a864d5590" +dependencies = [ + "bitflags 2.13.1", + "libc", +] + [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -3148,6 +3391,15 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38d1115007560874e373613744c6fba374c17688327a71c1476d1a5954cc857b" +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + [[package]] name = "minijinja" version = "2.21.0" @@ -3380,6 +3632,19 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.13.1", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + [[package]] name = "nix" version = "0.31.3" @@ -3437,6 +3702,20 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.4.8" @@ -3497,6 +3776,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -3619,6 +3909,16 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + [[package]] name = "p256" version = "0.14.0-rc.7" @@ -3680,6 +3980,12 @@ dependencies = [ "windows-strings", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.5" @@ -3835,6 +4141,17 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +[[package]] +name = "piper" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + [[package]] name = "pkcs1" version = "0.8.0-rc.4" @@ -3890,6 +4207,20 @@ version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix 1.1.4", + "windows-sys 0.61.2", +] + [[package]] name = "poly1305" version = "0.8.0" @@ -4600,7 +4931,7 @@ dependencies = [ "getrandom 0.2.17", "ghash 0.6.0", "hex-literal", - "hkdf", + "hkdf 0.13.0", "hmac 0.12.1", "hmac 0.13.0", "inout 0.1.4", @@ -4653,7 +4984,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37cb4d0360bdd8935392a306d8b5edb539cc455b30e8bf13dd213a0cf7879b40" dependencies = [ "log", - "nix", + "nix 0.31.3", "ssh-encoding", "windows-sys 0.61.2", ] @@ -4955,6 +5286,25 @@ dependencies = [ "zeroize", ] +[[package]] +name = "secret-service" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4d35ad99a181be0a60ffcbe85d680d98f87bdc4d7644ade319b87076b9dbfd4" +dependencies = [ + "aes 0.8.4", + "cbc 0.1.2", + "futures-util", + "generic-array 0.14.7", + "hkdf 0.12.4", + "num", + "once_cell", + "rand 0.8.7", + "serde", + "sha2 0.10.9", + "zbus", +] + [[package]] name = "security-framework" version = "2.11.1" @@ -5051,6 +5401,17 @@ dependencies = [ "zmij", ] +[[package]] +name = "serde_repr" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -5393,6 +5754,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "stringprep" version = "0.1.5" @@ -6043,6 +6410,17 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" +[[package]] +name = "uds_windows" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" +dependencies = [ + "memoffset", + "tempfile", + "windows-sys 0.61.2", +] + [[package]] name = "unicode-bidi" version = "0.3.18" @@ -6424,7 +6802,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" dependencies = [ "windows-core 0.57.0", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -6457,7 +6835,7 @@ dependencies = [ "windows-implement 0.57.0", "windows-interface 0.57.0", "windows-result 0.1.2", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -6561,7 +6939,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -6588,7 +6966,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -6597,7 +6975,16 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", ] [[package]] @@ -6615,14 +7002,31 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -6640,48 +7044,96 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + [[package]] name = "winnow" version = "1.0.4" @@ -6734,6 +7186,16 @@ dependencies = [ "rustix 1.1.4", ] +[[package]] +name = "xdg-home" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "yoke" version = "0.8.3" @@ -6757,6 +7219,62 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zbus" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725" +dependencies = [ + "async-broadcast", + "async-process", + "async-recursion", + "async-trait", + "enumflags2", + "event-listener", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix 0.29.0", + "ordered-stream", + "rand 0.8.7", + "serde", + "serde_repr", + "sha1 0.10.7", + "static_assertions", + "tracing", + "uds_windows", + "windows-sys 0.52.0", + "xdg-home", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.119", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" +dependencies = [ + "serde", + "static_assertions", + "zvariant", +] + [[package]] name = "zerocopy" version = "0.8.55" @@ -6930,3 +7448,40 @@ dependencies = [ "cc", "pkg-config", ] + +[[package]] +name = "zvariant" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "zvariant_derive", +] + +[[package]] +name = "zvariant_derive" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.119", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] diff --git a/apps/desktop/src/App.vue b/apps/desktop/src/App.vue index fdb2f53a6f..296de3a0fe 100644 --- a/apps/desktop/src/App.vue +++ b/apps/desktop/src/App.vue @@ -95,6 +95,7 @@ import { parseConnectionDeepLink, parseConnectionDeepLinkUpdate, type Connection import { resolveConnectionDeepLinkUpdate } from "@/lib/connection/connectionDeepLinkUpdate"; import { parseAiConfigDeepLink, type AiConfigDeepLinkDraft } from "@/lib/ai/aiConfigDeepLink"; import { activeDesktopAiRuns, blockingDesktopAiRunsForQuit } from "@/lib/ai/desktopAiRunRegistry"; + import { isBrowserReloadShortcut, isCloseOtherTabsShortcut, diff --git a/apps/desktop/src/StartupGate.vue b/apps/desktop/src/StartupGate.vue new file mode 100644 index 0000000000..16812ff950 --- /dev/null +++ b/apps/desktop/src/StartupGate.vue @@ -0,0 +1,124 @@ + + + + diff --git a/apps/desktop/src/components/editor/EditorSettingsDialog.vue b/apps/desktop/src/components/editor/EditorSettingsDialog.vue index 44e71cd4b2..b8a197735b 100644 --- a/apps/desktop/src/components/editor/EditorSettingsDialog.vue +++ b/apps/desktop/src/components/editor/EditorSettingsDialog.vue @@ -4293,7 +4293,7 @@ async function testWebDav() { async function uploadWebDavSnapshot() { await runWebDavAction("upload", async () => { - const summary = await webdavSyncUpload(currentWebDavConfig(), settingsStore.editorSettings, webdavSyncSecrets.value ? webdavSecretsPassphrase.value : undefined); + const summary = await webdavSyncUpload(currentWebDavConfig(), settingsStore.editorSettings, webdavSyncSecrets.value ? webdavSecretsPassphrase.value : undefined, webdavSyncSecrets.value); return t("settings.syncUploadSuccess", { bytes: summary.bytes, path: summary.remotePath, @@ -4304,7 +4304,7 @@ async function uploadWebDavSnapshot() { async function downloadWebDavSnapshot() { if (!window.confirm(t("settings.syncDownloadConfirm"))) return; await runWebDavAction("download", async () => { - const result = await webdavSyncDownload(currentWebDavConfig(), webdavSyncSecrets.value ? webdavSecretsPassphrase.value : undefined); + const result = await webdavSyncDownload(currentWebDavConfig(), webdavSyncSecrets.value ? webdavSecretsPassphrase.value : undefined, webdavSyncSecrets.value); if (result.editorSettings && typeof result.editorSettings === "object") { settingsStore.updateEditorSettings(result.editorSettings as any); } diff --git a/apps/desktop/src/components/migration/SecurityMigrationWizard.spec.ts b/apps/desktop/src/components/migration/SecurityMigrationWizard.spec.ts new file mode 100644 index 0000000000..a90ba487ad --- /dev/null +++ b/apps/desktop/src/components/migration/SecurityMigrationWizard.spec.ts @@ -0,0 +1,70 @@ +// @vitest-environment happy-dom +import { createApp, nextTick } from "vue"; +import { describe, it, expect, vi } from "vitest"; +import { useMigrationStore } from "@/stores/migrationStore"; +import Wizard from "./SecurityMigrationWizard.vue"; +const mocks = vi.hoisted(() => ({ save: vi.fn().mockRejectedValue(new Error("unavailable")) })); +vi.mock("@/lib/backend/api", () => ({})); +vi.mock("@/i18n", () => ({ setLocale: vi.fn() })); +vi.mock("vue-i18n", () => ({ useI18n: () => ({ t: (key: string) => key, locale: "en" }) })); +vi.mock("@/lib/export/saveTextFile", () => ({ saveTextFile: mocks.save })); +describe("migration failure actions", () => { + it("shows a redacted diagnostic even when file saving fails and routes status retries correctly", async () => { + const backend = { migrationStatus: vi.fn().mockRejectedValue(new Error("private transport detail")), migrationStart: vi.fn(), migrationRetry: vi.fn(), migrationCleanupBackups: vi.fn() }; + const store = useMigrationStore(backend); + await store.initialize(); + store.state.step = 2; + const root = document.createElement("div"); + const app = createApp(Wizard, { store }); + app.mount(root); + try { + const button = (label: string) => Array.from(root.querySelectorAll("button")).find((b) => b.textContent === label)!; + expect(root.querySelector("textarea")?.value).toContain("statusFailed"); + button("migration.exportDiagnostic").click(); + await nextTick(); + await nextTick(); + expect(mocks.save).toHaveBeenCalledWith(expect.any(String), "dbx-migration-diagnostic.json", "JSON", "json"); + expect(root.querySelector("textarea")?.value).toContain("statusFailed"); + expect(root.querySelector("textarea")?.value).not.toContain("private transport detail"); + button("migration.retryStatus").click(); + await nextTick(); + expect(backend.migrationStatus).toHaveBeenCalledTimes(2); + expect(backend.migrationRetry).not.toHaveBeenCalled(); + } finally { + app.unmount(); + } + }); + + it("updates the visible diagnostic after a failed action refreshes migration status", async () => { + const pendingStatus = { + migrationId: "secret-store-v1", + state: "pending" as const, + needsMigration: true, + keyProviderAvailable: true, + databasePlaintextCount: 1, + connectionCount: 1, + pluginSecretCount: 0, + aiSecretCount: 0, + tunnelSecretCount: 0, + syncCredentialCount: 0, + legacyJsonFiles: [], + backupRequired: false, + }; + const failedStatus = { ...pendingStatus, state: "failed" as const, errorCode: "LEGACY_JSON_INVALID", errorMessage: "Repair connections.json" }; + const status = vi.fn().mockResolvedValueOnce(pendingStatus).mockResolvedValueOnce(failedStatus); + const backend = { migrationStatus: status, migrationStart: vi.fn().mockRejectedValue(new Error("request failed")), migrationRetry: vi.fn(), migrationCleanupBackups: vi.fn() }; + const store = useMigrationStore(backend); + await store.initialize(); + const root = document.createElement("div"); + const app = createApp(Wizard, { store }); + app.mount(root); + try { + const start = Array.from(root.querySelectorAll("button")).find((button) => button.textContent === "migration.start")!; + start.click(); + await vi.waitFor(() => expect(root.querySelector("textarea")?.value).toContain("LEGACY_JSON_INVALID")); + expect(root.querySelector("textarea")?.value).toContain('"state": "failed"'); + } finally { + app.unmount(); + } + }); +}); diff --git a/apps/desktop/src/components/migration/SecurityMigrationWizard.vue b/apps/desktop/src/components/migration/SecurityMigrationWizard.vue new file mode 100644 index 0000000000..46f2a1eca0 --- /dev/null +++ b/apps/desktop/src/components/migration/SecurityMigrationWizard.vue @@ -0,0 +1,228 @@ + +