mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-30 16:21:39 +00:00
feat(agent): pluggable database drivers via JDBC agent subprocess
Migrate domestic database support (DaMeng, KingBase, Vastbase, GoldenDB) from built-in ODBC/protocol drivers to standalone Java agent processes communicating via stdin/stdout JSON-RPC 2.0. Agents are distributed as fat JARs with embedded JDBC drivers, downloaded on-demand from GitHub Releases with proxy fallback. - Add AgentDriverClient for subprocess lifecycle + JSON-RPC communication - Add AgentManager for JRE/JAR install state management - Add Tauri commands for driver install/uninstall/list - Add DriverManager UI panel in settings dialog - Extend DatabaseType with Kingbase, Vastbase, Goldendb variants - Migrate Dameng from ODBC (dm_driver.rs) to JDBC agent - Delete dm_driver.rs (343 lines) - Add GitHub proxy fallback for downloads (update.hwdns.net, gh-proxy.org) Agent code lives at https://github.com/t8y2/dbx-agents
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from "vue";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
interface AgentDriverInfo {
|
||||
db_type: string;
|
||||
label: string;
|
||||
version: string;
|
||||
size: number;
|
||||
installed: boolean;
|
||||
installed_version: string | null;
|
||||
update_available: boolean;
|
||||
}
|
||||
|
||||
const drivers = ref<AgentDriverInfo[]>([]);
|
||||
const jreInstalled = ref(false);
|
||||
const installing = ref<string | null>(null);
|
||||
|
||||
onMounted(async () => {
|
||||
await refresh();
|
||||
});
|
||||
|
||||
async function refresh() {
|
||||
jreInstalled.value = await invoke<boolean>("check_jre_installed");
|
||||
drivers.value = await invoke<AgentDriverInfo[]>("list_installed_agents");
|
||||
}
|
||||
|
||||
async function installDriver(dbType: string) {
|
||||
installing.value = dbType;
|
||||
try {
|
||||
await invoke("install_agent", { dbType });
|
||||
await refresh();
|
||||
} catch (e: any) {
|
||||
alert(e);
|
||||
} finally {
|
||||
installing.value = null;
|
||||
}
|
||||
}
|
||||
|
||||
async function uninstallDriver(dbType: string) {
|
||||
try {
|
||||
await invoke("uninstall_agent", { dbType });
|
||||
await refresh();
|
||||
} catch (e: any) {
|
||||
alert(e);
|
||||
}
|
||||
}
|
||||
|
||||
function formatSize(bytes: number): string {
|
||||
return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-4 p-4">
|
||||
<h3 class="text-lg font-medium">驱动管理</h3>
|
||||
|
||||
<div class="text-sm text-muted-foreground">
|
||||
JRE: {{ jreInstalled ? "✓ 已安装" : "未安装" }}
|
||||
<span v-if="!jreInstalled" class="ml-2 text-xs">(首次安装驱动时自动下载)</span>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<div
|
||||
v-for="driver in drivers"
|
||||
:key="driver.db_type"
|
||||
class="flex items-center justify-between rounded-md border p-3"
|
||||
>
|
||||
<div>
|
||||
<span class="font-medium">{{ driver.label }}</span>
|
||||
<span v-if="driver.installed" class="ml-2 text-xs text-muted-foreground">
|
||||
v{{ driver.installed_version }}
|
||||
</span>
|
||||
<span class="ml-2 text-xs text-muted-foreground">{{ formatSize(driver.size) }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<Button
|
||||
v-if="!driver.installed"
|
||||
size="sm"
|
||||
:disabled="installing !== null"
|
||||
@click="installDriver(driver.db_type)"
|
||||
>
|
||||
{{ installing === driver.db_type ? "安装中..." : "安装" }}
|
||||
</Button>
|
||||
<div v-else class="flex items-center gap-2">
|
||||
<span class="text-sm text-green-600">✓ 已安装</span>
|
||||
<Button size="sm" variant="ghost" @click="uninstallDriver(driver.db_type)">卸载</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -146,7 +146,7 @@ const driverProfiles: Record<
|
||||
mariadb: { type: "mysql", port: 3306, user: "root", label: "MariaDB", icon: "mariadb" },
|
||||
tidb: { type: "mysql", port: 4000, user: "root", label: "TiDB", icon: "tidb" },
|
||||
oceanbase: { type: "mysql", port: 2881, user: "root", label: "OceanBase", icon: "oceanbase" },
|
||||
goldendb: { type: "mysql", port: 3306, user: "root", label: "GoldenDB", icon: "goldendb" },
|
||||
goldendb: { type: "goldendb", port: 3306, user: "root", label: "GoldenDB", icon: "goldendb" },
|
||||
opengauss: {
|
||||
type: "gaussdb",
|
||||
port: 5432,
|
||||
@@ -155,8 +155,8 @@ const driverProfiles: Record<
|
||||
icon: "opengauss",
|
||||
},
|
||||
gaussdb: { type: "gaussdb", port: 5432, user: "gaussdb", label: "GaussDB", icon: "gaussdb" },
|
||||
kingbase: { type: "postgres", port: 54321, user: "system", label: "KingBase", icon: "kingbase" },
|
||||
vastbase: { type: "postgres", port: 5432, user: "vastbase", label: "Vastbase", icon: "vastbase" },
|
||||
kingbase: { type: "kingbase", port: 54321, user: "system", label: "KingBase", icon: "kingbase" },
|
||||
vastbase: { type: "vastbase", port: 5432, user: "vastbase", label: "Vastbase", icon: "vastbase" },
|
||||
doris: { type: "mysql", port: 9030, user: "root", label: "Doris", icon: "doris", urlParams: "" },
|
||||
selectdb: {
|
||||
type: "mysql",
|
||||
@@ -322,7 +322,8 @@ function defaultDatabaseForProfile() {
|
||||
if (form.value.db_type === "redshift") return "dev";
|
||||
if (form.value.db_type === "gaussdb") return "postgres";
|
||||
if (selectedType.value === "cockroachdb") return "defaultdb";
|
||||
if (form.value.db_type === "postgres") return "postgres";
|
||||
if (form.value.db_type === "postgres" || form.value.db_type === "kingbase" || form.value.db_type === "vastbase")
|
||||
return "postgres";
|
||||
if (form.value.db_type === "sqlserver") return "master";
|
||||
if (form.value.db_type === "oracle") return "ORCL";
|
||||
return "";
|
||||
@@ -1116,7 +1117,14 @@ function openExternalUrl(url: string) {
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="form.db_type === 'mysql' || form.db_type === 'postgres' || form.db_type === 'redshift'"
|
||||
v-if="
|
||||
form.db_type === 'mysql' ||
|
||||
form.db_type === 'postgres' ||
|
||||
form.db_type === 'redshift' ||
|
||||
form.db_type === 'kingbase' ||
|
||||
form.db_type === 'vastbase' ||
|
||||
form.db_type === 'goldendb'
|
||||
"
|
||||
class="grid grid-cols-4 items-center gap-4"
|
||||
>
|
||||
<Label class="text-right">{{ t("connection.urlParams") }}</Label>
|
||||
|
||||
@@ -25,6 +25,7 @@ import { isTauriRuntime } from "@/lib/tauriRuntime";
|
||||
import type { JdbcDriverInfo, JdbcPluginStatus } from "@/types/database";
|
||||
import * as api from "@/lib/api";
|
||||
import { aiTestConnection } from "@/lib/api";
|
||||
import DriverManager from "@/components/config/DriverManager.vue";
|
||||
import { useToast } from "@/composables/useToast";
|
||||
|
||||
const { t } = useI18n();
|
||||
@@ -489,6 +490,7 @@ watch(
|
||||
<TabsTrigger value="appearance" class="flex-1">{{ t("settings.appearanceTab") }}</TabsTrigger>
|
||||
<TabsTrigger value="ai" class="flex-1">{{ t("settings.aiTab") }}</TabsTrigger>
|
||||
<TabsTrigger v-if="!isWeb" value="jdbc" class="flex-1">{{ t("settings.jdbcTab") }}</TabsTrigger>
|
||||
<TabsTrigger v-if="!isWeb" value="drivers" class="flex-1">驱动管理</TabsTrigger>
|
||||
<TabsTrigger v-if="isWeb" value="security" class="flex-1">{{ t("settings.securityTab") }}</TabsTrigger>
|
||||
<TabsTrigger value="about" class="flex-1">{{ t("settings.aboutTab") }}</TabsTrigger>
|
||||
</TabsList>
|
||||
@@ -928,6 +930,10 @@ watch(
|
||||
</DialogFooter>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent v-if="!isWeb" value="drivers" class="py-2">
|
||||
<DriverManager />
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="about" class="space-y-5 py-2">
|
||||
<div class="rounded-lg border bg-muted/20 p-4">
|
||||
<div class="flex items-start justify-between gap-4">
|
||||
|
||||
@@ -7,6 +7,8 @@ export const SCHEMA_AWARE_TYPES = new Set<DatabaseType>([
|
||||
"redshift",
|
||||
"dameng",
|
||||
"gaussdb",
|
||||
"kingbase",
|
||||
"vastbase",
|
||||
"jdbc",
|
||||
]);
|
||||
|
||||
@@ -21,6 +23,9 @@ export const DIAGRAM_SUPPORTED_TYPES = new Set<DatabaseType>([
|
||||
"redshift",
|
||||
"dameng",
|
||||
"gaussdb",
|
||||
"kingbase",
|
||||
"vastbase",
|
||||
"goldendb",
|
||||
]);
|
||||
|
||||
export const DATABASE_SEARCH_SUPPORTED_TYPES = new Set<DatabaseType>([
|
||||
@@ -34,6 +39,9 @@ export const DATABASE_SEARCH_SUPPORTED_TYPES = new Set<DatabaseType>([
|
||||
"clickhouse",
|
||||
"dameng",
|
||||
"gaussdb",
|
||||
"kingbase",
|
||||
"vastbase",
|
||||
"goldendb",
|
||||
]);
|
||||
|
||||
export const TABLE_IMPORT_SUPPORTED_TYPES = new Set<DatabaseType>([
|
||||
@@ -49,6 +57,9 @@ export const TABLE_IMPORT_SUPPORTED_TYPES = new Set<DatabaseType>([
|
||||
"redshift",
|
||||
"dameng",
|
||||
"gaussdb",
|
||||
"kingbase",
|
||||
"vastbase",
|
||||
"goldendb",
|
||||
]);
|
||||
|
||||
export const TABLE_STRUCTURE_SUPPORTED_TYPES = new Set<DatabaseType>(["mysql", "postgres", "sqlite", "sqlserver"]);
|
||||
@@ -85,6 +96,8 @@ export const TREE_SCHEMA_TYPES = new Set<DatabaseType>(["postgres", "redshift",
|
||||
|
||||
export const PG_LIKE_STRUCTURE_TYPES = new Set<DatabaseType>(["postgres", "redshift", "gaussdb"]);
|
||||
|
||||
export const AGENT_DRIVER_TYPES = new Set<DatabaseType>(["dameng", "kingbase", "vastbase", "goldendb"]);
|
||||
|
||||
export const TRANSFER_SQL_TYPES = new Set<DatabaseType>([
|
||||
"mysql",
|
||||
"postgres",
|
||||
|
||||
@@ -159,6 +159,9 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
redshift: "Redshift",
|
||||
dameng: "DM (Dameng)",
|
||||
gaussdb: "GaussDB",
|
||||
kingbase: "KingBase",
|
||||
vastbase: "Vastbase",
|
||||
goldendb: "GoldenDB",
|
||||
};
|
||||
|
||||
const profile = config.driver_profile || config.db_type;
|
||||
@@ -167,6 +170,12 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
dbType = "gaussdb" as ConnectionConfig["db_type"];
|
||||
} else if (profile === "redshift" && dbType === "postgres") {
|
||||
dbType = "redshift" as ConnectionConfig["db_type"];
|
||||
} else if (profile === "kingbase" && dbType === "postgres") {
|
||||
dbType = "kingbase" as ConnectionConfig["db_type"];
|
||||
} else if (profile === "vastbase" && dbType === "postgres") {
|
||||
dbType = "vastbase" as ConnectionConfig["db_type"];
|
||||
} else if (profile === "goldendb" && dbType === "mysql") {
|
||||
dbType = "goldendb" as ConnectionConfig["db_type"];
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -14,6 +14,9 @@ export type DatabaseType =
|
||||
| "redshift"
|
||||
| "dameng"
|
||||
| "gaussdb"
|
||||
| "kingbase"
|
||||
| "vastbase"
|
||||
| "goldendb"
|
||||
| "jdbc";
|
||||
|
||||
export interface ConnectionConfig {
|
||||
|
||||
Reference in New Issue
Block a user