feat(agents): concurrent all-agent-driver upgrade
This commit is contained in:
1 parent
2a99f91099
commit
95db01e359
15 files changed
+1083
-282
No files matched your search
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, computed, watch, nextTick, type Ref } from "vue";
|
||||
import { ref, reactive, onMounted, onUnmounted, computed, watch, nextTick } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { Activity, ExternalLink, Cpu, FolderOpen, FolderSync, MemoryStick, Search, Square, Trash2, Download, RotateCcw, Loader2, RefreshCw, Check, Clock3, FileUp } from "@lucide/vue";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -18,7 +18,17 @@ import type { JdbcDriverInfo, JdbcLocalBundleInfo, JdbcMavenBundleInfo, JdbcPlug
|
||||
import * as api from "@/lib/backend/api";
|
||||
import type { AgentDriverInfo, DriverRuntimeInfo, DriverRuntimeSummary, DriverStoreUsage, JavaRuntimeConfig } from "@/lib/backend/api";
|
||||
import { formatRuntimeBytes, formatRuntimeCpu, formatRuntimeUptime, runtimeHealthClass, runtimeStatusClass, runtimeStatusDotClass } from "@/lib/connection/driverRuntimePresentation";
|
||||
import { addDriverInstallQueue, driverInstallProgressChannel, driverInstallProgressPercent, isDriverInstallProgressTarget, removeDriverInstallQueue, takeNextDriverInstallQueue, updateDriverInstallProgress, type DriverInstallProgress } from "@/lib/connection/driverInstallProgressUi";
|
||||
import {
|
||||
addDriverInstallQueue,
|
||||
driverInstallProgressChannel,
|
||||
driverInstallProgressPercent,
|
||||
isDriverInstallProgressForOperation,
|
||||
isDriverInstallProgressTarget,
|
||||
removeDriverInstallQueue,
|
||||
takeNextDriverInstallQueue,
|
||||
updatePerDriverProgress,
|
||||
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";
|
||||
@@ -166,13 +176,13 @@ const drivers = ref<AgentDriverInfo[]>([]);
|
||||
const agentDriverSearch = ref("");
|
||||
const installing = ref<string | null>(null);
|
||||
const upgradingAll = ref(false);
|
||||
const upgradingCurrent = ref("");
|
||||
const upgradingIndex = ref(0);
|
||||
const upgradingCompletedCount = ref(0);
|
||||
const upgradingTotal = ref(0);
|
||||
const queuedDriverInstalls = ref<string[]>([]);
|
||||
const reinstallingJre = ref<string | null>(null);
|
||||
const activeAgentOperationId = ref<string | null>(null);
|
||||
const refreshing = ref(false);
|
||||
const agentProgress = ref<DriverInstallProgress | null>(null);
|
||||
const agentProgressByDbType = reactive<Record<string, DriverInstallProgress | null | undefined>>({});
|
||||
const jdbcPluginProgress = ref<DriverInstallProgress | null>(null);
|
||||
const javaRuntimeConfig = ref<JavaRuntimeConfig>({ mode: "managed", custom_java_path: null });
|
||||
const customJavaPath = ref("");
|
||||
@@ -188,7 +198,7 @@ const DRIVER_RUNTIME_POLL_MS = 5000;
|
||||
const OFFLINE_DRIVER_DOWNLOAD_URL = "https://dbxio.com/cn/drivers";
|
||||
|
||||
let unlisten: (() => void) | null = null;
|
||||
const lastAgentProgressPercent = ref<number | null>(null);
|
||||
const lastAgentProgressPercent: Record<string, number> = {};
|
||||
const lastJdbcPluginProgressPercent = ref<number | null>(null);
|
||||
|
||||
const installedJres = computed(() => {
|
||||
@@ -201,7 +211,7 @@ const installedJres = computed(() => {
|
||||
return [...jreMap.entries()].map(([key, installed]) => ({ key, installed })).sort((a, b) => b.key.localeCompare(a.key));
|
||||
});
|
||||
|
||||
function formatProgressText(p: DriverInstallProgress | null, includeBatch: boolean): string {
|
||||
function formatProgressText(p: DriverInstallProgress | null | undefined): string {
|
||||
if (!p) return "";
|
||||
if (p.step === "jre-extract") return t("driverStore.progressJreExtract");
|
||||
if (p.step === "jdbc-plugin-extract") return t("driverStore.progressJdbcPluginExtract");
|
||||
@@ -210,27 +220,49 @@ function formatProgressText(p: DriverInstallProgress | null, includeBatch: boole
|
||||
const pct = Math.round(((p.downloaded ?? 0) / p.total) * 100);
|
||||
const dl = formatSize(p.downloaded ?? 0);
|
||||
const total = formatSize(p.total);
|
||||
const prefix = includeBatch && upgradingAll.value && upgradingCurrent.value ? `[${upgradingIndex.value}/${upgradingTotal.value}] ${upgradingCurrent.value} - ` : "";
|
||||
return `${prefix}${label} ${dl} / ${total} (${pct}%)`;
|
||||
return `${label} ${dl} / ${total} (${pct}%)`;
|
||||
}
|
||||
|
||||
const agentProgressText = computed(() => formatProgressText(agentProgress.value, true));
|
||||
const jdbcPluginProgressText = computed(() => formatProgressText(jdbcPluginProgress.value, false));
|
||||
function getAgentProgressText(dbType: string): string {
|
||||
return formatProgressText(agentProgressByDbType[dbType]);
|
||||
}
|
||||
|
||||
function progressNumber(progress: DriverInstallProgress | null, lastProgressPercent: Ref<number | null>): number | null {
|
||||
function getJdbcPluginProgressTitle(fallback: string): string {
|
||||
return jdbcPluginProgressText.value || fallback;
|
||||
}
|
||||
|
||||
function getAgentProgressTitle(dbType: string, fallback: string): string {
|
||||
return getAgentProgressText(dbType) || formatProgressText(agentProgressByDbType[dbType]) || fallback;
|
||||
}
|
||||
|
||||
const jdbcPluginProgressText = computed(() => formatProgressText(jdbcPluginProgress.value));
|
||||
|
||||
function getAgentProgressPercent(dbType: string): number | null {
|
||||
const progress = agentProgressByDbType[dbType];
|
||||
const next = driverInstallProgressPercent(progress);
|
||||
if (next !== null) {
|
||||
lastProgressPercent.value = next;
|
||||
lastAgentProgressPercent[dbType] = next;
|
||||
}
|
||||
return next ?? lastProgressPercent.value;
|
||||
return next ?? lastAgentProgressPercent[dbType] ?? null;
|
||||
}
|
||||
|
||||
const agentProgressNumber = computed(() => progressNumber(agentProgress.value, lastAgentProgressPercent));
|
||||
const jdbcPluginProgressNumber = computed(() => progressNumber(jdbcPluginProgress.value, lastJdbcPluginProgressPercent));
|
||||
const jdbcPluginProgressNumber = computed(() => {
|
||||
const next = driverInstallProgressPercent(jdbcPluginProgress.value);
|
||||
if (next !== null) {
|
||||
lastJdbcPluginProgressPercent.value = next;
|
||||
}
|
||||
return next ?? lastJdbcPluginProgressPercent.value;
|
||||
});
|
||||
|
||||
function resetAgentInstallProgress() {
|
||||
agentProgress.value = null;
|
||||
lastAgentProgressPercent.value = null;
|
||||
for (const key of Object.keys(agentProgressByDbType)) {
|
||||
delete agentProgressByDbType[key];
|
||||
}
|
||||
for (const key of Object.keys(lastAgentProgressPercent)) {
|
||||
delete lastAgentProgressPercent[key];
|
||||
}
|
||||
jreReinstallProgress.value = null;
|
||||
lastJreReinstallPercent.value = null;
|
||||
}
|
||||
|
||||
function resetJdbcPluginInstallProgress() {
|
||||
@@ -281,7 +313,7 @@ function isDriverProgressActive(dbType: string): boolean {
|
||||
return isDriverInstallProgressTarget(dbType, {
|
||||
installing: installing.value,
|
||||
upgradingAll: upgradingAll.value,
|
||||
progress: agentProgress.value,
|
||||
progressMap: agentProgressByDbType,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -289,12 +321,18 @@ function driverRequiresJavaRuntime(driver: AgentDriverInfo): boolean {
|
||||
return driver.requires_java_runtime ?? Boolean(driver.jre);
|
||||
}
|
||||
|
||||
function agentProgressTitle(fallback: string): string {
|
||||
return agentProgressText.value || fallback;
|
||||
// JRE reinstall is single-threaded, so a simpler model suffices.
|
||||
const jreReinstallProgress = ref<DriverInstallProgress | null>(null);
|
||||
const lastJreReinstallPercent = ref<number | null>(null);
|
||||
|
||||
function getJreReinstallPercent(): number | null {
|
||||
const next = driverInstallProgressPercent(jreReinstallProgress.value);
|
||||
if (next !== null) lastJreReinstallPercent.value = next;
|
||||
return next ?? lastJreReinstallPercent.value;
|
||||
}
|
||||
|
||||
function jdbcPluginProgressTitle(fallback: string): string {
|
||||
return jdbcPluginProgressText.value || fallback;
|
||||
function getJreReinstallTitle(fallback: string): string {
|
||||
return formatProgressText(jreReinstallProgress.value) || fallback;
|
||||
}
|
||||
|
||||
function isPrestoSqlBuiltinDriver(dbType: string): boolean {
|
||||
@@ -408,6 +446,7 @@ async function installDriver(dbType: string) {
|
||||
async function runDriverInstall(dbType: string) {
|
||||
const label = driverLabel(dbType);
|
||||
installing.value = dbType;
|
||||
activeAgentOperationId.value = crypto.randomUUID();
|
||||
resetAgentInstallProgress();
|
||||
try {
|
||||
if (isPrestoSqlBuiltinDriver(dbType)) {
|
||||
@@ -426,13 +465,14 @@ async function runDriverInstall(dbType: string) {
|
||||
toast(t("driverStore.driverUpdateBlocked", { labels: blockers.map((blocker) => blocker.label).join(", ") }));
|
||||
return;
|
||||
}
|
||||
await api.installAgent(dbType);
|
||||
await api.installAgent(dbType, activeAgentOperationId.value);
|
||||
await refreshAgents();
|
||||
toast(t("driverStore.driverInstallSuccess", { label }));
|
||||
} catch (e: any) {
|
||||
toast(t("driverStore.driverInstallFailed", { label, error: e }));
|
||||
} finally {
|
||||
installing.value = null;
|
||||
activeAgentOperationId.value = null;
|
||||
resetAgentInstallProgress();
|
||||
}
|
||||
}
|
||||
@@ -450,16 +490,19 @@ async function runQueuedDriverInstalls() {
|
||||
|
||||
async function upgradeAll() {
|
||||
upgradingAll.value = true;
|
||||
activeAgentOperationId.value = crypto.randomUUID();
|
||||
upgradingCompletedCount.value = 0;
|
||||
queuedDriverInstalls.value = [];
|
||||
resetAgentInstallProgress();
|
||||
try {
|
||||
const updatableDbTypes = drivers.value.filter((driver) => driver.update_available).map((driver) => driver.db_type);
|
||||
upgradingTotal.value = updatableDbTypes.length;
|
||||
const blockers = await api.checkAgentUpdateBlockers(updatableDbTypes);
|
||||
if (blockers.length > 0) {
|
||||
toast(t("driverStore.driverUpdateBlocked", { labels: blockers.map((blocker) => blocker.label).join(", ") }));
|
||||
return;
|
||||
}
|
||||
const result = await api.upgradeAllAgents();
|
||||
const result = await api.upgradeAllAgents(activeAgentOperationId.value);
|
||||
await refreshAgents();
|
||||
if (result.failed.length > 0) {
|
||||
const failedLabels = result.failed.map((item) => drivers.value.find((driver) => driver.db_type === item.db_type)?.label ?? item.db_type).join(", ");
|
||||
@@ -471,8 +514,8 @@ async function upgradeAll() {
|
||||
toast(t("driverStore.upgradeAllFailed", { error: e }));
|
||||
} finally {
|
||||
upgradingAll.value = false;
|
||||
upgradingCurrent.value = "";
|
||||
upgradingIndex.value = 0;
|
||||
activeAgentOperationId.value = null;
|
||||
upgradingCompletedCount.value = 0;
|
||||
upgradingTotal.value = 0;
|
||||
resetAgentInstallProgress();
|
||||
}
|
||||
@@ -554,15 +597,17 @@ async function importOfflineZip() {
|
||||
}
|
||||
if (!selected) return;
|
||||
importingZip.value = true;
|
||||
activeAgentOperationId.value = crypto.randomUUID();
|
||||
resetAgentInstallProgress();
|
||||
try {
|
||||
const count = await api.importAgentsFromZip(selected);
|
||||
const count = await api.importAgentsFromZip(selected, activeAgentOperationId.value);
|
||||
await refreshAgents();
|
||||
toast(t("driverStore.offlineImportSuccess", { count }));
|
||||
} catch (e: any) {
|
||||
toast(t("driverStore.offlineImportFailed", { error: e }));
|
||||
} finally {
|
||||
importingZip.value = false;
|
||||
activeAgentOperationId.value = null;
|
||||
resetAgentInstallProgress();
|
||||
}
|
||||
}
|
||||
@@ -583,9 +628,16 @@ async function importDriverFile(driver: AgentDriverInfo) {
|
||||
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 }));
|
||||
activeAgentOperationId.value = crypto.randomUUID();
|
||||
resetAgentInstallProgress();
|
||||
try {
|
||||
const count = await api.importAgentsFromZip(selected, activeAgentOperationId.value);
|
||||
await refreshAgents();
|
||||
toast(t("driverStore.offlineImportSuccess", { count }));
|
||||
} finally {
|
||||
activeAgentOperationId.value = null;
|
||||
resetAgentInstallProgress();
|
||||
}
|
||||
} else {
|
||||
await api.importAgentDriver(dbType, selected);
|
||||
await refreshAgents();
|
||||
@@ -620,15 +672,17 @@ async function importDriverFile(driver: AgentDriverInfo) {
|
||||
|
||||
async function reinstallJre(jreKey: string) {
|
||||
reinstallingJre.value = jreKey;
|
||||
activeAgentOperationId.value = crypto.randomUUID();
|
||||
resetAgentInstallProgress();
|
||||
try {
|
||||
await api.reinstallJre(jreKey);
|
||||
await api.reinstallJre(jreKey, activeAgentOperationId.value);
|
||||
await refreshAgents();
|
||||
toast(t("driverStore.jreReinstallSuccess", { jre: jreKey }));
|
||||
} catch (e: any) {
|
||||
toast(t("driverStore.jreReinstallFailed", { jre: jreKey, error: e }));
|
||||
} finally {
|
||||
reinstallingJre.value = null;
|
||||
activeAgentOperationId.value = null;
|
||||
resetAgentInstallProgress();
|
||||
}
|
||||
}
|
||||
@@ -1133,25 +1187,36 @@ onMounted(async () => {
|
||||
|
||||
unlisten = await api.listenAgentInstallProgress((payload) => {
|
||||
const incoming = payload as DriverInstallProgress;
|
||||
if (!isDriverInstallProgressForOperation(incoming, activeAgentOperationId.value)) return;
|
||||
const channel = driverInstallProgressChannel(incoming);
|
||||
const jdbcProgressBelongsToPrestoSql = channel === "jdbc-plugin" && installing.value === PRESTOSQL_DRIVER_DB_TYPE && !isInstallingJdbcPlugin.value;
|
||||
if (jdbcProgressBelongsToPrestoSql) {
|
||||
// PrestoSQL is shown as a built-in driver but installs through the JDBC plugin pipeline.
|
||||
agentProgress.value = updateDriverInstallProgress(agentProgress.value, incoming, "jdbc-plugin");
|
||||
} else {
|
||||
agentProgress.value = updateDriverInstallProgress(agentProgress.value, incoming, "agent");
|
||||
jdbcPluginProgress.value = updateDriverInstallProgress(jdbcPluginProgress.value, incoming, "jdbc-plugin");
|
||||
// Route its events into the per-driver map using the presto key.
|
||||
updatePerDriverProgress(agentProgressByDbType, { ...incoming, db_type: PRESTOSQL_DRIVER_DB_TYPE });
|
||||
} else if (channel === "agent") {
|
||||
if (incoming.db_type) {
|
||||
updatePerDriverProgress(agentProgressByDbType, incoming);
|
||||
// Track completions for the batch counter.
|
||||
if (incoming.step === "done" && upgradingAll.value) {
|
||||
upgradingCompletedCount.value++;
|
||||
}
|
||||
} else {
|
||||
// No db_type — single operation (e.g. JRE reinstall).
|
||||
jreReinstallProgress.value = incoming.step === "done" ? null : incoming;
|
||||
}
|
||||
jdbcPluginProgress.value = null; // clear any stale jdbc-only progress
|
||||
} else if (channel === "jdbc-plugin") {
|
||||
jdbcPluginProgress.value = incoming.step === "done" ? null : incoming;
|
||||
}
|
||||
if (payload.db_type && payload.total_drivers) {
|
||||
upgradingCurrent.value = drivers.value.find((d) => d.db_type === payload.db_type)?.label ?? payload.db_type;
|
||||
upgradingIndex.value = payload.current ?? 0;
|
||||
upgradingTotal.value = payload.total_drivers ?? 0;
|
||||
if (payload.total_drivers && !upgradingTotal.value) {
|
||||
upgradingTotal.value = payload.total_drivers;
|
||||
}
|
||||
// During a batch upgrade, refresh the list as soon as each driver finishes
|
||||
// (step="done") so its "Update" button disappears immediately instead of
|
||||
// staying disabled until the whole batch completes (step="all-done").
|
||||
// Single-driver installs (upgradingAll=false) are refreshed by runDriverInstall.
|
||||
if (upgradingAll.value && payload.step === "done" && channel === "agent") {
|
||||
if (upgradingAll.value && payload.step === "done" && channel === "agent" && payload.db_type) {
|
||||
void refreshAgents();
|
||||
}
|
||||
});
|
||||
@@ -1205,7 +1270,7 @@ watch(driverStoreTab, (tab) => {
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<Button variant="ghost" size="sm" class="h-7 rounded-md text-xs gap-1 text-muted-foreground" :disabled="importingZip" @click="importOfflineZip">
|
||||
<Button variant="ghost" size="sm" class="h-7 rounded-md text-xs gap-1 text-muted-foreground" :disabled="importingZip || installing !== null || upgradingAll || reinstallingJre !== null || queuedDriverInstalls.length > 0" @click="importOfflineZip">
|
||||
<FileUp class="h-3.5 w-3.5" />
|
||||
{{ importingZip ? t("driverStore.importing") : t("driverStore.importOfflinePackage") }}
|
||||
</Button>
|
||||
@@ -1256,16 +1321,16 @@ watch(driverStoreTab, (tab) => {
|
||||
</span>
|
||||
<Check v-if="jre.installed" class="h-4 w-4 text-green-600" />
|
||||
<span v-else class="text-xs text-muted-foreground">{{ t("driverStore.notInstalled") }}</span>
|
||||
<DriverInstallProgressCircle v-if="reinstallingJre === jre.key" :percent="agentProgressNumber" :title="agentProgressTitle(jre.installed ? t('driverStore.reinstalling') : t('driverStore.installing'))" />
|
||||
<Button v-else-if="!jre.installed" type="button" variant="default" size="sm" class="h-8 rounded-md text-xs" :disabled="reinstallingJre !== null || installing !== null" @click="reinstallJre(jre.key)">
|
||||
<DriverInstallProgressCircle v-if="reinstallingJre === jre.key" :percent="getJreReinstallPercent()" :title="getJreReinstallTitle(jre.installed ? t('driverStore.reinstalling') : t('driverStore.installing'))" />
|
||||
<Button v-else-if="!jre.installed" type="button" variant="default" size="sm" class="h-8 rounded-md text-xs" :disabled="reinstallingJre !== null || installing !== null || importingZip" @click="reinstallJre(jre.key)">
|
||||
<Download class="h-3.5 w-3.5 mr-1" />
|
||||
{{ t("driverStore.install") }}
|
||||
</Button>
|
||||
<Button v-else-if="jre.installed" type="button" variant="outline" size="sm" class="h-8 rounded-md text-xs" :disabled="reinstallingJre !== null || installing !== null" @click="reinstallJre(jre.key)">
|
||||
<Button v-else-if="jre.installed" type="button" variant="outline" size="sm" class="h-8 rounded-md text-xs" :disabled="reinstallingJre !== null || installing !== null || importingZip" @click="reinstallJre(jre.key)">
|
||||
<RotateCcw class="h-3.5 w-3.5 mr-1" />
|
||||
{{ t("driverStore.reinstall") }}
|
||||
</Button>
|
||||
<Button v-if="jre.installed" type="button" variant="ghost" size="sm" class="h-8 rounded-md text-xs text-muted-foreground hover:text-destructive" :disabled="reinstallingJre !== null || installing !== null" @click="uninstallJre(jre.key)">
|
||||
<Button v-if="jre.installed" type="button" variant="ghost" size="sm" class="h-8 rounded-md text-xs text-muted-foreground hover:text-destructive" :disabled="reinstallingJre !== null || installing !== null || importingZip" @click="uninstallJre(jre.key)">
|
||||
{{ t("driverStore.uninstall") }}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -1291,10 +1356,10 @@ watch(driverStoreTab, (tab) => {
|
||||
<div class="text-sm font-semibold">{{ t("driverStore.updatesAvailableTitle") }} ({{ filteredUpdatableCount }})</div>
|
||||
<p class="text-xs text-muted-foreground">{{ t("driverStore.updatesAvailableDescription") }}</p>
|
||||
</div>
|
||||
<Button size="sm" class="h-7 rounded-md text-xs shrink-0 ml-3" :disabled="installing !== null || upgradingAll" @click="upgradeAll">
|
||||
<Button size="sm" class="h-7 rounded-md text-xs shrink-0 ml-3" :disabled="installing !== null || upgradingAll || importingZip" @click="upgradeAll">
|
||||
<Loader2 v-if="upgradingAll" class="h-3 w-3 animate-spin mr-1" />
|
||||
<Download v-else class="h-3 w-3 mr-1" />
|
||||
{{ upgradingAll ? t("driverStore.upgradingProgress", { current: upgradingIndex, total: upgradingTotal }) : t("driverStore.upgradeAll") }}
|
||||
{{ upgradingAll ? t("driverStore.upgradingProgress", { current: upgradingCompletedCount, total: upgradingTotal }) : t("driverStore.upgradeAll") }}
|
||||
</Button>
|
||||
</div>
|
||||
<div
|
||||
@@ -1318,12 +1383,19 @@ watch(driverStoreTab, (tab) => {
|
||||
<span v-if="formatSize(driver.size)" class="rounded-full bg-muted px-2 py-0.5 text-[11px] text-muted-foreground">{{ formatSize(driver.size) }}</span>
|
||||
</div>
|
||||
<div class="driver-store-agent-actions flex shrink-0 items-center gap-2">
|
||||
<Button v-if="!driver.installed && isDriverQueued(driver.db_type)" size="sm" variant="outline" class="h-7 rounded-md border-green-500/30 bg-green-500/10 text-xs text-green-700 hover:bg-green-500/15" :disabled="upgradingAll" @click="removeQueuedDriverInstall(driver.db_type)">
|
||||
<Button
|
||||
v-if="!driver.installed && isDriverQueued(driver.db_type)"
|
||||
size="sm"
|
||||
variant="outline"
|
||||
class="h-7 rounded-md border-green-500/30 bg-green-500/10 text-xs text-green-700 hover:bg-green-500/15"
|
||||
:disabled="upgradingAll || importingZip"
|
||||
@click="removeQueuedDriverInstall(driver.db_type)"
|
||||
>
|
||||
<Clock3 class="h-3 w-3 mr-1" />
|
||||
{{ t("driverStore.queued") }}
|
||||
</Button>
|
||||
<DriverInstallProgressCircle v-else-if="!driver.installed && isDriverProgressActive(driver.db_type)" :percent="agentProgressNumber" :title="agentProgressTitle(t('driverStore.installing'))" />
|
||||
<Button v-else-if="!driver.installed" size="sm" class="h-7 rounded-md text-xs" :disabled="upgradingAll" @click="installDriver(driver.db_type)">
|
||||
<DriverInstallProgressCircle v-else-if="!driver.installed && isDriverProgressActive(driver.db_type)" :percent="getAgentProgressPercent(driver.db_type)" :title="getAgentProgressTitle(driver.db_type, t('driverStore.installing'))" />
|
||||
<Button v-else-if="!driver.installed" size="sm" class="h-7 rounded-md text-xs" :disabled="upgradingAll || importingZip" @click="installDriver(driver.db_type)">
|
||||
<Download class="h-3 w-3 mr-1" />
|
||||
{{ t("driverStore.install") }}
|
||||
</Button>
|
||||
@@ -1333,7 +1405,7 @@ watch(driverStoreTab, (tab) => {
|
||||
variant="ghost"
|
||||
class="h-7 w-7 rounded-md text-xs text-muted-foreground"
|
||||
:title="t('driverStore.importLocalJar')"
|
||||
:disabled="upgradingAll || installing !== null"
|
||||
:disabled="upgradingAll || installing !== null || importingZip"
|
||||
@click="importDriverFile(driver)"
|
||||
>
|
||||
<FileUp class="h-3.5 w-3.5" />
|
||||
@@ -1344,17 +1416,17 @@ watch(driverStoreTab, (tab) => {
|
||||
size="sm"
|
||||
variant="outline"
|
||||
class="h-7 rounded-md border-green-500/30 bg-green-500/10 text-xs text-green-700 hover:bg-green-500/15"
|
||||
:disabled="upgradingAll"
|
||||
:disabled="upgradingAll || importingZip"
|
||||
@click="removeQueuedDriverInstall(driver.db_type)"
|
||||
>
|
||||
<Clock3 class="h-3 w-3 mr-1" />
|
||||
{{ t("driverStore.queued") }}
|
||||
</Button>
|
||||
<DriverInstallProgressCircle v-else-if="driver.installed && driver.update_available && isDriverProgressActive(driver.db_type)" :percent="agentProgressNumber" :title="agentProgressTitle(t('driverStore.updating'))" />
|
||||
<Button v-else-if="driver.installed && driver.update_available" size="sm" variant="outline" class="h-7 rounded-md text-xs" :disabled="upgradingAll" @click="installDriver(driver.db_type)">
|
||||
<DriverInstallProgressCircle v-else-if="driver.installed && driver.update_available && isDriverProgressActive(driver.db_type)" :percent="getAgentProgressPercent(driver.db_type)" :title="getAgentProgressTitle(driver.db_type, t('driverStore.updating'))" />
|
||||
<Button v-else-if="driver.installed && driver.update_available" size="sm" variant="outline" class="h-7 rounded-md text-xs" :disabled="upgradingAll || importingZip" @click="installDriver(driver.db_type)">
|
||||
{{ t("driverStore.update") }}
|
||||
</Button>
|
||||
<Button v-if="driver.installed" variant="ghost" size="sm" class="h-7 rounded-md text-xs text-muted-foreground hover:text-destructive" :disabled="installing !== null || upgradingAll || isDriverQueued(driver.db_type)" @click="uninstallDriver(driver.db_type)">
|
||||
<Button v-if="driver.installed" variant="ghost" size="sm" class="h-7 rounded-md text-xs text-muted-foreground hover:text-destructive" :disabled="installing !== null || upgradingAll || importingZip || isDriverQueued(driver.db_type)" @click="uninstallDriver(driver.db_type)">
|
||||
{{ t("driverStore.uninstall") }}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -1384,12 +1456,19 @@ watch(driverStoreTab, (tab) => {
|
||||
<span v-if="formatSize(driver.size)" class="rounded-full bg-muted px-2 py-0.5 text-[11px] text-muted-foreground">{{ formatSize(driver.size) }}</span>
|
||||
</div>
|
||||
<div class="driver-store-agent-actions flex shrink-0 items-center gap-2">
|
||||
<Button v-if="!driver.installed && isDriverQueued(driver.db_type)" size="sm" variant="outline" class="h-7 rounded-md border-green-500/30 bg-green-500/10 text-xs text-green-700 hover:bg-green-500/15" :disabled="upgradingAll" @click="removeQueuedDriverInstall(driver.db_type)">
|
||||
<Button
|
||||
v-if="!driver.installed && isDriverQueued(driver.db_type)"
|
||||
size="sm"
|
||||
variant="outline"
|
||||
class="h-7 rounded-md border-green-500/30 bg-green-500/10 text-xs text-green-700 hover:bg-green-500/15"
|
||||
:disabled="upgradingAll || importingZip"
|
||||
@click="removeQueuedDriverInstall(driver.db_type)"
|
||||
>
|
||||
<Clock3 class="h-3 w-3 mr-1" />
|
||||
{{ t("driverStore.queued") }}
|
||||
</Button>
|
||||
<DriverInstallProgressCircle v-else-if="!driver.installed && isDriverProgressActive(driver.db_type)" :percent="agentProgressNumber" :title="agentProgressTitle(t('driverStore.installing'))" />
|
||||
<Button v-else-if="!driver.installed" size="sm" class="h-7 rounded-md text-xs" :disabled="upgradingAll" @click="installDriver(driver.db_type)">
|
||||
<DriverInstallProgressCircle v-else-if="!driver.installed && isDriverProgressActive(driver.db_type)" :percent="getAgentProgressPercent(driver.db_type)" :title="getAgentProgressTitle(driver.db_type, t('driverStore.installing'))" />
|
||||
<Button v-else-if="!driver.installed" size="sm" class="h-7 rounded-md text-xs" :disabled="upgradingAll || importingZip" @click="installDriver(driver.db_type)">
|
||||
<Download class="h-3 w-3 mr-1" />
|
||||
{{ t("driverStore.install") }}
|
||||
</Button>
|
||||
@@ -1399,7 +1478,7 @@ watch(driverStoreTab, (tab) => {
|
||||
variant="ghost"
|
||||
class="h-7 w-7 rounded-md text-xs text-muted-foreground"
|
||||
:title="t('driverStore.importLocalJar')"
|
||||
:disabled="upgradingAll || installing !== null"
|
||||
:disabled="upgradingAll || installing !== null || importingZip"
|
||||
@click="importDriverFile(driver)"
|
||||
>
|
||||
<FileUp class="h-3.5 w-3.5" />
|
||||
@@ -1410,17 +1489,17 @@ watch(driverStoreTab, (tab) => {
|
||||
size="sm"
|
||||
variant="outline"
|
||||
class="h-7 rounded-md border-green-500/30 bg-green-500/10 text-xs text-green-700 hover:bg-green-500/15"
|
||||
:disabled="upgradingAll"
|
||||
:disabled="upgradingAll || importingZip"
|
||||
@click="removeQueuedDriverInstall(driver.db_type)"
|
||||
>
|
||||
<Clock3 class="h-3 w-3 mr-1" />
|
||||
{{ t("driverStore.queued") }}
|
||||
</Button>
|
||||
<DriverInstallProgressCircle v-else-if="driver.installed && driver.update_available && isDriverProgressActive(driver.db_type)" :percent="agentProgressNumber" :title="agentProgressTitle(t('driverStore.updating'))" />
|
||||
<Button v-else-if="driver.installed && driver.update_available" size="sm" variant="outline" class="h-7 rounded-md text-xs" :disabled="upgradingAll" @click="installDriver(driver.db_type)">
|
||||
<DriverInstallProgressCircle v-else-if="driver.installed && driver.update_available && isDriverProgressActive(driver.db_type)" :percent="getAgentProgressPercent(driver.db_type)" :title="getAgentProgressTitle(driver.db_type, t('driverStore.updating'))" />
|
||||
<Button v-else-if="driver.installed && driver.update_available" size="sm" variant="outline" class="h-7 rounded-md text-xs" :disabled="upgradingAll || importingZip" @click="installDriver(driver.db_type)">
|
||||
{{ t("driverStore.update") }}
|
||||
</Button>
|
||||
<Button v-if="driver.installed" variant="ghost" size="sm" class="h-7 rounded-md text-xs text-muted-foreground hover:text-destructive" :disabled="installing !== null || upgradingAll || isDriverQueued(driver.db_type)" @click="uninstallDriver(driver.db_type)">
|
||||
<Button v-if="driver.installed" variant="ghost" size="sm" class="h-7 rounded-md text-xs text-muted-foreground hover:text-destructive" :disabled="installing !== null || upgradingAll || importingZip || isDriverQueued(driver.db_type)" @click="uninstallDriver(driver.db_type)">
|
||||
{{ t("driverStore.uninstall") }}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -1440,7 +1519,7 @@ watch(driverStoreTab, (tab) => {
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex shrink-0 items-center gap-3">
|
||||
<DriverInstallProgressCircle v-if="isInstallingJdbcPlugin" :percent="jdbcPluginProgressNumber" :title="jdbcPluginProgressTitle(t('driverStore.progressDownloadJdbcPlugin'))" />
|
||||
<DriverInstallProgressCircle v-if="isInstallingJdbcPlugin" :percent="jdbcPluginProgressNumber" :title="getJdbcPluginProgressTitle(t('driverStore.progressDownloadJdbcPlugin'))" />
|
||||
<span v-if="jdbcPluginStatus?.installed" class="text-xs" :class="jdbcPluginStatus.compatible ? 'text-green-600' : 'text-destructive'">
|
||||
{{
|
||||
jdbcPluginStatus.compatible
|
||||
|
||||
@@ -54,7 +54,7 @@ import { loadConnectionPickerView, saveConnectionPickerView, type DbPickerView }
|
||||
import { normalizeRocketmqNamesrvAddr } from "@/lib/connection/rocketmqNamesrv";
|
||||
import { normalizeRabbitmqAddresses } from "@/lib/connection/rabbitmqAddresses";
|
||||
import { detectMqUiAuthKind, isMqAuthKindAllowedForSystem, type MqUiAuthKind } from "@/lib/connection/mqAuth";
|
||||
import { driverInstallProgressChannel, driverInstallProgressPercent, type DriverInstallProgress } from "@/lib/connection/driverInstallProgressUi";
|
||||
import { driverInstallProgressChannel, driverInstallProgressPercent, isDriverInstallProgressForOperation, type DriverInstallProgress } from "@/lib/connection/driverInstallProgressUi";
|
||||
import { requiresSqlServerLegacyCompatibilityComponent, setSqlServerLegacyCompatibilityConfig, sqlServerUsesLegacyCompatibility, SQLSERVER_LEGACY_COMPATIBILITY_DRIVER_KEY } from "@/lib/connection/sqlServerLegacyCompatibility";
|
||||
import { normalizeNacosEndpoint } from "@/lib/nacos/nacosAdmin";
|
||||
import {
|
||||
@@ -175,6 +175,7 @@ const savedDatabaseInfoFingerprint = ref("");
|
||||
const savedConnectionConfigFingerprint = ref("");
|
||||
const showAgentInstallDialog = ref(false);
|
||||
const agentInstallRunning = ref(false);
|
||||
const agentInstallOperationId = ref<string | null>(null);
|
||||
const agentInstallDriverKey = ref("");
|
||||
const agentInstallLabel = ref("");
|
||||
const agentInstallProgress = ref<DriverInstallProgress | null>(null);
|
||||
@@ -1385,6 +1386,7 @@ async function refreshLocalAgentDrivers(): Promise<AgentDriverInstallState[]> {
|
||||
}
|
||||
|
||||
function beginAgentDriverInstall(driverKey: string, label: string) {
|
||||
agentInstallOperationId.value = crypto.randomUUID();
|
||||
agentInstallDriverKey.value = driverKey;
|
||||
agentInstallLabel.value = label;
|
||||
agentInstallProgress.value = null;
|
||||
@@ -1394,6 +1396,7 @@ function beginAgentDriverInstall(driverKey: string, label: string) {
|
||||
}
|
||||
|
||||
function finishAgentDriverInstall() {
|
||||
agentInstallOperationId.value = null;
|
||||
agentInstallRunning.value = false;
|
||||
agentInstallProgress.value = null;
|
||||
agentInstallError.value = "";
|
||||
@@ -1401,6 +1404,7 @@ function finishAgentDriverInstall() {
|
||||
}
|
||||
|
||||
function failAgentDriverInstall(error: unknown) {
|
||||
agentInstallOperationId.value = null;
|
||||
agentInstallRunning.value = false;
|
||||
agentInstallError.value = errorMessage(error);
|
||||
showAgentInstallDialog.value = true;
|
||||
@@ -1420,6 +1424,7 @@ function setAgentInstallDialogOpen(value: boolean) {
|
||||
function handleAgentInstallProgress(payload: DriverInstallProgress) {
|
||||
if (!agentInstallRunning.value || !agentInstallDriverKey.value) return;
|
||||
if (driverInstallProgressChannel(payload) !== "agent") return;
|
||||
if (!isDriverInstallProgressForOperation(payload, agentInstallOperationId.value)) return;
|
||||
if (payload.db_type && payload.db_type !== agentInstallDriverKey.value) return;
|
||||
if (payload.step === "done" || payload.step === "all-done") {
|
||||
agentInstallProgress.value = null;
|
||||
@@ -1454,7 +1459,7 @@ async function ensureRequiredAgentDriverInstalled(config: ConnectionConfig): Pro
|
||||
testResult.value = { ok: true, message: `Installing ${label} driver...` };
|
||||
beginAgentDriverInstall(driverKey, label);
|
||||
try {
|
||||
await api.installAgent(driverKey);
|
||||
await api.installAgent(driverKey, agentInstallOperationId.value ?? undefined);
|
||||
await refreshLocalAgentDrivers();
|
||||
finishAgentDriverInstall();
|
||||
} catch (error) {
|
||||
@@ -1485,7 +1490,7 @@ async function installSqlServerLegacyCompatibilityComponentIfNeeded(): Promise<b
|
||||
const label = t("connection.sqlServerLegacyCompatibilityComponent");
|
||||
beginAgentDriverInstall(SQLSERVER_LEGACY_COMPATIBILITY_DRIVER_KEY, label);
|
||||
try {
|
||||
await api.installAgent(SQLSERVER_LEGACY_COMPATIBILITY_DRIVER_KEY);
|
||||
await api.installAgent(SQLSERVER_LEGACY_COMPATIBILITY_DRIVER_KEY, agentInstallOperationId.value ?? undefined);
|
||||
await refreshLocalAgentDrivers();
|
||||
finishAgentDriverInstall();
|
||||
} catch (error) {
|
||||
|
||||
Reference in new issue
Block a user