5.9 KiB
fix(proxmox): Reliable connect/reconnect after app restart
Description
Adding a new Proxmox host reported "connected" immediately, but after closing the app or clicking Disconnect the host could not be reconnected.
Three compounding bugs caused this:
-
authenticate()could never succeed on reconnect —ProxmoxClient::authenticate()calledresponse.json::<AuthResponse>()but the Proxmox API wraps every response in{"data": {...}}. The deserialiser always failed with "missing fieldticket", soget_proxmox_client_for_cluster(and the newconnect_proxmox_cluster) threw an error on every app-restart reconnect. -
False "connected" indicator on add —
add_proxmox_clusterinserted an unauthenticated client (no ticket) into the in-memory pool.list_proxmox_clustersreportedconnected: truejust because the HashMap key existed, even though any API call would have failed. -
Double-unwrap of
datain 10 commands —handle_responsealready strips the{"data": ...}envelope before returning to callers, butlist_acls,list_users,get_cluster_notes,search_proxmox_resources,get_node_status,get_syslog,list_network_interfaces,get_subscription_status,list_cluster_tasks, andlist_proxmox_containersall called.get("data")on the already-unwrapped value, causing them to always return "Invalid response format". -
VM list always empty —
list_vmsinvm.rsusedPOSToncluster/resources(a GET-only endpoint). The Proxmox API ignores the POST body and the function also had the same double-unwrap bug, meaning the resource list always came back empty. Additionally, VMs with nocpufield (e.g. stopped VMs) were silently dropped byfilter_mapusing?— fixed tounwrap_or(0.0). -
Double-unwrap in all other proxmox modules — the same
.get("data")double-unwrap was present across 18 module files (ceph, ceph_cluster, certificates, acme, firewall, sdn, ha, apt, updates, updates_ext, tasks, migration, metrics, shell, auth_realm, views, backup, vm). All 19 affected functions fixed in a single follow-up commit.
Additional gaps addressed:
CSRFPreventionTokenwas never sent on POST/PUT/DELETE, so all mutating operations (VM start/stop, firewall rules, etc.) would fail with "CSRF check failed".- Disconnect was UI-only with no backend call — the session stayed in the pool.
reqwest::Clientrejected self-signed Proxmox certificates.- No
connect_proxmox_cluster/disconnect_proxmox_clusterbackend commands existed; the Connect/Disconnect buttons in the Remotes page were wired to a non-existentping_proxmox_clusteror nothing at all.
Acceptance Criteria
- Adding a new Proxmox host authenticates immediately; the UI shows "connected" only when a real ticket has been obtained.
- Closing the app and re-opening it allows reconnecting via the Connect button without requiring the host to be removed and re-added.
- Clicking Disconnect removes the session from the backend pool; subsequent API calls fail with "Cluster not found" until Connect is clicked.
- All existing Rust tests (426) and frontend tests (386) continue to pass.
cargo clippy -- -D warningsandnpx eslint . --max-warnings 0report zero issues.
Work Implemented
| File | Change |
|---|---|
src-tauri/src/proxmox/client.rs |
Added ProxmoxEnvelope<T> wrapper; fixed authenticate() to use &mut self, parse the envelope, and store both ticket and CSRF token; added set_csrf_token(); build_headers() now takes include_csrf flag; POST/PUT/DELETE pass true; danger_accept_invalid_certs(true) on the reqwest client |
src-tauri/src/commands/proxmox.rs |
add_proxmox_cluster authenticates before inserting into pool; fixed double-unwrap in 10 commands; added connect_proxmox_cluster and disconnect_proxmox_cluster Tauri commands |
src-tauri/src/lib.rs |
Registered connect_proxmox_cluster and disconnect_proxmox_cluster |
src-tauri/src/cli/mod.rs |
let mut client — authenticate is now &mut self |
src/lib/proxmoxClient.ts |
Added connectProxmoxCluster and disconnectProxmoxCluster wrappers |
src/pages/Proxmox/RemotesPage.tsx |
Added handleConnectRemote / handleDisconnectRemote handlers calling real backend commands; wired them to RemotesList onConnect / onDisconnect props |
src-tauri/src/proxmox/vm.rs |
list_vms: changed from client.post("cluster/resources", body) to client.get("cluster/resources?type=vm"); removed double-unwrap; cpu uses unwrap_or(0.0). get_vm: removed double-unwrap. list_snapshots: removed double-unwrap. |
src-tauri/src/proxmox/{acme,apt,auth_realm,backup,ceph,ceph_cluster,certificates,firewall,ha,metrics,migration,sdn,shell,tasks,updates,updates_ext,views}.rs |
Removed .get("data") double-unwrap from all 19 functions across 17 files. |
New tests added:
client.rs— envelope deserialization, no-CSRF path,build_headersGET omits / POST includes CSRF token,set_ticket/set_csrf_tokencommands/proxmox.rs— already-unwrapped array/object/notes response handling,connect_proxmox_clusternot-found error message
Testing Needed
- Add a new Proxmox host with correct credentials → verify "connected" status
- Add a host with wrong password → verify immediate auth error, host not saved
- Restart the app → verify all previously-connected hosts show "disconnected"
- Click Connect on a disconnected host → verify it re-authenticates and shows "connected"
- Click Disconnect → verify status changes to "disconnected" and subsequent API calls fail
- Verify a VM start/stop operation succeeds (exercises CSRF token flow)
- Verify
get_node_status,list_acls,get_syslogreturn real data (exercises double-unwrap fix) - Verify a Proxmox host with a self-signed certificate connects successfully