From 0968468978d6f4cf05c7d91c81d683d77c739013 Mon Sep 17 00:00:00 2001 From: ARIA Date: Mon, 6 Jul 2026 20:02:57 +0200 Subject: [PATCH] Dedup VMs across real Proxmox cluster nodes (was listing each VM once per entered host) --- package.json | 2 +- src/main.js | 25 ++++++++++++++++++++++++- src/proxmox.js | 10 ++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 18bf6ef..f575895 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "proxmox-spice-client", - "version": "1.0.4", + "version": "1.0.5", "description": "VDI SPICE Client f\u00fcr Proxmox", "main": "src/main.js", "scripts": { diff --git a/src/main.js b/src/main.js index 665a52b..c4168e5 100644 --- a/src/main.js +++ b/src/main.js @@ -153,7 +153,30 @@ ipcMain.handle('proxmox:login', async (_e, { host, username, password }) => { return { success: false, error: attempts[0].error }; } - clients = succeeded; + // Several hostnames can point at nodes of the SAME real Proxmox cluster + // (e.g. Stefan's setup) — in that case /cluster/resources returns the + // identical cluster-wide VM list from every one of them, so keeping all + // logged-in clients would just show each VM 3x. Detect real cluster + // membership and keep only one representative client per cluster. + const seenGroups = new Set(); + const dedupedClients = []; + await Promise.all( + succeeded.map(async (entry) => { + try { + entry.clusterId = await entry.client.getClusterId(); + } catch { + entry.clusterId = null; + } + }) + ); + for (const entry of succeeded) { + const group = entry.clusterId || `standalone:${entry.host}`; + if (seenGroups.has(group)) continue; + seenGroups.add(group); + dedupedClients.push(entry); + } + + clients = dedupedClients; createWindow('vms.html', 680, 520, true); if (failed.length > 0) { diff --git a/src/proxmox.js b/src/proxmox.js index f547707..c1a8bd9 100644 --- a/src/proxmox.js +++ b/src/proxmox.js @@ -121,6 +121,16 @@ class ProxmoxClient { return vga.startsWith('qxl') || vga === 'virtio-vga-gl'; } + // Real Proxmox clusters: every member node returns the identical + // cluster-wide resource list, so logging into several nodes of the same + // cluster would just duplicate every VM. Returns the cluster's name, or + // null if this host isn't part of a cluster (standalone). + async getClusterId() { + const status = await this.request('GET', '/cluster/status'); + const cluster = (status || []).find((s) => s.type === 'cluster'); + return cluster ? cluster.name || cluster.id || null : null; + } + async getSpiceTicket(node, vmid) { // proxy = address clients should connect to for SPICE traffic const proxy = this.host.split(':')[0];