mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-11-01 18:32:13 +00:00
fix: read completed update results without a live terminal
This commit is contained in:
@@ -307,6 +307,7 @@ def get_full_health():
|
||||
bg_key = '_bg_detailed'
|
||||
bg_last = health_monitor.last_check_times.get(bg_key, 0)
|
||||
bg_age = _time.time() - bg_last if type(bg_last) in (int, float) else float('inf')
|
||||
publish_live = False
|
||||
|
||||
if 0 <= bg_age < 360 and bg_key in health_monitor.cached_results:
|
||||
# Use cached result (at most ~5 min old)
|
||||
@@ -318,12 +319,29 @@ def get_full_health():
|
||||
else:
|
||||
# No fresh cache, run live (first load or cache expired)
|
||||
details = health_monitor.get_detailed_status()
|
||||
if (not isinstance(details, dict) or not details.get('timestamp')
|
||||
or details.get('overall') not in ('OK', 'INFO', 'WARNING', 'CRITICAL', 'UNKNOWN')
|
||||
or not isinstance(details.get('summary'), str)
|
||||
or not isinstance(details.get('details'), dict)
|
||||
or not isinstance(details['details'].get('updates'), dict)):
|
||||
raise ValueError('Health snapshot is incomplete')
|
||||
# Publish an explicit check too: its caller may have invalidated
|
||||
# the background cache after an update. A following read-only poll
|
||||
# must see this result instead of waiting for another collector.
|
||||
publish_live = True
|
||||
|
||||
# Installed/running kernel state changes outside Monitor jobs too.
|
||||
# Overlay fresh read-only evidence without refreshing other checks or
|
||||
# rewriting the background collector's snapshot/timestamp.
|
||||
from kernel_reboot import refresh_health_reboot
|
||||
details = refresh_health_reboot(details)
|
||||
if publish_live:
|
||||
checked_at = _time.time()
|
||||
health_monitor.cached_results[bg_key] = details
|
||||
health_monitor.cached_results['_bg_overall'] = {
|
||||
'status': details['overall'], 'summary': details['summary']}
|
||||
health_monitor.last_check_times[bg_key] = checked_at
|
||||
health_monitor.last_check_times['_bg_overall'] = checked_at
|
||||
|
||||
active_errors = health_persistence.get_active_errors()
|
||||
dismissed = health_persistence.get_dismissed_errors()
|
||||
|
||||
@@ -1752,6 +1752,7 @@ def get_uptime():
|
||||
_system_info_cache = {
|
||||
'proxmox_version': None,
|
||||
'proxmox_version_time': 0,
|
||||
'proxmox_version_stamp': 0.0,
|
||||
'available_updates': 0,
|
||||
'available_updates_time': 0,
|
||||
'available_updates_stamp': 0.0,
|
||||
@@ -3507,14 +3508,16 @@ def get_coral_info():
|
||||
|
||||
|
||||
def get_proxmox_version():
|
||||
"""Get Proxmox version if available. Cached for 6 hours."""
|
||||
"""Cache the installed PVE version until package state changes."""
|
||||
global _system_info_cache
|
||||
if is_pbs_host():
|
||||
return get_platform().get('version')
|
||||
|
||||
now = time.time()
|
||||
if _system_info_cache['proxmox_version'] is not None and \
|
||||
now - _system_info_cache['proxmox_version_time'] < _SYSTEM_INFO_CACHE_TTL:
|
||||
stamp = _apt_state_stamp()
|
||||
age = now - _system_info_cache['proxmox_version_time']
|
||||
if _system_info_cache['proxmox_version'] is not None and (0 <= age < _AVAILABLE_UPDATES_MIN_INTERVAL or
|
||||
(stamp == _system_info_cache.get('proxmox_version_stamp') and 0 <= age < _SYSTEM_INFO_CACHE_TTL)):
|
||||
return _system_info_cache['proxmox_version']
|
||||
|
||||
proxmox_version = None
|
||||
@@ -3530,8 +3533,11 @@ def get_proxmox_version():
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
if proxmox_version is None:
|
||||
return None # Keep last-known cache intact, but do not present it as fresh.
|
||||
_system_info_cache['proxmox_version'] = proxmox_version
|
||||
_system_info_cache['proxmox_version_time'] = now
|
||||
_system_info_cache['proxmox_version_stamp'] = stamp
|
||||
return proxmox_version
|
||||
|
||||
def _apt_state_stamp():
|
||||
|
||||
@@ -215,6 +215,22 @@ def host_update_jobs_status():
|
||||
return jsonify({'jobs': list_jobs()})
|
||||
|
||||
|
||||
@terminal_bp.route('/api/host-update-jobs/<run_id>/result', methods=['GET'])
|
||||
@require_admin_scope
|
||||
def host_update_result(run_id):
|
||||
"""Read a completed owner's retained output; never create or resume a job."""
|
||||
from host_update_jobs import get
|
||||
try:
|
||||
view = get(run_id).read(0)
|
||||
except (ValueError, KeyError):
|
||||
return jsonify({'error': 'Host update result is unavailable'}), 404
|
||||
if view['state'] != 'complete' or type(view['exit_code']) is not int:
|
||||
return jsonify({'error': 'Host update has not completed'}), 409
|
||||
response = jsonify({key: view[key] for key in ('state', 'exit_code', 'text', 'truncated')})
|
||||
response.headers['Cache-Control'] = 'no-store'
|
||||
return response
|
||||
|
||||
|
||||
@sock.route('/ws/host-update/<run_id>')
|
||||
def host_update_viewer(ws, run_id):
|
||||
"""New fixed-script contract; existing script/LXC routes stay intact."""
|
||||
|
||||
Reference in New Issue
Block a user