mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2025-11-18 03:26:17 +00:00
Update AppImage
This commit is contained in:
@@ -993,29 +993,31 @@ export function SystemLogs() {
|
|||||||
{/* Notifications Tab */}
|
{/* Notifications Tab */}
|
||||||
<TabsContent value="notifications" className="space-y-4">
|
<TabsContent value="notifications" className="space-y-4">
|
||||||
<ScrollArea className="h-[600px] w-full rounded-md border border-border">
|
<ScrollArea className="h-[600px] w-full rounded-md border border-border">
|
||||||
<div className="space-y-2 p-2 sm:p-4">
|
<div className="space-y-3 p-3 sm:p-4">
|
||||||
{notifications.map((notification, index) => (
|
{notifications.map((notification, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className="flex flex-col space-y-2 p-2 sm:p-3 rounded-lg bg-card/50 border border-border/50 hover:bg-card/80 transition-colors cursor-pointer"
|
className="flex flex-col space-y-2 p-3 sm:p-4 rounded-lg bg-card/50 border border-border/50 hover:bg-card/80 transition-colors cursor-pointer"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedNotification(notification)
|
setSelectedNotification(notification)
|
||||||
setIsNotificationModalOpen(true)
|
setIsNotificationModalOpen(true)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-2 flex-wrap">
|
<div className="flex items-start sm:items-center gap-2 flex-wrap">
|
||||||
{getNotificationIcon(notification.type)}
|
<div className="flex items-center gap-2">
|
||||||
<Badge variant="outline" className={`${getNotificationTypeColor(notification.type)} text-xs`}>
|
{getNotificationIcon(notification.type)}
|
||||||
{notification.type.toUpperCase()}
|
<Badge variant="outline" className={`${getNotificationTypeColor(notification.type)} text-xs`}>
|
||||||
</Badge>
|
{notification.type.toUpperCase()}
|
||||||
<div className="text-xs text-muted-foreground font-mono ml-auto whitespace-nowrap">
|
</Badge>
|
||||||
{notification.timestamp}
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className="text-xs text-muted-foreground font-mono ml-auto">{notification.timestamp}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-1">
|
<div className="space-y-1.5">
|
||||||
<div className="text-sm text-foreground line-clamp-2 break-words">{notification.message}</div>
|
<div className="text-sm text-foreground break-words overflow-hidden">
|
||||||
<div className="text-xs text-muted-foreground truncate">
|
{notification.message}
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-muted-foreground break-words">
|
||||||
Service: {notification.service} • Source: {notification.source}
|
Service: {notification.service} • Source: {notification.source}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1221,11 +1223,11 @@ export function SystemLogs() {
|
|||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
<Dialog open={isNotificationModalOpen} onOpenChange={setIsNotificationModalOpen}>
|
<Dialog open={isNotificationModalOpen} onOpenChange={setIsNotificationModalOpen}>
|
||||||
<DialogContent className="max-w-3xl max-h-[90vh] overflow-y-auto w-[95vw] sm:w-full">
|
<DialogContent className="max-w-3xl max-h-[85vh] overflow-y-auto w-[96vw] sm:w-full mx-2 sm:mx-auto">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle className="flex items-center gap-2 text-base sm:text-lg">
|
<DialogTitle className="flex items-center gap-2 text-base sm:text-lg pr-8">
|
||||||
<Bell className="h-4 w-4 sm:h-5 sm:w-5" />
|
<Bell className="h-4 w-4 sm:h-5 sm:w-5 flex-shrink-0" />
|
||||||
Notification Details
|
<span className="truncate">Notification Details</span>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogDescription className="text-xs sm:text-sm">
|
<DialogDescription className="text-xs sm:text-sm">
|
||||||
Complete information about this notification
|
Complete information about this notification
|
||||||
@@ -1235,30 +1237,30 @@ export function SystemLogs() {
|
|||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3 sm:gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-3 sm:gap-4">
|
||||||
<div>
|
<div>
|
||||||
<div className="text-xs sm:text-sm font-medium text-muted-foreground mb-1">Type</div>
|
<div className="text-xs sm:text-sm font-medium text-muted-foreground mb-1.5">Type</div>
|
||||||
<Badge variant="outline" className={`${getNotificationTypeColor(selectedNotification.type)} text-xs`}>
|
<Badge variant="outline" className={`${getNotificationTypeColor(selectedNotification.type)} text-xs`}>
|
||||||
{selectedNotification.type.toUpperCase()}
|
{selectedNotification.type.toUpperCase()}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="text-xs sm:text-sm font-medium text-muted-foreground mb-1">Timestamp</div>
|
<div className="text-xs sm:text-sm font-medium text-muted-foreground mb-1.5">Timestamp</div>
|
||||||
<div className="text-xs sm:text-sm text-foreground font-mono break-words">
|
<div className="text-xs sm:text-sm text-foreground font-mono break-all">
|
||||||
{selectedNotification.timestamp}
|
{selectedNotification.timestamp}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="text-xs sm:text-sm font-medium text-muted-foreground mb-1">Service</div>
|
<div className="text-xs sm:text-sm font-medium text-muted-foreground mb-1.5">Service</div>
|
||||||
<div className="text-xs sm:text-sm text-foreground break-words">{selectedNotification.service}</div>
|
<div className="text-xs sm:text-sm text-foreground break-words">{selectedNotification.service}</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="text-xs sm:text-sm font-medium text-muted-foreground mb-1">Source</div>
|
<div className="text-xs sm:text-sm font-medium text-muted-foreground mb-1.5">Source</div>
|
||||||
<div className="text-xs sm:text-sm text-foreground break-words">{selectedNotification.source}</div>
|
<div className="text-xs sm:text-sm text-foreground break-words">{selectedNotification.source}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="text-xs sm:text-sm font-medium text-muted-foreground mb-2">Message</div>
|
<div className="text-xs sm:text-sm font-medium text-muted-foreground mb-2">Message</div>
|
||||||
<div className="p-3 sm:p-4 rounded-lg bg-muted/50 border border-border max-h-[200px] sm:max-h-[300px] overflow-y-auto">
|
<div className="p-3 sm:p-4 rounded-lg bg-muted/50 border border-border max-h-[180px] sm:max-h-[300px] overflow-y-auto">
|
||||||
<pre className="text-xs sm:text-sm text-foreground whitespace-pre-wrap break-words">
|
<pre className="text-xs sm:text-sm text-foreground whitespace-pre-wrap break-all font-mono">
|
||||||
{selectedNotification.message}
|
{selectedNotification.message}
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
@@ -1267,10 +1269,10 @@ export function SystemLogs() {
|
|||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => handleDownloadNotificationLog(selectedNotification)}
|
onClick={() => handleDownloadNotificationLog(selectedNotification)}
|
||||||
className="border-border w-full sm:w-auto text-xs sm:text-sm"
|
className="border-border w-full sm:w-auto text-xs sm:text-sm h-9 sm:h-10"
|
||||||
>
|
>
|
||||||
<Download className="h-3 w-3 sm:h-4 sm:w-4 mr-2" />
|
<Download className="h-3 w-3 sm:h-4 sm:w-4 mr-2" />
|
||||||
Download Complete Message
|
<span className="truncate">Download Complete Message</span>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -179,7 +179,16 @@ def get_intel_gpu_processes_from_text():
|
|||||||
utilization = min(100.0, (filled_chars / 50.0) * 100.0)
|
utilization = min(100.0, (filled_chars / 50.0) * 100.0)
|
||||||
if utilization > 0:
|
if utilization > 0:
|
||||||
engines[engine_name] = f"{utilization:.1f}%"
|
engines[engine_name] = f"{utilization:.1f}%"
|
||||||
|
|
||||||
|
if engine_name == 'Render/3D' and utilization > 0:
|
||||||
|
engine_names[0] = f"Render/3D ({utilization:.1f}%)"
|
||||||
|
elif engine_name == 'Blitter' and utilization > 0:
|
||||||
|
engine_names[1] = f"Blitter ({utilization:.1f}%)"
|
||||||
|
elif engine_name == 'Video' and utilization > 0:
|
||||||
|
engine_names[2] = f"Video ({utilization:.1f}%)"
|
||||||
|
elif engine_name == 'VideoEnhance' and utilization > 0:
|
||||||
|
engine_names[3] = f"VideoEnhance ({utilization:.1f}%)"
|
||||||
|
|
||||||
if engines: # Only add if there's some GPU activity
|
if engines: # Only add if there's some GPU activity
|
||||||
process_info = {
|
process_info = {
|
||||||
'name': name,
|
'name': name,
|
||||||
@@ -3804,11 +3813,15 @@ def get_task_log(upid):
|
|||||||
try:
|
try:
|
||||||
print(f"[v0] Getting task log for UPID: {upid}")
|
print(f"[v0] Getting task log for UPID: {upid}")
|
||||||
|
|
||||||
|
# Proxmox stores files without trailing :: but API may include them
|
||||||
|
upid_clean = upid.rstrip(':')
|
||||||
|
print(f"[v0] Cleaned UPID: {upid_clean}")
|
||||||
|
|
||||||
# Parse UPID to extract node name and calculate index
|
# Parse UPID to extract node name and calculate index
|
||||||
# UPID format: UPID:node:pid:pstart:starttime:type:id:user:
|
# UPID format: UPID:node:pid:pstart:starttime:type:id:user:
|
||||||
parts = upid.split(':')
|
parts = upid_clean.split(':')
|
||||||
if len(parts) < 5:
|
if len(parts) < 5:
|
||||||
print(f"[v0] Invalid UPID format: {upid}")
|
print(f"[v0] Invalid UPID format: {upid_clean}")
|
||||||
return jsonify({'error': 'Invalid UPID format'}), 400
|
return jsonify({'error': 'Invalid UPID format'}), 400
|
||||||
|
|
||||||
node = parts[1]
|
node = parts[1]
|
||||||
@@ -3819,39 +3832,56 @@ def get_task_log(upid):
|
|||||||
|
|
||||||
print(f"[v0] Extracted node: {node}, starttime: {starttime}, index: {index}")
|
print(f"[v0] Extracted node: {node}, starttime: {starttime}, index: {index}")
|
||||||
|
|
||||||
# Construct file path
|
# Try with cleaned UPID (no trailing colons)
|
||||||
log_file_path = f"/var/log/pve/tasks/{index}/{upid}"
|
log_file_path = f"/var/log/pve/tasks/{index}/{upid_clean}"
|
||||||
print(f"[v0] Reading log file: {log_file_path}")
|
print(f"[v0] Trying log file: {log_file_path}")
|
||||||
|
|
||||||
# Read the log file
|
|
||||||
if os.path.exists(log_file_path):
|
if os.path.exists(log_file_path):
|
||||||
with open(log_file_path, 'r', encoding='utf-8', errors='ignore') as f:
|
with open(log_file_path, 'r', encoding='utf-8', errors='ignore') as f:
|
||||||
log_text = f.read()
|
log_text = f.read()
|
||||||
|
|
||||||
print(f"[v0] Successfully read {len(log_text)} bytes from log file")
|
print(f"[v0] Successfully read {len(log_text)} bytes from log file")
|
||||||
return log_text, 200, {'Content-Type': 'text/plain; charset=utf-8'}
|
return log_text, 200, {'Content-Type': 'text/plain; charset=utf-8'}
|
||||||
else:
|
|
||||||
# Try with uppercase index
|
# Try with single trailing colon
|
||||||
log_file_path_upper = f"/var/log/pve/tasks/{index.upper()}/{upid}"
|
log_file_path_single = f"/var/log/pve/tasks/{index}/{upid_clean}:"
|
||||||
print(f"[v0] Trying alternative path: {log_file_path_upper}")
|
print(f"[v0] Trying alternative path with single colon: {log_file_path_single}")
|
||||||
|
|
||||||
|
if os.path.exists(log_file_path_single):
|
||||||
|
with open(log_file_path_single, 'r', encoding='utf-8', errors='ignore') as f:
|
||||||
|
log_text = f.read()
|
||||||
|
print(f"[v0] Successfully read {len(log_text)} bytes from alternative log file")
|
||||||
|
return log_text, 200, {'Content-Type': 'text/plain; charset=utf-8'}
|
||||||
|
|
||||||
|
# Try with uppercase index
|
||||||
|
log_file_path_upper = f"/var/log/pve/tasks/{index.upper()}/{upid_clean}"
|
||||||
|
print(f"[v0] Trying uppercase index path: {log_file_path_upper}")
|
||||||
|
|
||||||
|
if os.path.exists(log_file_path_upper):
|
||||||
|
with open(log_file_path_upper, 'r', encoding='utf-8', errors='ignore') as f:
|
||||||
|
log_text = f.read()
|
||||||
|
print(f"[v0] Successfully read {len(log_text)} bytes from uppercase index log file")
|
||||||
|
return log_text, 200, {'Content-Type': 'text/plain; charset=utf-8'}
|
||||||
|
|
||||||
|
# List available files in the directory for debugging
|
||||||
|
tasks_dir = f"/var/log/pve/tasks/{index}"
|
||||||
|
if os.path.exists(tasks_dir):
|
||||||
|
available_files = os.listdir(tasks_dir)
|
||||||
|
print(f"[v0] Available files in {tasks_dir}: {available_files[:10]}") # Show first 10
|
||||||
|
|
||||||
if os.path.exists(log_file_path_upper):
|
upid_prefix = ':'.join(parts[:5]) # Get first 5 parts of UPID
|
||||||
with open(log_file_path_upper, 'r', encoding='utf-8', errors='ignore') as f:
|
for filename in available_files:
|
||||||
log_text = f.read()
|
if filename.startswith(upid_prefix):
|
||||||
|
matched_file = f"{tasks_dir}/{filename}"
|
||||||
print(f"[v0] Successfully read {len(log_text)} bytes from alternative log file")
|
print(f"[v0] Found matching file by prefix: {matched_file}")
|
||||||
return log_text, 200, {'Content-Type': 'text/plain; charset=utf-8'}
|
with open(matched_file, 'r', encoding='utf-8', errors='ignore') as f:
|
||||||
else:
|
log_text = f.read()
|
||||||
# List available files in the directory for debugging
|
print(f"[v0] Successfully read {len(log_text)} bytes from matched file")
|
||||||
tasks_dir = f"/var/log/pve/tasks/{index}"
|
return log_text, 200, {'Content-Type': 'text/plain; charset=utf-8'}
|
||||||
if os.path.exists(tasks_dir):
|
else:
|
||||||
available_files = os.listdir(tasks_dir)
|
print(f"[v0] Tasks directory does not exist: {tasks_dir}")
|
||||||
print(f"[v0] Available files in {tasks_dir}: {available_files[:10]}") # Show first 10
|
|
||||||
else:
|
print(f"[v0] Log file not found after trying all variations")
|
||||||
print(f"[v0] Tasks directory does not exist: {tasks_dir}")
|
return jsonify({'error': 'Log file not found', 'tried_paths': [log_file_path, log_file_path_single, log_file_path_upper]}), 404
|
||||||
|
|
||||||
print(f"[v0] Log file not found: {log_file_path}")
|
|
||||||
return jsonify({'error': 'Log file not found', 'path': log_file_path}), 404
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[v0] Error fetching task log for UPID {upid}: {type(e).__name__}: {e}")
|
print(f"[v0] Error fetching task log for UPID {upid}: {type(e).__name__}: {e}")
|
||||||
|
|||||||
Reference in New Issue
Block a user