mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-05-01 03:46:22 +00:00
Update notification service
This commit is contained in:
@@ -80,6 +80,7 @@ export function ProxmoxDashboard() {
|
|||||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
|
||||||
const [activeTab, setActiveTab] = useState("overview")
|
const [activeTab, setActiveTab] = useState("overview")
|
||||||
const [infoCount, setInfoCount] = useState(0)
|
const [infoCount, setInfoCount] = useState(0)
|
||||||
|
const [updateAvailable, setUpdateAvailable] = useState(false)
|
||||||
const [showNavigation, setShowNavigation] = useState(true)
|
const [showNavigation, setShowNavigation] = useState(true)
|
||||||
const [lastScrollY, setLastScrollY] = useState(0)
|
const [lastScrollY, setLastScrollY] = useState(0)
|
||||||
const [showHealthModal, setShowHealthModal] = useState(false)
|
const [showHealthModal, setShowHealthModal] = useState(false)
|
||||||
@@ -99,6 +100,19 @@ export function ProxmoxDashboard() {
|
|||||||
{ key: "security", category: "security" },
|
{ 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)
|
// Fetch health info count independently (for initial load and refresh)
|
||||||
const fetchHealthInfoCount = useCallback(async () => {
|
const fetchHealthInfoCount = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
@@ -178,9 +192,10 @@ export function ProxmoxDashboard() {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Siempre fetch inicial
|
// Siempre fetch inicial
|
||||||
fetchSystemData()
|
fetchSystemData()
|
||||||
fetchHealthInfoCount() // Fetch info count on initial load
|
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 overview: cada 30 segundos para actualización frecuente del estado de salud
|
||||||
// En otras tabs: cada 60 segundos para reducir carga
|
// En otras tabs: cada 60 segundos para reducir carga
|
||||||
@@ -198,7 +213,7 @@ export function ProxmoxDashboard() {
|
|||||||
if (interval) clearInterval(interval)
|
if (interval) clearInterval(interval)
|
||||||
if (healthInterval) clearInterval(healthInterval)
|
if (healthInterval) clearInterval(healthInterval)
|
||||||
}
|
}
|
||||||
}, [fetchSystemData, fetchHealthInfoCount, activeTab])
|
}, [fetchSystemData, fetchHealthInfoCount, fetchUpdateStatus, activeTab])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleChangeTab = (event: CustomEvent) => {
|
const handleChangeTab = (event: CustomEvent) => {
|
||||||
@@ -376,14 +391,13 @@ export function ProxmoxDashboard() {
|
|||||||
<div className="flex items-center space-x-2 md:space-x-3 min-w-0">
|
<div className="flex items-center space-x-2 md:space-x-3 min-w-0">
|
||||||
<div className="w-16 h-16 md:w-10 md:h-10 relative flex items-center justify-center bg-primary/10 flex-shrink-0">
|
<div className="w-16 h-16 md:w-10 md:h-10 relative flex items-center justify-center bg-primary/10 flex-shrink-0">
|
||||||
<Image
|
<Image
|
||||||
src="/images/proxmenux-logo.png"
|
src={updateAvailable ? "/images/proxmenux_update-logo.png" : "/images/proxmenux-logo.png"}
|
||||||
alt="ProxMenux Logo"
|
alt="ProxMenux Logo"
|
||||||
width={64}
|
width={64}
|
||||||
height={64}
|
height={64}
|
||||||
className="object-contain md:w-10 md:h-10"
|
className="object-contain md:w-10 md:h-10"
|
||||||
priority
|
priority
|
||||||
onError={(e) => {
|
onError={(e) => {
|
||||||
console.log("[v0] Logo failed to load, using fallback icon")
|
|
||||||
const target = e.target as HTMLImageElement
|
const target = e.target as HTMLImageElement
|
||||||
target.style.display = "none"
|
target.style.display = "none"
|
||||||
const fallback = target.parentElement?.querySelector(".fallback-icon")
|
const fallback = target.parentElement?.querySelector(".fallback-icon")
|
||||||
|
|||||||
BIN
AppImage/public/images/proxmenux_update-logo.png
Normal file
BIN
AppImage/public/images/proxmenux_update-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
@@ -28,6 +28,45 @@ TOOL_DESCRIPTIONS = {
|
|||||||
'persistent_network': 'Setting persistent network interfaces'
|
'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'])
|
@proxmenux_bp.route('/api/proxmenux/installed-tools', methods=['GET'])
|
||||||
def get_installed_tools():
|
def get_installed_tools():
|
||||||
"""Get list of installed ProxMenux tools/optimizations"""
|
"""Get list of installed ProxMenux tools/optimizations"""
|
||||||
|
|||||||
@@ -2231,12 +2231,42 @@ class PollingCollector:
|
|||||||
except Exception:
|
except Exception:
|
||||||
return (0,)
|
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:
|
try:
|
||||||
# Check main version
|
# Check main version
|
||||||
local_main = read_local_version(self.PROXMENUX_VERSION_FILE)
|
local_main = read_local_version(self.PROXMENUX_VERSION_FILE)
|
||||||
if local_main:
|
if local_main:
|
||||||
remote_main = read_remote_version(self.REPO_MAIN_VERSION_URL)
|
remote_main = read_remote_version(self.REPO_MAIN_VERSION_URL)
|
||||||
if remote_main and version_tuple(remote_main) > version_tuple(local_main):
|
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
|
# Only notify if we haven't already notified for this version
|
||||||
if self._notified_proxmenux_version != remote_main:
|
if self._notified_proxmenux_version != remote_main:
|
||||||
self._notified_proxmenux_version = remote_main
|
self._notified_proxmenux_version = remote_main
|
||||||
@@ -2255,6 +2285,8 @@ class PollingCollector:
|
|||||||
if local_beta:
|
if local_beta:
|
||||||
remote_beta = read_remote_version(self.REPO_DEVELOP_VERSION_URL)
|
remote_beta = read_remote_version(self.REPO_DEVELOP_VERSION_URL)
|
||||||
if remote_beta and version_tuple(remote_beta) > version_tuple(local_beta):
|
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
|
# Only notify if we haven't already notified for this version
|
||||||
if self._notified_proxmenux_beta_version != remote_beta:
|
if self._notified_proxmenux_beta_version != remote_beta:
|
||||||
self._notified_proxmenux_beta_version = remote_beta
|
self._notified_proxmenux_beta_version = remote_beta
|
||||||
|
|||||||
Reference in New Issue
Block a user