mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-05-31 12:34:48 +00:00
Update AppImage 1.2.1.3
This commit is contained in:
Binary file not shown.
@@ -1 +1 @@
|
|||||||
d825487696ecdf071bf9aaed58f4bcc3e5b2e44e51770b746a85a359d1d71794
|
1caca89b574241c9d754b9ac3bb11987c5eccc5f182d01a5c62e61623b62fda7
|
||||||
|
|||||||
@@ -271,7 +271,7 @@ export function Login({ onLogin }: LoginProps) {
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="text-center text-sm text-muted-foreground">ProxMenux Monitor v1.2.1.2-beta</p>
|
<p className="text-center text-sm text-muted-foreground">ProxMenux Monitor v1.2.1.3-beta</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -814,7 +814,7 @@ export function ProxmoxDashboard() {
|
|||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
<footer className="mt-8 md:mt-12 pt-4 md:pt-6 border-t border-border text-center text-xs md:text-sm text-muted-foreground">
|
<footer className="mt-8 md:mt-12 pt-4 md:pt-6 border-t border-border text-center text-xs md:text-sm text-muted-foreground">
|
||||||
<p className="font-medium mb-2">ProxMenux Monitor v1.2.1.2-beta</p>
|
<p className="font-medium mb-2">ProxMenux Monitor v1.2.1.3-beta</p>
|
||||||
<p>
|
<p>
|
||||||
<a
|
<a
|
||||||
href="https://ko-fi.com/macrimi"
|
href="https://ko-fi.com/macrimi"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Dialog, DialogContent, DialogTitle } from "./ui/dialog"
|
|||||||
import { X, Sparkles, Thermometer, Activity, HardDrive, Shield, Globe, Cpu, Zap, Sliders, Wrench, RefreshCw, Server } from "lucide-react"
|
import { X, Sparkles, Thermometer, Activity, HardDrive, Shield, Globe, Cpu, Zap, Sliders, Wrench, RefreshCw, Server } from "lucide-react"
|
||||||
import { Checkbox } from "./ui/checkbox"
|
import { Checkbox } from "./ui/checkbox"
|
||||||
|
|
||||||
const APP_VERSION = "1.2.1.2-beta" // Sync with AppImage/package.json
|
const APP_VERSION = "1.2.1.3-beta" // Sync with AppImage/package.json
|
||||||
|
|
||||||
interface ReleaseNote {
|
interface ReleaseNote {
|
||||||
date: string
|
date: string
|
||||||
@@ -18,6 +18,22 @@ interface ReleaseNote {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const CHANGELOG: Record<string, ReleaseNote> = {
|
export const CHANGELOG: Record<string, ReleaseNote> = {
|
||||||
|
"1.2.1.3-beta": {
|
||||||
|
date: "May 22, 2026",
|
||||||
|
changes: {
|
||||||
|
added: [
|
||||||
|
"LXC Update Detection - A new dedicated section in Settings (between Health Monitor Thresholds and Notifications) with a single toggle that gates the per-CT apt list --upgradable / apk list -u scan end-to-end. Default ON. When OFF the scan stops entirely (no pct exec calls), every type=lxc entry is purged from the managed-installs registry immediately, and the matching notification toggle in Notifications -> Services disappears from the UI while preserving its stored preference",
|
||||||
|
"LXC update checker auto-refresh - The checker now reads the mtime of the CT's package-manager metadata cache and runs apt-get update / apk update from outside via pct exec if it is older than 24h, with a 60s timeout and silent failure. Long-running appliance CTs whose caches were months stale now surface their real upstream backlog (a Debian 12 CT with a 524-day-old cache went from \"0 updates\" to \"117 (12 security)\" on lab hardware)",
|
||||||
|
],
|
||||||
|
changed: [
|
||||||
|
"AI Enhancement section in Notifications - Rewritten from a muted uppercase row that testers consistently scrolled past, to a normal-case foreground label with a leading Sparkles icon and a persistent badge (green Active when AI is enabled, neutral Optional when it isn't) so the feature is visible regardless of state",
|
||||||
|
],
|
||||||
|
fixed: [
|
||||||
|
"Terminal modals on HTTPS hosts - Every terminal modal (dashboard terminal, LXC terminal, script terminal) used to fail with WebSocket connection error on hosts with HTTPS enabled. Root cause: the gevent+SSL path stacked geventwebsocket's WebSocketHandler on top of flask-sock's protocol implementation, so the server emitted two consecutive HTTP/1.1 101 Switching Protocols headers and the browser closed the connection as a corrupt frame. Dropping handler_class=WebSocketHandler restores a single 101 response and lets the handshake complete normally",
|
||||||
|
"Health Monitor kernel updates on PVE 9.x (#208) - The System Updates -> Kernel/PVE row reported \"Kernel/PVE up to date\" on PVE 9.x hosts even when an update for the running kernel was waiting upstream. Three combined fixes: (a) the kernel-package prefix list now includes proxmox-kernel-* and proxmox-firmware-* (PVE 9.x ships kernels under proxmox-kernel-, not pve-kernel- as in 7.x/8.x), (b) the dry-run switched from apt-get upgrade --dry-run to apt-get dist-upgrade --dry-run so kernel updates packaged as new installs are visible at all, (c) the categoriser now reads uname -r and flags an update as a running-kernel update when the package matches the running release exactly or its branch meta-package (e.g. proxmox-kernel-6.14 for a host on 6.14.11-4-pve). The row text now distinguishes \"Running kernel update available (reboot required)\" from \"N kernel update(s) available (none for running kernel)\"",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
"1.2.1.2-beta": {
|
"1.2.1.2-beta": {
|
||||||
date: "May 20, 2026",
|
date: "May 20, 2026",
|
||||||
changes: {
|
changes: {
|
||||||
|
|||||||
@@ -3624,7 +3624,7 @@ ${observationsHtml}
|
|||||||
<!-- Footer -->
|
<!-- Footer -->
|
||||||
<div class="rpt-footer">
|
<div class="rpt-footer">
|
||||||
<div>Report generated by ProxMenux Monitor</div>
|
<div>Report generated by ProxMenux Monitor</div>
|
||||||
<div>ProxMenux Monitor v1.2.1.2-beta</div>
|
<div>ProxMenux Monitor v1.2.1.3-beta</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -10435,7 +10435,7 @@ def api_health():
|
|||||||
return jsonify({
|
return jsonify({
|
||||||
'status': 'healthy',
|
'status': 'healthy',
|
||||||
'timestamp': datetime.now().isoformat(),
|
'timestamp': datetime.now().isoformat(),
|
||||||
'version': '1.2.1.2-beta'
|
'version': '1.2.1.3-beta'
|
||||||
})
|
})
|
||||||
|
|
||||||
# ─── User-configurable health thresholds ─────────────────────────────────────
|
# ─── User-configurable health thresholds ─────────────────────────────────────
|
||||||
@@ -10872,7 +10872,7 @@ def api_info():
|
|||||||
"""Root endpoint with API information"""
|
"""Root endpoint with API information"""
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'name': 'ProxMenux Monitor API',
|
'name': 'ProxMenux Monitor API',
|
||||||
'version': '1.2.1.2-beta',
|
'version': '1.2.1.3-beta',
|
||||||
'endpoints': [
|
'endpoints': [
|
||||||
'/api/system',
|
'/api/system',
|
||||||
'/api/system-info',
|
'/api/system-info',
|
||||||
@@ -11522,7 +11522,7 @@ if __name__ == '__main__':
|
|||||||
try:
|
try:
|
||||||
import sqlite3
|
import sqlite3
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
MONITOR_VERSION = '1.2.1.2-beta'
|
MONITOR_VERSION = '1.2.1.3-beta'
|
||||||
db_path = Path('/usr/local/share/proxmenux/health_monitor.db')
|
db_path = Path('/usr/local/share/proxmenux/health_monitor.db')
|
||||||
if db_path.exists():
|
if db_path.exists():
|
||||||
conn = sqlite3.connect(str(db_path), timeout=10)
|
conn = sqlite3.connect(str(db_path), timeout=10)
|
||||||
|
|||||||
@@ -4177,7 +4177,21 @@ class HealthMonitor:
|
|||||||
'pve-', 'proxmox-', 'qemu-server', 'lxc-pve', 'ceph',
|
'pve-', 'proxmox-', 'qemu-server', 'lxc-pve', 'ceph',
|
||||||
'corosync', 'libpve', 'pbs-', 'pmg-',
|
'corosync', 'libpve', 'pbs-', 'pmg-',
|
||||||
)
|
)
|
||||||
_KERNEL_PREFIXES = ('linux-image', 'pve-kernel', 'pve-firmware')
|
# Kernel-package prefixes for categorisation. Order matters: the matcher
|
||||||
|
# in update parsing checks KERNEL first and PVE second, so `proxmox-kernel`
|
||||||
|
# must appear here before the generic `proxmox-` falls through to
|
||||||
|
# _PVE_PREFIXES. Bug #208 — PVE 9.x ships kernels as `proxmox-kernel-*`
|
||||||
|
# (Debian 13 base), not `pve-kernel-*` as in older Proxmox; without the
|
||||||
|
# `proxmox-kernel` prefix every kernel update was mis-categorised as a
|
||||||
|
# generic PVE update and the dashboard reported "Kernel/PVE up to date"
|
||||||
|
# even with a kernel patch waiting for the running CT.
|
||||||
|
_KERNEL_PREFIXES = (
|
||||||
|
'linux-image',
|
||||||
|
'pve-kernel',
|
||||||
|
'proxmox-kernel',
|
||||||
|
'pve-firmware',
|
||||||
|
'proxmox-firmware',
|
||||||
|
)
|
||||||
_IMPORTANT_PKGS = {
|
_IMPORTANT_PKGS = {
|
||||||
'pve-manager', 'proxmox-ve', 'qemu-server', 'pve-container',
|
'pve-manager', 'proxmox-ve', 'qemu-server', 'pve-container',
|
||||||
'pve-ha-manager', 'pve-firewall', 'ceph-common',
|
'pve-ha-manager', 'pve-firewall', 'ceph-common',
|
||||||
@@ -4232,10 +4246,18 @@ class HealthMonitor:
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Perform a dry run of apt-get upgrade to see pending packages
|
# Perform a dry run of apt-get dist-upgrade to see pending
|
||||||
|
# packages. `dist-upgrade` (a.k.a. `full-upgrade`) is what the
|
||||||
|
# Proxmox VE web UI's Updates tab runs internally, and unlike
|
||||||
|
# plain `apt-get upgrade` it surfaces kernel updates packaged
|
||||||
|
# as NEW installs (e.g. `proxmox-kernel-6.14.11-9-pve-signed`
|
||||||
|
# showing up as `Inst` when the running CT is on -4). Bug #208
|
||||||
|
# — without dist-upgrade these updates never appeared in the
|
||||||
|
# `Inst` list at all, so the Kernel/PVE sub-check stayed
|
||||||
|
# green even when the running kernel had a patch waiting.
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
['apt-get', 'upgrade', '--dry-run'],
|
['apt-get', 'dist-upgrade', '--dry-run'],
|
||||||
capture_output=True, text=True, timeout=30
|
capture_output=True, text=True, timeout=30
|
||||||
)
|
)
|
||||||
except subprocess.TimeoutExpired:
|
except subprocess.TimeoutExpired:
|
||||||
@@ -4252,12 +4274,36 @@ class HealthMonitor:
|
|||||||
security_pkgs: list = []
|
security_pkgs: list = []
|
||||||
kernel_pkgs: list = []
|
kernel_pkgs: list = []
|
||||||
pve_pkgs: list = []
|
pve_pkgs: list = []
|
||||||
|
running_kernel_pkgs: list = [] # {name, cur, new} — kernel(s) matching `uname -r`
|
||||||
important_pkgs: list = [] # {name, cur, new}
|
important_pkgs: list = [] # {name, cur, new}
|
||||||
pve_manager_info = None # {cur, new} or None
|
pve_manager_info = None # {cur, new} or None
|
||||||
sec_result = None
|
sec_result = None
|
||||||
sec_severity = 'INFO'
|
sec_severity = 'INFO'
|
||||||
sec_days_unpatched = 0
|
sec_days_unpatched = 0
|
||||||
|
|
||||||
|
# Read the running kernel release (e.g. `7.0.2-5-pve`) so we can
|
||||||
|
# distinguish a kernel update that REPLACES the kernel the host
|
||||||
|
# is currently booted from (urgent, requires reboot) from one
|
||||||
|
# that only touches an older kernel still present in the
|
||||||
|
# bootloader or the meta-package pointers. Bug #208.
|
||||||
|
#
|
||||||
|
# `running_kernel_branch` extracts the major.minor branch from
|
||||||
|
# the running release — e.g. `7.0.2-5-pve` → `7.0`. We use it
|
||||||
|
# to detect updates to the branch META package
|
||||||
|
# (`proxmox-kernel-7.0` / `pve-kernel-7.0`), which when
|
||||||
|
# upgraded pull in the latest kernel of that branch and so
|
||||||
|
# also replace the running kernel on the next boot.
|
||||||
|
running_kernel = ''
|
||||||
|
running_kernel_branch = ''
|
||||||
|
try:
|
||||||
|
running_kernel = os.uname().release
|
||||||
|
rk_low = running_kernel.lower()
|
||||||
|
m = re.match(r'^(\d+\.\d+)', rk_low)
|
||||||
|
if m:
|
||||||
|
running_kernel_branch = m.group(1)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
for line in result.stdout.strip().split('\n'):
|
for line in result.stdout.strip().split('\n'):
|
||||||
if not line.startswith('Inst '):
|
if not line.startswith('Inst '):
|
||||||
@@ -4288,6 +4334,44 @@ class HealthMonitor:
|
|||||||
|
|
||||||
if any(name_lower.startswith(p) for p in self._KERNEL_PREFIXES):
|
if any(name_lower.startswith(p) for p in self._KERNEL_PREFIXES):
|
||||||
kernel_pkgs.append(pkg_name)
|
kernel_pkgs.append(pkg_name)
|
||||||
|
# Does this package match the currently running
|
||||||
|
# kernel? Proxmox kernels are named
|
||||||
|
# `proxmox-kernel-<release>` and `pve-kernel-<release>`,
|
||||||
|
# plus their `-signed` variant. Compare against
|
||||||
|
# `uname -r` to flag updates the operator should
|
||||||
|
# reboot for. Bug #208.
|
||||||
|
if running_kernel:
|
||||||
|
stripped = name_lower
|
||||||
|
if stripped.endswith('-signed'):
|
||||||
|
stripped = stripped[:-len('-signed')]
|
||||||
|
for k_prefix in ('proxmox-kernel-', 'pve-kernel-', 'linux-image-'):
|
||||||
|
if stripped.startswith(k_prefix):
|
||||||
|
suffix = stripped[len(k_prefix):]
|
||||||
|
# Two cases both count as "this update
|
||||||
|
# will replace the running kernel":
|
||||||
|
# a) exact match — the in-place
|
||||||
|
# package of the running release
|
||||||
|
# (e.g. `proxmox-kernel-7.0.2-5-pve`
|
||||||
|
# upgrading from 7.0.2-5 to -6).
|
||||||
|
# b) branch meta — the meta package
|
||||||
|
# pointing at the latest of the
|
||||||
|
# running branch (e.g. running
|
||||||
|
# `6.14.11-4-pve`, upgradable
|
||||||
|
# `proxmox-kernel-6.14` pulls a
|
||||||
|
# newer 6.14.x on next boot).
|
||||||
|
is_exact = (suffix == running_kernel.lower())
|
||||||
|
is_branch_meta = (
|
||||||
|
running_kernel_branch
|
||||||
|
and suffix == running_kernel_branch
|
||||||
|
)
|
||||||
|
if is_exact or is_branch_meta:
|
||||||
|
running_kernel_pkgs.append({
|
||||||
|
'name': pkg_name,
|
||||||
|
'cur': cur_ver,
|
||||||
|
'new': new_ver,
|
||||||
|
'match': 'exact' if is_exact else 'branch',
|
||||||
|
})
|
||||||
|
break
|
||||||
elif any(name_lower.startswith(p) for p in self._PVE_PREFIXES):
|
elif any(name_lower.startswith(p) for p in self._PVE_PREFIXES):
|
||||||
pve_pkgs.append(pkg_name)
|
pve_pkgs.append(pkg_name)
|
||||||
|
|
||||||
@@ -4354,6 +4438,15 @@ class HealthMonitor:
|
|||||||
if age_result and age_result.get('type') == 'skipped_acknowledged':
|
if age_result and age_result.get('type') == 'skipped_acknowledged':
|
||||||
status = 'INFO'
|
status = 'INFO'
|
||||||
reason = None
|
reason = None
|
||||||
|
elif running_kernel_pkgs:
|
||||||
|
# Running kernel has an update available. Bug #208.
|
||||||
|
# Reported as INFO — the Updates section never
|
||||||
|
# escalates to WARNING for pending updates; WARNING /
|
||||||
|
# CRITICAL on this section are reserved for time-based
|
||||||
|
# gates (system_age >= 365d / 548d, security_updates
|
||||||
|
# unpatched >= SECURITY_WARN_DAYS).
|
||||||
|
status = 'INFO'
|
||||||
|
reason = f'Kernel update available for running {running_kernel}'
|
||||||
elif kernel_pkgs or pve_pkgs:
|
elif kernel_pkgs or pve_pkgs:
|
||||||
status = 'INFO'
|
status = 'INFO'
|
||||||
reason = f'{len(kernel_pkgs)} kernel + {len(pve_pkgs)} Proxmox update(s) available'
|
reason = f'{len(kernel_pkgs)} kernel + {len(pve_pkgs)} Proxmox update(s) available'
|
||||||
@@ -4382,11 +4475,57 @@ class HealthMonitor:
|
|||||||
if security_pkgs and sec_days_unpatched >= self.SECURITY_WARN_DAYS:
|
if security_pkgs and sec_days_unpatched >= self.SECURITY_WARN_DAYS:
|
||||||
sec_detail += f' ({sec_days_unpatched} days unpatched)'
|
sec_detail += f' ({sec_days_unpatched} days unpatched)'
|
||||||
|
|
||||||
|
# Kernel/PVE sub-check. Bug #208: distinguish three cases.
|
||||||
|
# All non-OK results stay at INFO — the Updates section never
|
||||||
|
# raises WARNING for pending updates; WARNING / CRITICAL on
|
||||||
|
# this section is reserved for time-based gates (system_age,
|
||||||
|
# security_updates unpatched too long).
|
||||||
|
#
|
||||||
|
# 1) Update for the running kernel → INFO with explicit
|
||||||
|
# "Running kernel update available, reboot required to
|
||||||
|
# apply" wording. Bug #208 — without this, the row read
|
||||||
|
# "Kernel/PVE up to date" even when the running kernel
|
||||||
|
# had a patch pending.
|
||||||
|
# 2) Kernel updates exist but only for non-running kernels
|
||||||
|
# (older kernels still present in the bootloader, meta-
|
||||||
|
# packages like `proxmox-kernel-7.0` pointing to the
|
||||||
|
# latest 7.0.x, signed variants of already-installed
|
||||||
|
# kernels) → INFO with explicit wording.
|
||||||
|
# 3) No kernel updates → OK.
|
||||||
|
if running_kernel_pkgs:
|
||||||
|
rk_count = len(running_kernel_pkgs)
|
||||||
|
# Keep the row text tight — on mobile the System Updates
|
||||||
|
# modal renders this string in a single line and longer
|
||||||
|
# wording wrapped awkwardly. Just `<pkg> <cur> -> <new>`
|
||||||
|
# is enough to convey "your running kernel has an update";
|
||||||
|
# the row's INFO icon already signals that no action is
|
||||||
|
# forced.
|
||||||
|
first = running_kernel_pkgs[0]
|
||||||
|
if first.get('cur') and first.get('new'):
|
||||||
|
rk_detail = f"{first['name']} {first['cur']} -> {first['new']}"
|
||||||
|
else:
|
||||||
|
rk_detail = first['name']
|
||||||
|
if rk_count > 1:
|
||||||
|
rk_detail += f" (+{rk_count - 1} more)"
|
||||||
|
kernel_status = 'INFO'
|
||||||
|
kernel_detail = rk_detail
|
||||||
|
elif kernel_pkgs:
|
||||||
|
kernel_status = 'INFO'
|
||||||
|
kernel_detail = (
|
||||||
|
f"{len(kernel_pkgs)} kernel update(s) available "
|
||||||
|
f"(none for running kernel"
|
||||||
|
+ (f" {running_kernel}" if running_kernel else "")
|
||||||
|
+ ")"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
kernel_status = 'OK'
|
||||||
|
kernel_detail = 'Kernel/PVE up to date'
|
||||||
|
|
||||||
checks = {
|
checks = {
|
||||||
'kernel_pve': {
|
'kernel_pve': {
|
||||||
'status': 'INFO' if kernel_pkgs else 'OK',
|
'status': kernel_status,
|
||||||
'detail': f'{len(kernel_pkgs)} kernel/PVE update(s)' if kernel_pkgs else 'Kernel/PVE up to date',
|
'detail': kernel_detail,
|
||||||
'error_key': 'kernel_pve'
|
'error_key': 'kernel_pve',
|
||||||
},
|
},
|
||||||
'pending_updates': {
|
'pending_updates': {
|
||||||
'status': 'INFO' if update_count > 0 else 'OK',
|
'status': 'INFO' if update_count > 0 else 'OK',
|
||||||
@@ -4437,6 +4576,9 @@ class HealthMonitor:
|
|||||||
update_result['security_count'] = len(security_pkgs)
|
update_result['security_count'] = len(security_pkgs)
|
||||||
update_result['pve_count'] = len(pve_pkgs)
|
update_result['pve_count'] = len(pve_pkgs)
|
||||||
update_result['kernel_count'] = len(kernel_pkgs)
|
update_result['kernel_count'] = len(kernel_pkgs)
|
||||||
|
update_result['running_kernel'] = running_kernel
|
||||||
|
update_result['running_kernel_update_count'] = len(running_kernel_pkgs)
|
||||||
|
update_result['running_kernel_packages'] = running_kernel_pkgs[:5]
|
||||||
update_result['important_packages'] = important_pkgs[:8]
|
update_result['important_packages'] = important_pkgs[:8]
|
||||||
|
|
||||||
self.cached_results[cache_key] = update_result
|
self.cached_results[cache_key] = update_result
|
||||||
|
|||||||
Reference in New Issue
Block a user