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() {
{
- 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