diff --git a/AppImage/components/proxmox-dashboard.tsx b/AppImage/components/proxmox-dashboard.tsx index 19f3c0dc..823fd9e9 100644 --- a/AppImage/components/proxmox-dashboard.tsx +++ b/AppImage/components/proxmox-dashboard.tsx @@ -80,6 +80,7 @@ export function ProxmoxDashboard() { const [mobileMenuOpen, setMobileMenuOpen] = useState(false) const [activeTab, setActiveTab] = useState("overview") const [infoCount, setInfoCount] = useState(0) + const [updateAvailable, setUpdateAvailable] = useState(false) const [showNavigation, setShowNavigation] = useState(true) const [lastScrollY, setLastScrollY] = useState(0) const [showHealthModal, setShowHealthModal] = useState(false) @@ -99,6 +100,19 @@ export function ProxmoxDashboard() { { key: "security", category: "security" }, ] + // Fetch ProxMenux update status + const fetchUpdateStatus = useCallback(async () => { + try { + const response = await fetchApi("/api/proxmenux/update-status") + if (response?.success && response?.update_available) { + const { stable, beta } = response.update_available + setUpdateAvailable(stable || beta) + } + } catch (error) { + // Silently fail - updateAvailable will remain false + } + }, []) + // Fetch health info count independently (for initial load and refresh) const fetchHealthInfoCount = useCallback(async () => { try { @@ -178,9 +192,10 @@ export function ProxmoxDashboard() { }, []) useEffect(() => { - // Siempre fetch inicial - fetchSystemData() - fetchHealthInfoCount() // Fetch info count on initial load + // Siempre fetch inicial + fetchSystemData() + fetchHealthInfoCount() // Fetch info count on initial load + fetchUpdateStatus() // Fetch ProxMenux update status on initial load // En overview: cada 30 segundos para actualización frecuente del estado de salud // En otras tabs: cada 60 segundos para reducir carga @@ -198,7 +213,7 @@ export function ProxmoxDashboard() { if (interval) clearInterval(interval) if (healthInterval) clearInterval(healthInterval) } - }, [fetchSystemData, fetchHealthInfoCount, activeTab]) + }, [fetchSystemData, fetchHealthInfoCount, fetchUpdateStatus, activeTab]) useEffect(() => { const handleChangeTab = (event: CustomEvent) => { @@ -376,14 +391,13 @@ export function ProxmoxDashboard() {
ProxMenux Logo { - console.log("[v0] Logo failed to load, using fallback icon") const target = e.target as HTMLImageElement target.style.display = "none" const fallback = target.parentElement?.querySelector(".fallback-icon") diff --git a/AppImage/public/images/proxmenux_update-logo.png b/AppImage/public/images/proxmenux_update-logo.png new file mode 100644 index 00000000..6ca7f9d2 Binary files /dev/null and b/AppImage/public/images/proxmenux_update-logo.png differ diff --git a/AppImage/scripts/flask_proxmenux_routes.py b/AppImage/scripts/flask_proxmenux_routes.py index 6e48e3fe..d8725b43 100644 --- a/AppImage/scripts/flask_proxmenux_routes.py +++ b/AppImage/scripts/flask_proxmenux_routes.py @@ -28,6 +28,45 @@ TOOL_DESCRIPTIONS = { 'persistent_network': 'Setting persistent network interfaces' } +@proxmenux_bp.route('/api/proxmenux/update-status', methods=['GET']) +def get_update_status(): + """Get ProxMenux update availability status from config.json""" + config_path = '/usr/local/share/proxmenux/config.json' + + try: + if not os.path.exists(config_path): + return jsonify({ + 'success': True, + 'update_available': { + 'stable': False, + 'stable_version': '', + 'beta': False, + 'beta_version': '' + } + }) + + with open(config_path, 'r') as f: + config = json.load(f) + + update_status = config.get('update_available', { + 'stable': False, + 'stable_version': '', + 'beta': False, + 'beta_version': '' + }) + + return jsonify({ + 'success': True, + 'update_available': update_status + }) + + except Exception as e: + return jsonify({ + 'success': False, + 'error': str(e) + }), 500 + + @proxmenux_bp.route('/api/proxmenux/installed-tools', methods=['GET']) def get_installed_tools(): """Get list of installed ProxMenux tools/optimizations""" diff --git a/AppImage/scripts/notification_events.py b/AppImage/scripts/notification_events.py index affec3d0..310032a9 100644 --- a/AppImage/scripts/notification_events.py +++ b/AppImage/scripts/notification_events.py @@ -2231,12 +2231,42 @@ class PollingCollector: except Exception: return (0,) + def update_config_json(stable: bool = None, stable_version: str = None, + beta: bool = None, beta_version: str = None): + """Update update_available status in config.json.""" + config_path = Path('/usr/local/share/proxmenux/config.json') + try: + config = {} + if config_path.exists(): + with open(config_path, 'r') as f: + config = json.load(f) + + if 'update_available' not in config: + config['update_available'] = { + 'stable': False, 'stable_version': '', + 'beta': False, 'beta_version': '' + } + + if stable is not None: + config['update_available']['stable'] = stable + config['update_available']['stable_version'] = stable_version or '' + if beta is not None: + config['update_available']['beta'] = beta + config['update_available']['beta_version'] = beta_version or '' + + with open(config_path, 'w') as f: + json.dump(config, f, indent=2) + except Exception as e: + print(f"[PollingCollector] Failed to update config.json: {e}") + try: # Check main version local_main = read_local_version(self.PROXMENUX_VERSION_FILE) if local_main: remote_main = read_remote_version(self.REPO_MAIN_VERSION_URL) if remote_main and version_tuple(remote_main) > version_tuple(local_main): + # Update config.json with stable update status + update_config_json(stable=True, stable_version=remote_main) # Only notify if we haven't already notified for this version if self._notified_proxmenux_version != remote_main: self._notified_proxmenux_version = remote_main @@ -2255,6 +2285,8 @@ class PollingCollector: if local_beta: remote_beta = read_remote_version(self.REPO_DEVELOP_VERSION_URL) if remote_beta and version_tuple(remote_beta) > version_tuple(local_beta): + # Update config.json with beta update status + update_config_json(beta=True, beta_version=remote_beta) # Only notify if we haven't already notified for this version if self._notified_proxmenux_beta_version != remote_beta: self._notified_proxmenux_beta_version = remote_beta