From 6e0f4e1dc3862edf78f5e128fa8463f2c6881123 Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Tue, 26 May 2026 01:23:26 +0800 Subject: [PATCH] feat: enable driver management on web version - Extract JDBC driver/plugin logic from Tauri commands to dbx-core::jdbc - Add web API routes for JDBC drivers, JDBC plugin, Agent JAR import, system fonts - Upgrade zip crate from v2 to v4 in dbx-core - Replace http.ts stubs with real HTTP calls (support File and string paths) - Remove isWeb restrictions in DriverStoreDialog, add browser file picker support - Fix Mac traffic light inset incorrectly applied on web version - Show driver management toolbar button on web version --- Cargo.lock | 20 +- .../components/config/DriverStoreDialog.vue | 84 ++++- .../src/components/layout/AppToolbar.vue | 3 +- .../src/composables/useWindowControls.ts | 4 +- apps/desktop/src/lib/http.ts | 74 ++-- crates/dbx-core/Cargo.toml | 3 +- crates/dbx-core/src/jdbc.rs | 341 ++++++++++++++++++ crates/dbx-core/src/lib.rs | 1 + crates/dbx-web/src/error.rs | 10 + crates/dbx-web/src/main.rs | 10 + crates/dbx-web/src/routes/agents.rs | 34 ++ crates/dbx-web/src/routes/jdbc.rs | 100 +++++ crates/dbx-web/src/routes/mod.rs | 1 + src-tauri/src/commands/plugins.rs | 290 +-------------- src-tauri/src/commands/system_fonts.rs | 34 +- 15 files changed, 635 insertions(+), 374 deletions(-) create mode 100644 crates/dbx-core/src/jdbc.rs create mode 100644 crates/dbx-web/src/routes/jdbc.rs diff --git a/Cargo.lock b/Cargo.lock index e30cd95543..9a6228fe80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1842,6 +1842,7 @@ dependencies = [ "csv", "deadpool-postgres", "duckdb", + "font-kit", "futures", "iana-time-zone", "log", @@ -1866,7 +1867,7 @@ dependencies = [ "tokio-util", "uuid", "webpki-roots 0.26.11", - "zip 2.4.2", + "zip 4.6.1", ] [[package]] @@ -10137,23 +10138,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "zip" -version = "2.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50" -dependencies = [ - "arbitrary", - "crc32fast", - "crossbeam-utils", - "displaydoc", - "flate2", - "indexmap 2.14.0", - "memchr", - "thiserror 2.0.18", - "zopfli", -] - [[package]] name = "zip" version = "4.6.1" diff --git a/apps/desktop/src/components/config/DriverStoreDialog.vue b/apps/desktop/src/components/config/DriverStoreDialog.vue index f466813a07..9977334eda 100644 --- a/apps/desktop/src/components/config/DriverStoreDialog.vue +++ b/apps/desktop/src/components/config/DriverStoreDialog.vue @@ -273,6 +273,34 @@ function chooseWebOfflineZip(): Promise { }); } +function chooseWebFiles(accept: string, multiple: boolean): Promise { + return new Promise((resolve) => { + const input = document.createElement("input"); + input.type = "file"; + input.accept = accept; + input.multiple = multiple; + input.onchange = () => { + const files = input.files; + if (!files || files.length === 0) { + resolve(null); + return; + } + resolve(Array.from(files)); + }; + input.click(); + }); +} + +function chooseWebFile(accept: string): Promise { + return new Promise((resolve) => { + const input = document.createElement("input"); + input.type = "file"; + input.accept = accept; + input.onchange = () => resolve(input.files?.[0] ?? null); + input.click(); + }); +} + async function importOfflineZip() { if (importingZip.value) return; let selected: string | File | null = null; @@ -303,7 +331,19 @@ async function importOfflineZip() { } async function importDriverJar(dbType: string) { - if (isWeb) return; + const label = drivers.value.find((d) => d.db_type === dbType)?.label ?? dbType; + if (isWeb) { + const file = await chooseWebFile(".jar"); + if (!file) return; + try { + await api.importAgentJar(dbType, file); + await refreshAgents(); + toast(`${label} 驱动导入成功`); + } catch (e: any) { + toast(`${label} 驱动导入失败: ${e}`); + } + return; + } const { open } = await import("@tauri-apps/plugin-dialog"); const selected = await open({ title: "选择驱动 JAR 文件", @@ -311,7 +351,6 @@ async function importDriverJar(dbType: string) { filters: [{ name: "JAR", extensions: ["jar"] }], }); if (typeof selected !== "string") return; - const label = drivers.value.find((d) => d.db_type === dbType)?.label ?? dbType; try { await api.importAgentJar(dbType, selected); await refreshAgents(); @@ -373,7 +412,6 @@ function jreUsageLabel(key: string) { } async function loadJdbcDrivers() { - if (isWeb) return; isLoadingJdbcDrivers.value = true; try { jdbcDrivers.value = await api.listJdbcDrivers(); @@ -394,7 +432,6 @@ async function loadDriverStoreUsage() { } async function loadJdbcPluginStatus() { - if (isWeb) return; try { jdbcPluginStatus.value = await api.jdbcPluginStatus(); emitDriverUpdateCount(); @@ -404,7 +441,7 @@ async function loadJdbcPluginStatus() { } async function installJdbcPlugin() { - if (isWeb || isInstallingJdbcPlugin.value) return; + if (isInstallingJdbcPlugin.value) return; isInstallingJdbcPlugin.value = true; try { jdbcPluginStatus.value = await api.installJdbcPlugin(); @@ -419,14 +456,20 @@ async function installJdbcPlugin() { } async function installJdbcPluginLocal() { - if (isWeb || isInstallingJdbcPlugin.value) return; - const { open } = await import("@tauri-apps/plugin-dialog"); - const selected = await open({ - title: "选择 JDBC 插件 zip 文件", - multiple: false, - filters: [{ name: "ZIP", extensions: ["zip"] }], - }); - if (typeof selected !== "string") return; + if (isInstallingJdbcPlugin.value) return; + let selected: string | File | null = null; + if (isWeb) { + selected = await chooseWebFile(".zip"); + } else { + const { open } = await import("@tauri-apps/plugin-dialog"); + const result = await open({ + title: "选择 JDBC 插件 zip 文件", + multiple: false, + filters: [{ name: "ZIP", extensions: ["zip"] }], + }); + selected = typeof result === "string" ? result : null; + } + if (!selected) return; isInstallingJdbcPlugin.value = true; try { jdbcPluginStatus.value = await api.installJdbcPluginLocal(selected); @@ -441,7 +484,7 @@ async function installJdbcPluginLocal() { } async function uninstallJdbcPlugin() { - if (isWeb || isUninstallingJdbcPlugin.value) return; + if (isUninstallingJdbcPlugin.value) return; isUninstallingJdbcPlugin.value = true; try { jdbcPluginStatus.value = await api.uninstallJdbcPlugin(); @@ -468,7 +511,18 @@ async function importJdbcDriverPaths(paths: string[]) { } async function importJdbcDrivers() { - if (isWeb) return; + if (isWeb) { + const files = await chooseWebFiles(".jar", true); + if (!files || !files.length) return; + try { + jdbcDrivers.value = await api.importJdbcDrivers(files); + void loadDriverStoreUsage(); + toast(t("settings.jdbcImportSuccess", { count: files.length })); + } catch (e: any) { + toast(String(e?.message || e), 5000); + } + return; + } const { open } = await import("@tauri-apps/plugin-dialog"); const selected = await open({ title: t("settings.jdbcImport"), diff --git a/apps/desktop/src/components/layout/AppToolbar.vue b/apps/desktop/src/components/layout/AppToolbar.vue index 77b066c3f6..8fe3f926bf 100644 --- a/apps/desktop/src/components/layout/AppToolbar.vue +++ b/apps/desktop/src/components/layout/AppToolbar.vue @@ -82,7 +82,7 @@ function onToolbarDblClick(e: MouseEvent) {