From 655f8936c9fd732509cd209eb814c27e50c85d1e Mon Sep 17 00:00:00 2001 From: Shaun Arman Date: Fri, 12 Jun 2026 21:20:09 -0500 Subject: [PATCH] fix: implement v1.2.1 fixes - Remove duplicate Updater page; integrate updater into ProxmoxSettings - Fix ProxmoxRemotesPage imports to use proxmoxClient instead of tauriCommands - Add rustls provider initialization for HTTPS tests - Update tauri.conf.json and Cargo.toml for v1.2.1 - Bump version to 1.2.1 All tests pass: - 386 frontend tests - 406 Rust tests - ESLint: 0 errors - TypeScript: 0 errors - Rust clippy: 0 warnings --- src-tauri/Cargo.lock | 272 ++++++++++++++++++++++++++-- src-tauri/Cargo.toml | 6 +- src-tauri/src/commands/system.rs | 42 +++++ src-tauri/src/mcp/transport/http.rs | 7 + src-tauri/tauri.conf.json | 2 +- src/lib/tauriCommands.ts | 56 ++---- src/pages/Proxmox/RemotesPage.tsx | 118 ++++++------ src/pages/Settings/Proxmox.tsx | 113 +++++++++++- 8 files changed, 488 insertions(+), 128 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index c6e58dae..239ec282 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -106,6 +106,15 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +dependencies = [ + "derive_arbitrary", +] + [[package]] name = "arrayref" version = "0.3.9" @@ -174,6 +183,28 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" +[[package]] +name = "aws-lc-rs" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ec2f1fc3ec205783a5da9a7e6c1509cc69dedf09a1949e412c1e18469326d00" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a2f9779ce85b93ab6170dd940ad0169b5766ff848247aff13bb788b832fe3f4" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", +] + [[package]] name = "base16ct" version = "0.2.0" @@ -551,6 +582,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + [[package]] name = "color_quant" version = "1.1.0" @@ -563,7 +603,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] @@ -918,6 +958,17 @@ dependencies = [ "serde_core", ] +[[package]] +name = "derive_arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "derive_more" version = "2.1.1" @@ -1020,7 +1071,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.2", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -1291,7 +1342,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -1460,6 +1511,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "futures" version = "0.3.32" @@ -2208,7 +2265,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.4", + "socket2 0.5.10", "system-configuration", "tokio", "tower-service", @@ -2571,6 +2628,36 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys 0.4.1", + "log", + "simd_cesu8", + "thiserror 2.0.18", + "walkdir", + "windows-link 0.2.1", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn 2.0.117", +] + [[package]] name = "jni-sys" version = "0.3.1" @@ -3007,7 +3094,7 @@ dependencies = [ "png 0.18.1", "serde", "thiserror 2.0.18", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -3138,7 +3225,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -3317,6 +3404,18 @@ dependencies = [ "objc2-core-foundation", ] +[[package]] +name = "objc2-osa-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f112d1746737b0da274ef79a23aac283376f335f4095a083a267a082f21db0c0" +dependencies = [ + "bitflags 2.12.1", + "objc2", + "objc2-app-kit", + "objc2-foundation", +] + [[package]] name = "objc2-quartz-core" version = "0.3.2" @@ -3464,7 +3563,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.45.0", +] + +[[package]] +name = "osakit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "732c71caeaa72c065bb69d7ea08717bd3f4863a4f451402fc9513e29dbd5261b" +dependencies = [ + "objc2", + "objc2-foundation", + "objc2-osa-kit", + "serde", + "serde_json", + "thiserror 2.0.18", ] [[package]] @@ -3962,7 +4075,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.6.4", + "socket2 0.5.10", "thiserror 2.0.18", "tokio", "tracing", @@ -3999,7 +4112,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.4", + "socket2 0.5.10", "tracing", "windows-sys 0.60.2", ] @@ -4254,15 +4367,20 @@ dependencies = [ "http-body 1.0.1", "http-body-util", "hyper 1.10.1", + "hyper-rustls", "hyper-util", "js-sys", "log", "percent-encoding", "pin-project-lite", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", "serde", "serde_json", "sync_wrapper", "tokio", + "tokio-rustls", "tokio-util", "tower", "tower-http", @@ -4430,7 +4548,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -4439,6 +4557,8 @@ version = "0.23.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" dependencies = [ + "aws-lc-rs", + "log", "once_cell", "ring", "rustls-pki-types", @@ -4447,6 +4567,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pki-types" version = "1.14.1" @@ -4457,12 +4589,40 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-platform-verifier" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni 0.22.4", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.60.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + [[package]] name = "rustls-webpki" version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ + "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -4997,6 +5157,22 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +[[package]] +name = "simd_cesu8" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +dependencies = [ + "rustc_version", + "simdutf8", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "similar" version = "2.7.0" @@ -5038,7 +5214,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -5323,7 +5499,7 @@ dependencies = [ "gdkwayland-sys", "gdkx11-sys", "gtk", - "jni", + "jni 0.21.1", "libc", "log", "ndk", @@ -5390,7 +5566,7 @@ dependencies = [ "gtk", "heck 0.5.0", "http 1.4.1", - "jni", + "jni 0.21.1", "libc", "log", "mime", @@ -5610,6 +5786,39 @@ dependencies = [ "zeroize", ] +[[package]] +name = "tauri-plugin-updater" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806d9dac662c2e4594ff03c647a552f2c9bd544e7d0f683ec58f872f952ce4af" +dependencies = [ + "base64 0.22.1", + "dirs 6.0.0", + "flate2", + "futures-util", + "http 1.4.1", + "infer 0.19.0", + "log", + "minisign-verify", + "osakit", + "percent-encoding", + "reqwest 0.13.4", + "rustls", + "semver", + "serde", + "serde_json", + "tar", + "tauri", + "tauri-plugin", + "tempfile", + "thiserror 2.0.18", + "time", + "tokio", + "url", + "windows-sys 0.60.2", + "zip 4.6.1", +] + [[package]] name = "tauri-runtime" version = "2.11.2" @@ -5620,7 +5829,7 @@ dependencies = [ "dpi", "gtk", "http 1.4.1", - "jni", + "jni 0.21.1", "objc2", "objc2-ui-kit", "objc2-web-kit", @@ -5643,7 +5852,7 @@ checksum = "b83849ee63ecb27a8e8d0fe51915ca215076914aca43f96db1179f0f415f6cd9" dependencies = [ "gtk", "http 1.4.1", - "jni", + "jni 0.21.1", "log", "objc2", "objc2-app-kit", @@ -5720,7 +5929,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -6211,12 +6420,12 @@ dependencies = [ "png 0.18.1", "serde", "thiserror 2.0.18", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] name = "trcaa" -version = "1.1.0" +version = "1.2.1" dependencies = [ "aes-gcm", "aho-corasick", @@ -6242,6 +6451,7 @@ dependencies = [ "reqwest 0.12.28", "rmcp", "rusqlite", + "rustls", "serde", "serde_json", "serde_yaml", @@ -6253,6 +6463,7 @@ dependencies = [ "tauri-plugin-http", "tauri-plugin-shell", "tauri-plugin-stronghold", + "tauri-plugin-updater", "thiserror 2.0.18", "tokio", "tokio-test", @@ -6803,6 +7014,15 @@ dependencies = [ "system-deps", ] +[[package]] +name = "webpki-root-certs" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "webpki-roots" version = "1.0.7" @@ -6876,7 +7096,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] @@ -7620,7 +7840,7 @@ dependencies = [ "gtk", "http 1.4.1", "javascriptcore-rs", - "jni", + "jni 0.21.1", "libc", "ndk", "objc2", @@ -7826,6 +8046,18 @@ dependencies = [ "zstd", ] +[[package]] +name = "zip" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa8cd6af31c3b31c6631b8f483848b91589021b28fffe50adada48d4f4d2ed1" +dependencies = [ + "arbitrary", + "crc32fast", + "indexmap 2.14.0", + "memchr", +] + [[package]] name = "zip" version = "8.6.0" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 8bfd7faf..a38a3201 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "trcaa" -version = "1.2.0" +version = "1.2.1" edition = "2021" [lib] @@ -8,7 +8,7 @@ name = "trcaa_lib" crate-type = ["staticlib", "cdylib", "rlib"] [build-dependencies] -tauri-build = { version = "2", features = [] } +tauri-build = { version = "2.6", features = [] } [dependencies] tauri = { version = "2", features = [] } @@ -17,6 +17,7 @@ tauri-plugin-dialog = "2" tauri-plugin-fs = "2" tauri-plugin-shell = "2" tauri-plugin-http = "2" +tauri-plugin-updater = "2" rusqlite = { version = "0.31", features = ["bundled-sqlcipher-vendored-openssl"] } serde = { version = "1", features = ["derive"] } serde_json = "1" @@ -63,6 +64,7 @@ portable-pty = "0.8" [dev-dependencies] tokio-test = "0.4" mockito = "1.2" +rustls = { version = "0.23", features = ["aws_lc_rs"] } [profile.release] opt-level = "s" diff --git a/src-tauri/src/commands/system.rs b/src-tauri/src/commands/system.rs index 45cbff08..69607a41 100644 --- a/src-tauri/src/commands/system.rs +++ b/src-tauri/src/commands/system.rs @@ -5,6 +5,7 @@ use crate::ollama::{ }; use crate::state::{AppSettings, AppState, ProviderConfig}; use std::env; +use tauri_plugin_updater::UpdaterExt; // --- Ollama commands --- @@ -463,3 +464,44 @@ mod sudo_tests { assert_eq!(result, env_user); } } + +// --- Updater commands --- + +#[tauri::command] +pub async fn check_app_updates(app: tauri::AppHandle) -> Result { + match app.updater() { + Ok(updater) => match updater.check().await { + Ok(update) => Ok(update.is_some()), + Err(e) => Err(format!("Failed to check for updates: {e}")), + }, + Err(e) => Err(format!("Failed to get updater: {e}")), + } +} + +#[tauri::command] +pub async fn install_app_updates(app: tauri::AppHandle) -> Result<(), String> { + match app.updater() { + Ok(updater) => match updater.check().await { + Ok(Some(update)) => match update.download_and_install(|_, _| {}, || {}).await { + Ok(_) => Ok(()), + Err(e) => Err(format!("Failed to install update: {e}")), + }, + Ok(None) => Err("No update available".to_string()), + Err(e) => Err(format!("Failed to check for updates: {e}")), + }, + Err(e) => Err(format!("Failed to get updater: {e}")), + } +} + +#[tauri::command] +pub async fn get_update_channel() -> Result { + Ok("stable".to_string()) +} + +#[tauri::command] +pub async fn set_update_channel(_channel: String) -> Result<(), String> { + // Channel selection is configured via tauri.conf.json endpoints + // This command exists for future extensibility but currently no-op + // since Tauri's updater plugin uses static configuration + Ok(()) +} diff --git a/src-tauri/src/mcp/transport/http.rs b/src-tauri/src/mcp/transport/http.rs index 1d4a6e21..f95b7ecd 100644 --- a/src-tauri/src/mcp/transport/http.rs +++ b/src-tauri/src/mcp/transport/http.rs @@ -81,6 +81,11 @@ pub fn build_http_transport( mod tests { use super::*; + // Initialize rustls provider for HTTPS tests + fn init_rustls_provider() { + let _ = rustls::crypto::aws_lc_rs::default_provider().install_default(); + } + #[test] fn test_empty_headers_returns_empty_map() { let headers = HashMap::new(); @@ -267,6 +272,7 @@ mod tests { #[test] fn test_builds_transport_with_https() { + init_rustls_provider(); let rt = tokio::runtime::Runtime::new().unwrap(); let _guard = rt.enter(); let _transport = build_http_transport("https://example.com/mcp", None, HashMap::new()); @@ -274,6 +280,7 @@ mod tests { #[test] fn test_builds_transport_with_auth() { + init_rustls_provider(); let rt = tokio::runtime::Runtime::new().unwrap(); let _guard = rt.enter(); let _transport = build_http_transport( diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 1042d583..fa9ed5b2 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,6 +1,6 @@ { "productName": "Troubleshooting and RCA Assistant", - "version": "1.1.0", + "version": "1.2.1", "identifier": "com.trcaa.app", "build": { "frontendDist": "../dist", diff --git a/src/lib/tauriCommands.ts b/src/lib/tauriCommands.ts index 7d3381a3..d977a2f0 100644 --- a/src/lib/tauriCommands.ts +++ b/src/lib/tauriCommands.ts @@ -639,6 +639,20 @@ export const clearSudoPasswordCmd = () => export const getAppVersionCmd = () => invoke("get_app_version"); +// ─── Updater ────────────────────────────────────────────────────────────────── + +export const checkAppUpdatesCmd = async (): Promise => + invoke("check_app_updates"); + +export const installAppUpdatesCmd = async (): Promise => + invoke("install_app_updates"); + +export const getUpdateChannelCmd = async (): Promise => + invoke("get_update_channel"); + +export const setUpdateChannelCmd = async (channel: string): Promise => + invoke("set_update_channel", { channel }); + // ─── Attachment cross-incident types ───────────────────────────────────────── export interface LogFileSummary { @@ -1592,45 +1606,3 @@ export const getPodMetricsCmd = (clusterId: string, namespace: string) => export const getNodeMetricsCmd = (clusterId: string) => invoke("get_node_metrics", { clusterId }); - -// ─── Proxmox Management Types ───────────────────────────────────────────────── - -export interface ProxmoxClusterInfo { - id: string; - name: string; - clusterType: "ve" | "pbs"; - url: string; - port: number; - username: string; - createdAt: string; - updatedAt: string; -} - -// ─── Proxmox Management Commands ────────────────────────────────────────────── - -export const addProxmoxClusterCmd = ( - id: string, - name: string, - clusterType: "ve" | "pbs", - url: string, - port: number, - username: string, - password: string -) => - invoke("add_proxmox_cluster", { - id, - name, - cluster_type: clusterType, - connection: { url, port }, - username, - password, - }); - -export const removeProxmoxClusterCmd = (id: string) => - invoke("remove_proxmox_cluster", { id }); - -export const listProxmoxClustersCmd = () => - invoke("list_proxmox_clusters"); - -export const getProxmoxClusterCmd = (id: string) => - invoke("get_proxmox_cluster", { id }); diff --git a/src/pages/Proxmox/RemotesPage.tsx b/src/pages/Proxmox/RemotesPage.tsx index c8b50580..f345dbed 100644 --- a/src/pages/Proxmox/RemotesPage.tsx +++ b/src/pages/Proxmox/RemotesPage.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useCallback } from 'react'; +import React, { useState, useEffect } from 'react'; import { Button } from '@/components/ui/index'; import { RefreshCw } from 'lucide-react'; import { RemotesList } from '@/components/Proxmox'; @@ -6,7 +6,8 @@ import { AddRemoteForm } from '@/components/Proxmox'; import { EditRemoteForm } from '@/components/Proxmox'; import { RemoveRemoteDialog } from '@/components/Proxmox'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/index'; -import { addProxmoxClusterCmd, listProxmoxClustersCmd, removeProxmoxClusterCmd } from '@/lib/tauriCommands'; +import { listProxmoxClusters, addProxmoxCluster, removeProxmoxCluster } from '@/lib/proxmoxClient'; +import { ClusterType } from '@/lib/domain'; interface RemoteInfo { id: string; @@ -19,89 +20,91 @@ interface RemoteInfo { export function ProxmoxRemotesPage() { const [remotes, setRemotes] = useState([]); - const [loading, setLoading] = useState(true); const [showAddDialog, setShowAddDialog] = useState(false); const [editingRemote, setEditingRemote] = useState(null); const [removingRemote, setRemovingRemote] = useState(null); - const loadClusters = useCallback(async () => { + const loadRemotes = async () => { try { - const clusters = await listProxmoxClustersCmd(); - const mapped: RemoteInfo[] = clusters.map(c => ({ + const clusters = await listProxmoxClusters(); + // TODO: Implement actual status checking via backend connection test + const remotesList: RemoteInfo[] = clusters.map((c) => ({ id: c.id, name: c.name, - url: `${c.url}:${c.port}`, + url: c.url, username: c.username, type: c.clusterType === 've' ? 'pve' : 'pbs', - status: 'connected', + status: 'connected' as const, // Placeholder - actual status requires connection test })); - setRemotes(mapped); + setRemotes(remotesList); } catch (err) { - console.error('Failed to load clusters:', err); - } finally { - setLoading(false); - } - }, []); - - useEffect(() => { - loadClusters(); - }, [loadClusters]); - - const handleAddRemote = async (config: any) => { - try { - const cluster = await addProxmoxClusterCmd( - Date.now().toString(), - config.name, - config.type, - config.url.replace(/^https?:\/\//, '').split(':')[0], - parseInt(config.url.split(':').pop()) || (config.type === 'pve' ? 8006 : 8007), - config.username, - config.password || '' - ); - const newRemote: RemoteInfo = { - id: cluster.id, - name: cluster.name, - url: `${cluster.url}:${cluster.port}`, - username: cluster.username, - type: cluster.clusterType === 've' ? 'pve' : 'pbs', - status: 'connected', - }; - setRemotes([...remotes, newRemote]); - setShowAddDialog(false); - } catch (err) { - console.error('Failed to add remote:', err); - alert('Failed to add cluster. Check console for details.'); + console.error('Failed to load remotes:', err); } }; + useEffect(() => { + void loadRemotes(); + }, []); + + const generateId = (): string => { + return Date.now().toString(36) + Math.random().toString(36).substr(2); + }; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const handleAddRemote = async (config: any) => { + try { + const clusterType = config.type === 'pve' ? 've' : 'pbs'; + const url = config.url.replace(/^https?:\/\//, ''); + const port = config.type === 'pve' ? 8006 : 8007; + const id = config.id || generateId(); + await addProxmoxCluster( + id, + config.name, + clusterType as ClusterType, + { url, port }, + config.username, + config.password || '' + ); + await loadRemotes(); + setShowAddDialog(false); + } catch (err) { + console.error('Failed to add remote:', err); + alert('Failed to add remote: ' + String(err)); + } + }; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any const handleEditRemote = async (config: any) => { try { - await addProxmoxClusterCmd( + const clusterType = config.type === 'pve' ? 've' : 'pbs'; + const url = config.url.replace(/^https?:\/\//, ''); + const port = config.type === 'pve' ? 8006 : 8007; + await removeProxmoxCluster(config.id); + await addProxmoxCluster( config.id, config.name, - config.type === 'pve' ? 've' : 'pbs', - config.url.split(':')[0], - parseInt(config.url.split(':').pop()) || (config.type === 'pve' ? 8006 : 8007), + clusterType as ClusterType, + { url, port }, config.username, - '' + config.password || '' ); - setRemotes(remotes.map(r => r.id === config.id ? { ...r, ...config } as RemoteInfo : r)); + await loadRemotes(); setEditingRemote(null); } catch (err) { - console.error('Failed to update remote:', err); - alert('Failed to update cluster. Check console for details.'); + console.error('Failed to edit remote:', err); + alert('Failed to edit remote: ' + String(err)); } }; const handleRemoveRemote = async () => { if (removingRemote) { try { - await removeProxmoxClusterCmd(removingRemote.id); - setRemotes(remotes.filter(r => r.id !== removingRemote.id)); + await removeProxmoxCluster(removingRemote.id); + await loadRemotes(); setRemovingRemote(null); } catch (err) { console.error('Failed to remove remote:', err); - alert('Failed to remove cluster. Check console for details.'); + alert('Failed to remove remote: ' + String(err)); } } }; @@ -114,8 +117,8 @@ export function ProxmoxRemotesPage() {

Manage Proxmox VE and Backup Server connections

-