Merge pull request 'fix(proxmox): fix add-remote IPC failure and URL construction' (#122) from fix/proxmox-add-remote into beta
All checks were successful
Release Beta / autotag (push) Successful in 9s
Release Beta / changelog (push) Successful in 1m30s
Test / frontend-tests (push) Successful in 1m50s
Test / frontend-typecheck (push) Successful in 2m2s
Release Beta / build-macos-arm64 (push) Successful in 9m23s
Release Beta / build-linux-amd64 (push) Successful in 10m10s
Release Beta / build-windows-amd64 (push) Successful in 11m58s
Release Beta / build-linux-arm64 (push) Successful in 13m10s
Test / rust-fmt-check (push) Successful in 18m36s
Test / rust-clippy (push) Successful in 20m9s
Test / rust-tests (push) Successful in 22m35s

Reviewed-on: #122
This commit is contained in:
sarman 2026-06-19 22:03:58 +00:00
commit bb5c820881
3 changed files with 19 additions and 11 deletions

View File

@ -38,7 +38,7 @@ pub async fn add_proxmox_cluster(
cluster_type: ClusterType, cluster_type: ClusterType,
connection: ClusterConnection, connection: ClusterConnection,
username: String, username: String,
password: &str, password: String,
state: State<'_, AppState>, state: State<'_, AppState>,
) -> Result<ClusterInfo, String> { ) -> Result<ClusterInfo, String> {
// Create client (no live auth — credentials stored and used on first connect) // Create client (no live auth — credentials stored and used on first connect)

View File

@ -57,7 +57,10 @@ impl ProxmoxClient {
/// Authenticate with root username and password /// Authenticate with root username and password
/// Returns the API ticket for subsequent requests /// Returns the API ticket for subsequent requests
pub async fn authenticate(&self, password: &str) -> Result<String> { pub async fn authenticate(&self, password: &str) -> Result<String> {
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)]; 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 /// Get the full API URL for a given path
fn get_api_url(&self, path: &str) -> String { fn get_api_url(&self, path: &str) -> String {
format!( format!(
"{}/api2/json/{}", "https://{}:{}/api2/json/{}",
self.base_url, self.base_url,
self.port,
path.trim_start_matches('/') path.trim_start_matches('/')
) )
} }
@ -266,30 +270,34 @@ impl ProxmoxClient {
mod tests { mod tests {
use super::*; 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] #[test]
fn test_proxmox_client_new() { fn test_proxmox_client_new() {
let client = ProxmoxClient::new("https://pve.example.com", 8006, "root@pam"); let client = ProxmoxClient::new("pve.example.com", 8006, "root@pam");
assert_eq!(client.base_url(), "https://pve.example.com"); assert_eq!(client.base_url(), "pve.example.com");
assert_eq!(client.port(), 8006); assert_eq!(client.port(), 8006);
assert_eq!(client.username(), "root@pam"); assert_eq!(client.username(), "root@pam");
} }
#[test] #[test]
fn test_proxmox_client_with_trailing_slash() { fn test_proxmox_client_with_trailing_slash() {
let client = ProxmoxClient::new("https://pve.example.com/", 8006, "root@pam"); let client = ProxmoxClient::new("pve.example.com/", 8006, "root@pam");
assert_eq!(client.base_url(), "https://pve.example.com"); assert_eq!(client.base_url(), "pve.example.com");
} }
#[test] #[test]
fn test_get_api_url() { 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!( assert_eq!(
client.get_api_url("cluster/resources"), 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!( assert_eq!(
client.get_api_url("/cluster/resources"), client.get_api_url("/cluster/resources"),
"https://pve.example.com/api2/json/cluster/resources" "https://pve.example.com:8006/api2/json/cluster/resources"
); );
} }
} }

View File

@ -61,7 +61,7 @@ export function AddRemoteForm({ onAdd, onCancel }: AddRemoteFormProps) {
try { try {
await onAdd(config); await onAdd(config);
} catch (err) { } catch (err) {
setError(err instanceof Error ? err.message : 'Failed to add remote'); setError(String(err));
} finally { } finally {
setLoading(false); setLoading(false);
} }