From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [IPv6:2a0f:8001:1:32::40]) by lore.proxmox.com (Postfix) with ESMTPS id EA7351FF0AB for ; Wed, 23 Sep 2026 13:38:34 +0200 (CEST) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id 18769215AA; Wed, 23 Sep 2026 13:38:29 +0200 (CEST) From: Dominik Csapak To: pve-devel@lists.proxmox.com Subject: [PATCH manager 2/3] api: provide GUI versions via index and /cluster/resources Date: Wed, 23 Sep 2026 13:35:02 +0200 Message-ID: <20260923113757.2202858-3-d.csapak@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260923113757.2202858-1-d.csapak@proxmox.com> References: <20260923113757.2202858-1-d.csapak@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.293 Adjusted score from AWL reputation of From: address DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment (newer systems) POISEN_SPAM_PILL 0.1 Meta: its spam POISEN_SPAM_PILL_1 0.1 random spam to be learned in bayes POISEN_SPAM_PILL_3 0.1 random spam to be learned in bayes RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: JTN4B5LOAXUVGISMTJKHJNSSVQM5WECC X-Message-ID-Hash: JTN4B5LOAXUVGISMTJKHJNSSVQM5WECC X-MailFrom: d.csapak@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Index has these versions anyway, just expose them to the UI via the usual `Proxmox` object. In the /cluster/resources API call, we add that information to the node that is handling the request (which is normally the same where the index was loaded from). Cache the pwt versions locally for 60 seconds, so we don't hit the file read on every call. With this information, the UI can detect an outdated UI version still running. Signed-off-by: Dominik Csapak --- PVE/API2/Cluster.pm | 42 ++++++++++++++++++++++++++++++++++++++++++ www/index.html.tpl | 6 +++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/PVE/API2/Cluster.pm b/PVE/API2/Cluster.pm index 4e5efbfd..f66e0a6a 100644 --- a/PVE/API2/Cluster.pm +++ b/PVE/API2/Cluster.pm @@ -20,6 +20,7 @@ use PVE::RPCEnvironment; use PVE::SafeSyslog; use PVE::Storage; use PVE::Tools qw(extract_param); +use PVE::pvecfg; use PVE::API2::ACMEAccount; use PVE::API2::ACMEPlugin; @@ -259,6 +260,9 @@ my sub can_access_network { return 0; } +# widget toolkit version, re-read at most once per minute to avoid reading the file on every call +my $pwt_version_cache = {}; + __PACKAGE__->register_method({ name => 'resources', path => 'resources', @@ -503,6 +507,25 @@ __PACKAGE__->register_method({ default => 'x86_64', optional => 1, }, + 'versions' => { + description => "Optional package versions (for type 'node').", + type => 'object', + optional => 1, + properties => { + 'pve-manager' => { + type => "string", + optional => 1, + description => + "The pve-manager version of this node. Only included for the node handling the request.", + }, + 'proxmox-widget-toolkit' => { + type => "string", + optional => 1, + description => + "The proxmox-widget-toolkit version of this node. Only included for the node handling the request.", + }, + }, + }, }, }, }, @@ -618,6 +641,8 @@ __PACKAGE__->register_method({ my $static_node_info = PVE::Cluster::get_node_kv("static-info"); if (!$param->{type} || $param->{type} eq 'node') { + my $nodename = PVE::INotify::nodename(); + foreach my $node (@$nodelist) { my $can_audit = $rpcenv->check($authuser, "/nodes/$node", ['Sys.Audit'], 1); my $entry = @@ -634,6 +659,23 @@ __PACKAGE__->register_method({ $entry->{'hastate'} = $status; } + if ($node eq $nodename) { + my $now = time(); + if ( + !defined($pwt_version_cache->{version}) + || $pwt_version_cache->{time} + 60 < $now + ) { + $pwt_version_cache->{version} = PVE::API2Tools::read_pwt_version(); + $pwt_version_cache->{time} = $now; + } + my $pwt_version = $pwt_version_cache->{version}; + + $entry->{versions} = { 'pve-manager' => PVE::pvecfg::version() }; + # an empty version means reading failed, omit it to avoid false positives + $entry->{versions}->{'proxmox-widget-toolkit'} = $pwt_version + if $pwt_version ne ''; + } + push @$res, $entry; } } diff --git a/www/index.html.tpl b/www/index.html.tpl index c18e6411..8484710a 100644 --- a/www/index.html.tpl +++ b/www/index.html.tpl @@ -48,7 +48,11 @@ NodeArch: '[% arch %]', UserName: '[% username %]', CSRFPreventionToken: '[% token %]', - ConsentText: '[% consenttext %]' + ConsentText: '[% consenttext %]', + GUIVersions: { + 'pve-manager': '[% version %]', + 'proxmox-widget-toolkit': '[% wtversion %]', + }, }; -- 2.47.3