feat(agents): add versioned offline driver packages
This commit is contained in:
1 parent
8d68a527a0
commit
79cd12d13a
29 files changed
+1372
-162
No files matched your search
@@ -21,6 +21,7 @@ import { formatRuntimeBytes, formatRuntimeCpu, formatRuntimeUptime, runtimeHealt
|
||||
import { addDriverInstallQueue, driverInstallProgressPercent, isDriverInstallProgressTarget, removeDriverInstallQueue, takeNextDriverInstallQueue, type DriverInstallProgress } from "@/lib/connection/driverInstallProgressUi";
|
||||
import { PRESTOSQL_DRIVER_DB_TYPE, prestoSqlBuiltinDriverRow, prestoSqlMavenBundle } from "@/lib/database/prestoSqlBuiltinDriver";
|
||||
import type { DriverStoreFocus } from "@/lib/connection/agentDriverInstallHint";
|
||||
import { isOfflineDriverPackage, webDriverImportAccept } from "@/lib/driverStore/driverImportSelection";
|
||||
|
||||
const { t } = useI18n();
|
||||
const { toast } = useToast();
|
||||
@@ -550,19 +551,38 @@ async function importOfflineZip() {
|
||||
}
|
||||
}
|
||||
|
||||
async function importDriverJar(dbType: string) {
|
||||
async function importDriverFile(driver: AgentDriverInfo) {
|
||||
const dbType = driver.db_type;
|
||||
if (isPrestoSqlBuiltinDriver(dbType)) {
|
||||
await importJdbcDrivers();
|
||||
return;
|
||||
}
|
||||
const blockers = await api.checkAgentUpdateBlockers([dbType]);
|
||||
if (blockers.length > 0) {
|
||||
toast(t("driverStore.driverUpdateBlocked", { labels: blockers.map((blocker) => blocker.label).join(", ") }));
|
||||
return;
|
||||
}
|
||||
const label = driverLabel(dbType);
|
||||
if (isWeb) {
|
||||
const file = await chooseWebFile(".jar");
|
||||
if (!file) return;
|
||||
try {
|
||||
await api.importAgentJar(dbType, file);
|
||||
const requiresJavaRuntime = driverRequiresJavaRuntime(driver);
|
||||
const isWindows = navigator.userAgent.toLowerCase().includes("windows");
|
||||
const installSelectedFile = async (selected: string | File) => {
|
||||
if (isOfflineDriverPackage(selected)) {
|
||||
const count = await api.importAgentsFromZip(selected);
|
||||
await refreshAgents();
|
||||
toast(t("driverStore.offlineImportSuccess", { count }));
|
||||
} else {
|
||||
await api.importAgentDriver(dbType, selected);
|
||||
await refreshAgents();
|
||||
toast(t("driverStore.driverImportSuccess", { label }));
|
||||
}
|
||||
};
|
||||
if (isWeb) {
|
||||
// Native release assets have no extension on macOS/Linux, so do not apply
|
||||
// a browser accept filter that would make the correct file unselectable.
|
||||
const file = await chooseWebFile(webDriverImportAccept(requiresJavaRuntime, isWindows));
|
||||
if (!file) return;
|
||||
try {
|
||||
await installSelectedFile(file);
|
||||
} catch (e: any) {
|
||||
toast(t("driverStore.driverImportFailed", { label, error: e }));
|
||||
}
|
||||
@@ -572,13 +592,11 @@ async function importDriverJar(dbType: string) {
|
||||
const selected = await open({
|
||||
title: t("driverStore.chooseDriverJar"),
|
||||
multiple: false,
|
||||
filters: [{ name: "JAR", extensions: ["jar"] }],
|
||||
filters: requiresJavaRuntime ? [{ name: "Driver package or JAR", extensions: ["zip", "jar"] }] : isWindows ? [{ name: "Driver package or executable", extensions: ["zip", "exe"] }] : undefined,
|
||||
});
|
||||
if (typeof selected !== "string") return;
|
||||
try {
|
||||
await api.importAgentJar(dbType, selected);
|
||||
await refreshAgents();
|
||||
toast(t("driverStore.driverImportSuccess", { label }));
|
||||
await installSelectedFile(selected);
|
||||
} catch (e: any) {
|
||||
toast(t("driverStore.driverImportFailed", { label, error: e }));
|
||||
}
|
||||
@@ -1293,7 +1311,7 @@ watch(driverStoreTab, (tab) => {
|
||||
class="h-7 w-7 rounded-[6px] text-xs text-muted-foreground"
|
||||
:title="t('driverStore.importLocalJar')"
|
||||
:disabled="upgradingAll || installing !== null"
|
||||
@click="importDriverJar(driver.db_type)"
|
||||
@click="importDriverFile(driver)"
|
||||
>
|
||||
<FileUp class="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
@@ -1359,7 +1377,7 @@ watch(driverStoreTab, (tab) => {
|
||||
class="h-7 w-7 rounded-[6px] text-xs text-muted-foreground"
|
||||
:title="t('driverStore.importLocalJar')"
|
||||
:disabled="upgradingAll || installing !== null"
|
||||
@click="importDriverJar(driver.db_type)"
|
||||
@click="importDriverFile(driver)"
|
||||
>
|
||||
<FileUp class="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
|
||||
@@ -4075,7 +4075,7 @@ export default {
|
||||
offlineImportFailed: "Offline import failed: {error}",
|
||||
driverImportSuccess: "{label} driver imported",
|
||||
driverImportFailed: "Failed to import {label} driver: {error}",
|
||||
chooseDriverJar: "Choose driver JAR file",
|
||||
chooseDriverJar: "Choose driver file",
|
||||
jreReinstallSuccess: "JRE {jre} reinstalled",
|
||||
jreReinstallFailed: "Failed to reinstall JRE {jre}: {error}",
|
||||
jreUninstallSuccess: "JRE {jre} uninstalled",
|
||||
@@ -4109,7 +4109,7 @@ export default {
|
||||
upgradingProgress: "Upgrading ({current}/{total})",
|
||||
upgradeAll: "Upgrade all",
|
||||
queued: "Queued",
|
||||
importLocalJar: "Import local JAR",
|
||||
importLocalJar: "Import local driver file",
|
||||
updating: "Updating",
|
||||
update: "Update",
|
||||
localInstall: "Local install",
|
||||
|
||||
@@ -3841,7 +3841,7 @@ export default withEnglishFallback({
|
||||
offlineImportFailed: "Error en la importación offline: {error}",
|
||||
driverImportSuccess: "Driver {label} importado",
|
||||
driverImportFailed: "Error al importar el driver {label}: {error}",
|
||||
chooseDriverJar: "Seleccionar archivo JAR del driver",
|
||||
chooseDriverJar: "Seleccionar archivo del driver",
|
||||
jreReinstallSuccess: "JRE {jre} reinstalado",
|
||||
jreReinstallFailed: "Error al reinstalar JRE {jre}: {error}",
|
||||
jreUninstallSuccess: "JRE {jre} desinstalado",
|
||||
@@ -3875,7 +3875,7 @@ export default withEnglishFallback({
|
||||
upgradingProgress: "Actualizando ({current}/{total})",
|
||||
upgradeAll: "Actualizar todo",
|
||||
queued: "En cola",
|
||||
importLocalJar: "Importar JAR local",
|
||||
importLocalJar: "Importar archivo de driver local",
|
||||
updating: "Actualizando",
|
||||
update: "Actualizar",
|
||||
localInstall: "Instalación local",
|
||||
|
||||
@@ -3839,7 +3839,7 @@ export default withEnglishFallback({
|
||||
offlineImportFailed: "Importazione offline non riuscita: {error}",
|
||||
driverImportSuccess: "Driver {label} importato",
|
||||
driverImportFailed: "Importazione del driver {label} non riuscita: {error}",
|
||||
chooseDriverJar: "Scegli il file JAR del driver",
|
||||
chooseDriverJar: "Scegli il file del driver",
|
||||
jreReinstallSuccess: "JRE {jre} reinstallato",
|
||||
jreReinstallFailed: "Reinstallazione del JRE {jre} non riuscita: {error}",
|
||||
jreUninstallSuccess: "JRE {jre} disinstallato",
|
||||
@@ -3873,7 +3873,7 @@ export default withEnglishFallback({
|
||||
upgradingProgress: "Aggiornamento in corso ({current}/{total})",
|
||||
upgradeAll: "Aggiorna tutti",
|
||||
queued: "In coda",
|
||||
importLocalJar: "Importa JAR locale",
|
||||
importLocalJar: "Importa file driver locale",
|
||||
updating: "Aggiornamento in corso",
|
||||
update: "Aggiorna",
|
||||
localInstall: "Installazione locale",
|
||||
|
||||
@@ -3839,7 +3839,7 @@ export default withEnglishFallback({
|
||||
offlineImportFailed: "オフラインインポートに失敗しました: {error}",
|
||||
driverImportSuccess: "{label} ドライバーをインポートしました",
|
||||
driverImportFailed: "{label} ドライバーのインポートに失敗しました: {error}",
|
||||
chooseDriverJar: "ドライバーJARファイルを選択",
|
||||
chooseDriverJar: "ドライバーファイルを選択",
|
||||
jreReinstallSuccess: "JRE {jre} を再インストールしました",
|
||||
jreReinstallFailed: "JRE {jre} の再インストールに失敗しました: {error}",
|
||||
jreUninstallSuccess: "JRE {jre} をアンインストールしました",
|
||||
@@ -3873,7 +3873,7 @@ export default withEnglishFallback({
|
||||
upgradingProgress: "アップグレード中 ({current}/{total})",
|
||||
upgradeAll: "すべてアップグレード",
|
||||
queued: "待機中",
|
||||
importLocalJar: "ローカルJARをインポート",
|
||||
importLocalJar: "ローカルドライバーファイルをインポート",
|
||||
updating: "更新中",
|
||||
update: "更新",
|
||||
localInstall: "ローカルインストール",
|
||||
|
||||
@@ -3841,7 +3841,7 @@ export default withEnglishFallback({
|
||||
offlineImportFailed: "Falha na importação offline: {error}",
|
||||
driverImportSuccess: "Driver {label} importado",
|
||||
driverImportFailed: "Falha ao importar o driver {label}: {error}",
|
||||
chooseDriverJar: "Escolher arquivo JAR do driver",
|
||||
chooseDriverJar: "Escolher arquivo do driver",
|
||||
jreReinstallSuccess: "JRE {jre} reinstalada",
|
||||
jreReinstallFailed: "Falha ao reinstalar a JRE {jre}: {error}",
|
||||
jreUninstallSuccess: "JRE {jre} desinstalada",
|
||||
@@ -3875,7 +3875,7 @@ export default withEnglishFallback({
|
||||
upgradingProgress: "Atualizando ({current}/{total})",
|
||||
upgradeAll: "Atualizar todos",
|
||||
queued: "Na fila",
|
||||
importLocalJar: "Importar JAR local",
|
||||
importLocalJar: "Importar arquivo de driver local",
|
||||
updating: "Atualizando",
|
||||
update: "Atualizar",
|
||||
localInstall: "Instalação local",
|
||||
|
||||
@@ -4065,7 +4065,7 @@ export default withEnglishFallback({
|
||||
offlineImportFailed: "离线导入失败: {error}",
|
||||
driverImportSuccess: "{label} 驱动导入成功",
|
||||
driverImportFailed: "{label} 驱动导入失败: {error}",
|
||||
chooseDriverJar: "选择驱动 JAR 文件",
|
||||
chooseDriverJar: "选择驱动文件",
|
||||
jreReinstallSuccess: "JRE {jre} 重新安装成功",
|
||||
jreReinstallFailed: "JRE {jre} 重新安装失败: {error}",
|
||||
jreUninstallSuccess: "JRE {jre} 已卸载",
|
||||
@@ -4099,7 +4099,7 @@ export default withEnglishFallback({
|
||||
upgradingProgress: "升级中 ({current}/{total})",
|
||||
upgradeAll: "全部升级",
|
||||
queued: "排队中",
|
||||
importLocalJar: "导入本地 JAR",
|
||||
importLocalJar: "导入本地驱动文件",
|
||||
updating: "更新中",
|
||||
update: "更新",
|
||||
localInstall: "本地安装",
|
||||
|
||||
@@ -3645,7 +3645,7 @@ export default withEnglishFallback({
|
||||
offlineImportFailed: "離線匯入失敗: {error}",
|
||||
driverImportSuccess: "{label} 驅動程式匯入成功",
|
||||
driverImportFailed: "{label} 驅動程式匯入失敗: {error}",
|
||||
chooseDriverJar: "選擇驅動程式 JAR 檔案",
|
||||
chooseDriverJar: "選擇驅動程式檔案",
|
||||
jreReinstallSuccess: "JRE {jre} 重新安裝成功",
|
||||
jreReinstallFailed: "JRE {jre} 重新安裝失敗: {error}",
|
||||
jreUninstallSuccess: "JRE {jre} 已解除安裝",
|
||||
@@ -3679,7 +3679,7 @@ export default withEnglishFallback({
|
||||
upgradingProgress: "更新中 ({current}/{total})",
|
||||
upgradeAll: "全部更新",
|
||||
queued: "排隊中",
|
||||
importLocalJar: "匯入本機 JAR",
|
||||
importLocalJar: "匯入本機驅動程式檔案",
|
||||
updating: "更新中",
|
||||
update: "更新",
|
||||
localInstall: "本機安裝",
|
||||
|
||||
@@ -109,7 +109,8 @@ export const getAgentJavaRuntimeConfig = forward("getAgentJavaRuntimeConfig");
|
||||
export const setAgentJavaRuntimeConfig = forward("setAgentJavaRuntimeConfig");
|
||||
export const invalidateAgentRegistryCache = forward("invalidateAgentRegistryCache");
|
||||
export const importAgentsFromZip = forward("importAgentsFromZip");
|
||||
export const importAgentJar = forward("importAgentJar");
|
||||
export const importAgentDriver = forward("importAgentDriver");
|
||||
export const importAgentJar = importAgentDriver;
|
||||
export async function reinstallJre(jreKey?: string) {
|
||||
const backend = await getBackend();
|
||||
return backend.reinstallJre(jreKey, useSettingsStore().editorSettings.updateDownloadSource);
|
||||
|
||||
@@ -457,23 +457,25 @@ export async function importAgentsFromZip(fileOrPath: string | File): Promise<nu
|
||||
return result.count;
|
||||
}
|
||||
|
||||
export async function importAgentJar(dbType: string, pathOrFile: string | File): Promise<void> {
|
||||
export async function importAgentDriver(dbType: string, pathOrFile: string | File): Promise<void> {
|
||||
let blob: Blob;
|
||||
let fileName: string;
|
||||
if (pathOrFile instanceof File) {
|
||||
blob = pathOrFile;
|
||||
fileName = pathOrFile.name;
|
||||
} else {
|
||||
fileName = pathOrFile.split("/").pop() || "driver.jar";
|
||||
fileName = pathOrFile.split("/").pop() || "agent";
|
||||
blob = await (await fetch(pathOrFile)).blob();
|
||||
}
|
||||
const formData = new FormData();
|
||||
formData.append("dbType", dbType);
|
||||
formData.append("file", blob, fileName);
|
||||
const uploadRes = await fetch(apiUrl("/api/agents/import-jar"), { method: "POST", body: formData });
|
||||
const uploadRes = await fetch(apiUrl("/api/agents/import-driver"), { method: "POST", body: formData });
|
||||
if (!uploadRes.ok) throw new Error(await uploadRes.text());
|
||||
}
|
||||
|
||||
export const importAgentJar = importAgentDriver;
|
||||
|
||||
export async function reinstallJre(jreKey?: string, _source?: UpdateDownloadSource): Promise<void> {
|
||||
await post("/api/agents/reinstall-jre", { jreKey });
|
||||
}
|
||||
|
||||
@@ -1341,13 +1341,15 @@ export async function importAgentsFromZip(path: string | File): Promise<number>
|
||||
return invoke("import_agents_from_zip", { path });
|
||||
}
|
||||
|
||||
export async function importAgentJar(dbType: string, path: string | File): Promise<void> {
|
||||
export async function importAgentDriver(dbType: string, path: string | File): Promise<void> {
|
||||
if (typeof path !== "string") {
|
||||
throw new Error("Desktop driver JAR import requires a local file path");
|
||||
throw new Error("Desktop driver import requires a local file path");
|
||||
}
|
||||
return invoke("import_agent_jar_cmd", { dbType, path });
|
||||
return invoke("import_agent_driver_cmd", { dbType, path });
|
||||
}
|
||||
|
||||
export const importAgentJar = importAgentDriver;
|
||||
|
||||
export async function reinstallJre(jreKey?: string, source?: UpdateDownloadSource): Promise<void> {
|
||||
return invoke("reinstall_jre", { jreKey, source });
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { isOfflineDriverPackage, webDriverImportAccept } from "./driverImportSelection";
|
||||
|
||||
describe("driver import selection", () => {
|
||||
it("recognizes ZIP paths and uploaded files case-insensitively", () => {
|
||||
expect(isOfflineDriverPackage("C:\\Downloads\\dbx-agent-h2-0.2.5.ZIP")).toBe(true);
|
||||
expect(isOfflineDriverPackage({ name: "dbx-agent-kingbase-0.1.34-macos-aarch64.zip" })).toBe(true);
|
||||
expect(isOfflineDriverPackage({ name: "dbx-agent-h2-0.2.5.jar" })).toBe(false);
|
||||
});
|
||||
|
||||
it("allows ZIP alongside the platform raw artifact", () => {
|
||||
expect(webDriverImportAccept(true, false)).toBe(".zip,.jar");
|
||||
expect(webDriverImportAccept(false, true)).toBe(".zip,.exe");
|
||||
expect(webDriverImportAccept(false, false)).toBe("");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
export type DriverImportSelection = string | { name: string };
|
||||
|
||||
export function isOfflineDriverPackage(selection: DriverImportSelection): boolean {
|
||||
const name = typeof selection === "string" ? selection : selection.name;
|
||||
return name.toLowerCase().endsWith(".zip");
|
||||
}
|
||||
|
||||
export function webDriverImportAccept(requiresJavaRuntime: boolean, isWindows: boolean): string {
|
||||
if (requiresJavaRuntime) return ".zip,.jar";
|
||||
return isWindows ? ".zip,.exe" : "";
|
||||
}
|
||||
Reference in new issue
Block a user