feat: multi-JRE support, Oracle 10g driver profile, and JRE uninstall
- Support multiple JRE versions per agent driver (jre-8/, jre-17/) - Auto-migrate legacy jre/ directory to jre-17/ - Detect macOS Adoptium JRE 8 Contents/Home/ layout - Route Oracle 10g connections to oracle-10g agent via driver_profile - Add Oracle 10g option in connection dialog - Add JRE uninstall with dependency check - Show per-JRE status and controls in driver management UI
This commit is contained in:
parent
b4d1f4b04e
commit
809448a161
|
|
@ -161,6 +161,25 @@ pub async fn check_jre_installed(state: State<'_, Arc<AppState>>, jre_key: Optio
|
|||
Ok(state.agent_manager.is_jre_installed(key))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn uninstall_jre(state: State<'_, Arc<AppState>>, jre_key: String) -> Result<(), String> {
|
||||
let am = &state.agent_manager;
|
||||
let local_state = am.load_state();
|
||||
let dependents: Vec<&str> =
|
||||
local_state.installed_drivers.iter().filter(|(_, d)| d.jre == jre_key).map(|(k, _)| k.as_str()).collect();
|
||||
if !dependents.is_empty() {
|
||||
return Err(format!("JRE {} 正在被以下驱动使用: {},请先卸载这些驱动", jre_key, dependents.join(", ")));
|
||||
}
|
||||
let jre_dir = am.jre_dir(&jre_key);
|
||||
if jre_dir.exists() {
|
||||
std::fs::remove_dir_all(&jre_dir).map_err(|e| format!("Failed to remove JRE: {e}"))?;
|
||||
}
|
||||
let mut local_state = am.load_state();
|
||||
local_state.jre_versions.remove(&jre_key);
|
||||
am.save_state(&local_state)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn invalidate_agent_registry_cache() -> Result<(), String> {
|
||||
*REGISTRY_CACHE.lock().await = None;
|
||||
|
|
|
|||
|
|
@ -149,6 +149,7 @@ pub fn run() {
|
|||
commands::agents::install_agent,
|
||||
commands::agents::uninstall_agent,
|
||||
commands::agents::check_jre_installed,
|
||||
commands::agents::uninstall_jre,
|
||||
commands::agents::reinstall_jre,
|
||||
commands::agents::invalidate_agent_registry_cache,
|
||||
])
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ interface AgentDriverInfo {
|
|||
installed: boolean;
|
||||
installed_version: string | null;
|
||||
update_available: boolean;
|
||||
jre: string;
|
||||
jre_installed: boolean;
|
||||
}
|
||||
|
||||
interface InstallProgress {
|
||||
|
|
@ -38,14 +40,25 @@ interface InstallProgress {
|
|||
}
|
||||
|
||||
const drivers = ref<AgentDriverInfo[]>([]);
|
||||
const jreInstalled = ref(false);
|
||||
const installing = ref<string | null>(null);
|
||||
const reinstallingJre = ref(false);
|
||||
const reinstallingJre = ref<string | null>(null);
|
||||
const refreshing = ref(false);
|
||||
const progress = ref<InstallProgress | null>(null);
|
||||
|
||||
let unlisten: UnlistenFn | null = null;
|
||||
|
||||
const installedJres = computed(() => {
|
||||
const jreMap = new Map<string, boolean>();
|
||||
for (const d of drivers.value) {
|
||||
if (!jreMap.has(d.jre)) {
|
||||
jreMap.set(d.jre, d.jre_installed);
|
||||
}
|
||||
}
|
||||
return [...jreMap.entries()]
|
||||
.map(([key, installed]) => ({ key, installed }))
|
||||
.sort((a, b) => b.key.localeCompare(a.key));
|
||||
});
|
||||
|
||||
const progressText = computed(() => {
|
||||
const p = progress.value;
|
||||
if (!p) return "";
|
||||
|
|
@ -65,7 +78,6 @@ const progressPercent = computed(() => {
|
|||
});
|
||||
|
||||
async function refreshAgents() {
|
||||
jreInstalled.value = await invoke<boolean>("check_jre_installed");
|
||||
drivers.value = await invoke<AgentDriverInfo[]>("list_installed_agents");
|
||||
}
|
||||
|
||||
|
|
@ -106,21 +118,31 @@ async function uninstallDriver(dbType: string) {
|
|||
}
|
||||
}
|
||||
|
||||
async function reinstallJre() {
|
||||
reinstallingJre.value = true;
|
||||
async function reinstallJre(jreKey: string) {
|
||||
reinstallingJre.value = jreKey;
|
||||
progress.value = null;
|
||||
try {
|
||||
await invoke("reinstall_jre");
|
||||
await invoke("reinstall_jre", { jreKey });
|
||||
await refreshAgents();
|
||||
toast("JRE 重新安装成功");
|
||||
toast(`JRE ${jreKey} 重新安装成功`);
|
||||
} catch (e: any) {
|
||||
toast(`JRE 重新安装失败: ${e}`);
|
||||
toast(`JRE ${jreKey} 重新安装失败: ${e}`);
|
||||
} finally {
|
||||
reinstallingJre.value = false;
|
||||
reinstallingJre.value = null;
|
||||
progress.value = null;
|
||||
}
|
||||
}
|
||||
|
||||
async function uninstallJre(jreKey: string) {
|
||||
try {
|
||||
await invoke("uninstall_jre", { jreKey });
|
||||
await refreshAgents();
|
||||
toast(`JRE ${jreKey} 已卸载`);
|
||||
} catch (e: any) {
|
||||
toast(String(e));
|
||||
}
|
||||
}
|
||||
|
||||
function formatSize(bytes: number): string {
|
||||
if (!bytes) return "";
|
||||
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`;
|
||||
|
|
@ -238,12 +260,7 @@ async function deleteJdbcDriver(path: string) {
|
|||
// ──────────── Lifecycle ────────────
|
||||
|
||||
onMounted(async () => {
|
||||
const [jre, localDrivers] = await Promise.all([
|
||||
invoke<boolean>("check_jre_installed"),
|
||||
invoke<AgentDriverInfo[]>("list_installed_agents_local"),
|
||||
]);
|
||||
jreInstalled.value = jre;
|
||||
drivers.value = localDrivers;
|
||||
drivers.value = await invoke<AgentDriverInfo[]>("list_installed_agents_local");
|
||||
|
||||
invoke<AgentDriverInfo[]>("list_installed_agents").then((result) => {
|
||||
drivers.value = result;
|
||||
|
|
@ -290,29 +307,43 @@ onUnmounted(() => {
|
|||
<!-- Agent Tab -->
|
||||
<TabsContent value="agent" class="mt-5 space-y-5">
|
||||
<!-- JRE Runtime -->
|
||||
<div class="rounded-xl border bg-muted/20 p-4">
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<div v-if="installedJres.length > 0" class="rounded-xl border bg-muted/20 p-4 space-y-2.5">
|
||||
<div v-for="jre in installedJres" :key="jre.key" class="flex items-center justify-between gap-3">
|
||||
<div class="min-w-0">
|
||||
<div class="text-sm font-medium">JRE 运行时</div>
|
||||
<p v-if="!jreInstalled" class="text-xs text-muted-foreground mt-0.5">首次安装驱动时自动下载</p>
|
||||
<div class="text-sm font-medium">JRE {{ jre.key }} 运行时</div>
|
||||
</div>
|
||||
<div class="flex shrink-0 items-center gap-3">
|
||||
<span v-if="jreInstalled" class="text-xs text-green-600">已安装</span>
|
||||
<span v-if="jre.installed" class="text-xs text-green-600">已安装</span>
|
||||
<span v-else class="text-xs text-muted-foreground">未安装</span>
|
||||
<Button
|
||||
v-if="jreInstalled"
|
||||
v-if="jre.installed"
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
:disabled="reinstallingJre || installing !== null"
|
||||
@click="reinstallJre"
|
||||
:disabled="reinstallingJre !== null || installing !== null"
|
||||
@click="reinstallJre(jre.key)"
|
||||
>
|
||||
<RotateCcw class="h-3.5 w-3.5 mr-1" />
|
||||
{{ reinstallingJre ? "重装中..." : "重新安装" }}
|
||||
{{ reinstallingJre === jre.key ? "重装中..." : "重新安装" }}
|
||||
</Button>
|
||||
<Button
|
||||
v-if="jre.installed"
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
class="text-muted-foreground hover:text-destructive"
|
||||
:disabled="reinstallingJre !== null || installing !== null"
|
||||
@click="uninstallJre(jre.key)"
|
||||
>
|
||||
卸载
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="rounded-xl border bg-muted/20 p-4">
|
||||
<div class="text-sm font-medium">JRE 运行时</div>
|
||||
<p class="text-xs text-muted-foreground mt-0.5">首次安装驱动时自动下载</p>
|
||||
</div>
|
||||
|
||||
<!-- Progress bar -->
|
||||
<div v-if="progress" class="space-y-1.5 px-1">
|
||||
|
|
@ -340,6 +371,11 @@ onUnmounted(() => {
|
|||
<div class="text-sm font-medium">{{ driver.label }}</div>
|
||||
</div>
|
||||
<div class="flex shrink-0 items-center gap-1.5">
|
||||
<span
|
||||
v-if="driver.jre && driver.jre !== '17'"
|
||||
class="rounded-full bg-blue-500/10 px-2 py-0.5 text-[11px] text-blue-600"
|
||||
>JRE {{ driver.jre }}</span
|
||||
>
|
||||
<template v-if="driver.installed">
|
||||
<span class="rounded-full bg-muted px-2 py-0.5 text-[11px] text-muted-foreground"
|
||||
>v{{ driver.installed_version }}</span
|
||||
|
|
|
|||
|
|
@ -137,6 +137,7 @@ const driverProfiles: Record<
|
|||
},
|
||||
sqlserver: { type: "sqlserver", port: 1433, user: "sa", label: "SQL Server", icon: "sqlserver" },
|
||||
oracle: { type: "oracle", port: 1521, user: "system", label: "Oracle", icon: "oracle" },
|
||||
"oracle-10g": { type: "oracle", port: 1521, user: "system", label: "Oracle 10g", icon: "oracle" },
|
||||
elasticsearch: {
|
||||
type: "elasticsearch",
|
||||
port: 9200,
|
||||
|
|
@ -399,6 +400,7 @@ const dbOptions = [
|
|||
{ value: "clickhouse", label: "ClickHouse" },
|
||||
{ value: "sqlserver", label: "SQL Server" },
|
||||
{ value: "oracle", label: "Oracle" },
|
||||
{ value: "oracle-10g", label: "Oracle 10g" },
|
||||
{ value: "elasticsearch", label: "Elasticsearch" },
|
||||
{ value: "mariadb", label: "MariaDB" },
|
||||
{ value: "dm", label: "DM (Dameng)" },
|
||||
|
|
|
|||
Loading…
Reference in New Issue