From 1a586c86cd9d2a48ff54b5647df9de3efd621688 Mon Sep 17 00:00:00 2001 From: Shaun Arman Date: Fri, 19 Jun 2026 16:44:50 -0500 Subject: [PATCH] fix(proxmox): fix add-remote IPC failure and URL construction - Change password parameter from &str to String in add_proxmox_cluster so Tauri v2 IPC can deserialize it (requires DeserializeOwned + Send) - Construct full https:// URL with port in ProxmoxClient::get_api_url() and authenticate() since the frontend strips the protocol before sending the hostname - Show actual error string in AddRemoteForm instead of generic fallback (Tauri errors are plain strings, not Error instances) - Update client tests to reflect bare-hostname input from the frontend --- src-tauri/src/commands/proxmox.rs | 2 +- src-tauri/src/proxmox/client.rs | 26 ++++++++++++++++-------- src/components/Proxmox/AddRemoteForm.tsx | 2 +- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src-tauri/src/commands/proxmox.rs b/src-tauri/src/commands/proxmox.rs index 2338ed5c..7b23ce99 100644 --- a/src-tauri/src/commands/proxmox.rs +++ b/src-tauri/src/commands/proxmox.rs @@ -38,7 +38,7 @@ pub async fn add_proxmox_cluster( cluster_type: ClusterType, connection: ClusterConnection, username: String, - password: &str, + password: String, state: State<'_, AppState>, ) -> Result { // Create client (no live auth — credentials stored and used on first connect) diff --git a/src-tauri/src/proxmox/client.rs b/src-tauri/src/proxmox/client.rs index d2da7f84..d8b7485e 100644 --- a/src-tauri/src/proxmox/client.rs +++ b/src-tauri/src/proxmox/client.rs @@ -57,7 +57,10 @@ impl ProxmoxClient { /// Authenticate with root username and password /// Returns the API ticket for subsequent requests pub async fn authenticate(&self, password: &str) -> Result { - let url = format!("{}/api2/json/access/ticket", self.base_url); + let url = format!( + "https://{}:{}/api2/json/access/ticket", + self.base_url, self.port + ); let params = vec![("username", self.username.as_str()), ("password", password)]; @@ -95,8 +98,9 @@ impl ProxmoxClient { /// Get the full API URL for a given path fn get_api_url(&self, path: &str) -> String { format!( - "{}/api2/json/{}", + "https://{}:{}/api2/json/{}", self.base_url, + self.port, path.trim_start_matches('/') ) } @@ -266,30 +270,34 @@ impl ProxmoxClient { mod tests { use super::*; + // The frontend strips the protocol via parseRemoteUrl before sending to the backend, + // so ProxmoxClient always receives a bare hostname (no scheme, no port). + // get_api_url() is responsible for constructing the full https URL with port. + #[test] fn test_proxmox_client_new() { - let client = ProxmoxClient::new("https://pve.example.com", 8006, "root@pam"); - assert_eq!(client.base_url(), "https://pve.example.com"); + let client = ProxmoxClient::new("pve.example.com", 8006, "root@pam"); + assert_eq!(client.base_url(), "pve.example.com"); assert_eq!(client.port(), 8006); assert_eq!(client.username(), "root@pam"); } #[test] fn test_proxmox_client_with_trailing_slash() { - let client = ProxmoxClient::new("https://pve.example.com/", 8006, "root@pam"); - assert_eq!(client.base_url(), "https://pve.example.com"); + let client = ProxmoxClient::new("pve.example.com/", 8006, "root@pam"); + assert_eq!(client.base_url(), "pve.example.com"); } #[test] fn test_get_api_url() { - let client = ProxmoxClient::new("https://pve.example.com", 8006, "root@pam"); + let client = ProxmoxClient::new("pve.example.com", 8006, "root@pam"); assert_eq!( client.get_api_url("cluster/resources"), - "https://pve.example.com/api2/json/cluster/resources" + "https://pve.example.com:8006/api2/json/cluster/resources" ); assert_eq!( client.get_api_url("/cluster/resources"), - "https://pve.example.com/api2/json/cluster/resources" + "https://pve.example.com:8006/api2/json/cluster/resources" ); } } diff --git a/src/components/Proxmox/AddRemoteForm.tsx b/src/components/Proxmox/AddRemoteForm.tsx index eabc21bd..9b6bbf31 100644 --- a/src/components/Proxmox/AddRemoteForm.tsx +++ b/src/components/Proxmox/AddRemoteForm.tsx @@ -61,7 +61,7 @@ export function AddRemoteForm({ onAdd, onCancel }: AddRemoteFormProps) { try { await onAdd(config); } catch (err) { - setError(err instanceof Error ? err.message : 'Failed to add remote'); + setError(String(err)); } finally { setLoading(false); }