mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2025-11-18 11:36:17 +00:00
Update AppImage
This commit is contained in:
@@ -23,7 +23,6 @@ import {
|
|||||||
RefreshCw,
|
RefreshCw,
|
||||||
Bell,
|
Bell,
|
||||||
Mail,
|
Mail,
|
||||||
Eye,
|
|
||||||
} from "lucide-react"
|
} from "lucide-react"
|
||||||
import { useState, useEffect } from "react"
|
import { useState, useEffect } from "react"
|
||||||
|
|
||||||
@@ -510,14 +509,13 @@ export function SystemLogs() {
|
|||||||
|
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-center justify-between mb-1">
|
<div className="flex items-center justify-between mb-1">
|
||||||
<div className="text-sm font-medium text-foreground">{log.service}</div>
|
<div className="text-sm font-medium text-foreground truncate">{log.service}</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="text-xs text-muted-foreground font-mono whitespace-nowrap ml-2">
|
||||||
<Eye className="h-3 w-3 text-muted-foreground" />
|
{log.timestamp}
|
||||||
<div className="text-xs text-muted-foreground font-mono">{log.timestamp}</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm text-foreground mb-1 line-clamp-2">{log.message}</div>
|
<div className="text-sm text-foreground mb-1 line-clamp-2">{log.message}</div>
|
||||||
<div className="text-xs text-muted-foreground">
|
<div className="text-xs text-muted-foreground truncate">
|
||||||
Source: {log.source}
|
Source: {log.source}
|
||||||
{log.pid && ` • PID: ${log.pid}`}
|
{log.pid && ` • PID: ${log.pid}`}
|
||||||
{log.hostname && ` • Host: ${log.hostname}`}
|
{log.hostname && ` • Host: ${log.hostname}`}
|
||||||
@@ -550,27 +548,24 @@ export function SystemLogs() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
<Badge variant="outline" className={getLevelColor(event.level)}>
|
<Badge variant="outline" className={`${getLevelColor(event.level)} max-w-[120px] truncate`}>
|
||||||
{getLevelIcon(event.level)}
|
{getLevelIcon(event.level)}
|
||||||
{event.status}
|
<span className="truncate">{event.status}</span>
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-center justify-between mb-1">
|
<div className="flex items-center justify-between mb-1 gap-2">
|
||||||
<div className="text-sm font-medium text-foreground">
|
<div className="text-sm font-medium text-foreground truncate">
|
||||||
{event.type}
|
{event.type}
|
||||||
{event.vmid && ` (VM/CT ${event.vmid})`}
|
{event.vmid && ` (VM/CT ${event.vmid})`}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="text-xs text-muted-foreground whitespace-nowrap">{event.duration}</div>
|
||||||
<Eye className="h-3 w-3 text-muted-foreground" />
|
|
||||||
<div className="text-xs text-muted-foreground">{event.duration}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-muted-foreground">
|
<div className="text-xs text-muted-foreground truncate">
|
||||||
Node: {event.node} • User: {event.user}
|
Node: {event.node} • User: {event.user}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-muted-foreground mt-1">Started: {event.starttime}</div>
|
<div className="text-xs text-muted-foreground mt-1">{event.starttime}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@@ -624,8 +619,8 @@ export function SystemLogs() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-center justify-between mb-1">
|
<div className="flex items-center justify-between mb-1 gap-2 flex-wrap">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2 flex-wrap">
|
||||||
<Badge variant="outline" className={getBackupTypeColor(backup.volid)}>
|
<Badge variant="outline" className={getBackupTypeColor(backup.volid)}>
|
||||||
{getBackupTypeLabel(backup.volid)}
|
{getBackupTypeLabel(backup.volid)}
|
||||||
</Badge>
|
</Badge>
|
||||||
@@ -633,14 +628,17 @@ export function SystemLogs() {
|
|||||||
{getBackupStorageLabel(backup.volid)}
|
{getBackupStorageLabel(backup.volid)}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
<Badge variant="outline" className="bg-green-500/10 text-green-500 border-green-500/20">
|
<Badge
|
||||||
|
variant="outline"
|
||||||
|
className="bg-green-500/10 text-green-500 border-green-500/20 whitespace-nowrap"
|
||||||
|
>
|
||||||
{backup.size_human}
|
{backup.size_human}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-muted-foreground mb-1">Storage: {backup.storage}</div>
|
<div className="text-xs text-muted-foreground mb-1 truncate">Storage: {backup.storage}</div>
|
||||||
<div className="text-xs text-muted-foreground flex items-center">
|
<div className="text-xs text-muted-foreground flex items-center">
|
||||||
<Calendar className="h-3 w-3 mr-1" />
|
<Calendar className="h-3 w-3 mr-1 flex-shrink-0" />
|
||||||
{backup.created}
|
<span className="truncate">{backup.created}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -673,13 +671,15 @@ export function SystemLogs() {
|
|||||||
|
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-center justify-between mb-1">
|
<div className="flex items-center justify-between mb-1">
|
||||||
<div className="text-sm font-medium text-muted-foreground capitalize">
|
<div className="text-sm font-medium text-muted-foreground capitalize truncate">
|
||||||
{notification.type}
|
{notification.type}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-muted-foreground font-mono">{notification.timestamp}</div>
|
<div className="text-xs text-muted-foreground font-mono whitespace-nowrap ml-2">
|
||||||
|
{notification.timestamp}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm text-foreground mb-1">{notification.message}</div>
|
<div className="text-sm text-foreground mb-1 line-clamp-2">{notification.message}</div>
|
||||||
<div className="text-xs text-muted-foreground">
|
<div className="text-xs text-muted-foreground truncate">
|
||||||
Service: {notification.service} • Source: {notification.source}
|
Service: {notification.service} • Source: {notification.source}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -845,16 +845,18 @@ export function SystemLogs() {
|
|||||||
<div className="text-sm font-medium text-muted-foreground mb-1">Storage</div>
|
<div className="text-sm font-medium text-muted-foreground mb-1">Storage</div>
|
||||||
<div className="text-sm text-foreground">{selectedBackup.storage}</div>
|
<div className="text-sm text-foreground">{selectedBackup.storage}</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="text-sm font-medium text-muted-foreground mb-1">Size</div>
|
||||||
|
<Badge variant="outline" className="bg-green-500/10 text-green-500 border-green-500/20">
|
||||||
|
{selectedBackup.size_human}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
{selectedBackup.vmid && (
|
{selectedBackup.vmid && (
|
||||||
<div>
|
<div>
|
||||||
<div className="text-sm font-medium text-muted-foreground mb-1">VM/CT ID</div>
|
<div className="text-sm font-medium text-muted-foreground mb-1">VM/CT ID</div>
|
||||||
<div className="text-sm text-foreground font-mono">{selectedBackup.vmid}</div>
|
<div className="text-sm text-foreground font-mono">{selectedBackup.vmid}</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div>
|
|
||||||
<div className="text-sm font-medium text-muted-foreground mb-1">Size</div>
|
|
||||||
<div className="text-sm text-foreground font-mono">{selectedBackup.size_human}</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
<div>
|
||||||
<div className="text-sm font-medium text-muted-foreground mb-1">Created</div>
|
<div className="text-sm font-medium text-muted-foreground mb-1">Created</div>
|
||||||
<div className="text-sm text-foreground">{selectedBackup.created}</div>
|
<div className="text-sm text-foreground">{selectedBackup.created}</div>
|
||||||
|
|||||||
@@ -3403,23 +3403,33 @@ def api_logs():
|
|||||||
def api_logs_download():
|
def api_logs_download():
|
||||||
"""Download system logs as a text file"""
|
"""Download system logs as a text file"""
|
||||||
try:
|
try:
|
||||||
log_type = request.args.get('type', 'system') # system, kernel, auth
|
log_type = request.args.get('type', 'system')
|
||||||
lines = request.args.get('lines', '1000')
|
hours = int(request.args.get('hours', '48')) # Changed from lines to hours, default 48h
|
||||||
|
level = request.args.get('level', 'all') # Added level filter
|
||||||
|
service = request.args.get('service', 'all') # Added service filter
|
||||||
|
|
||||||
|
cmd = ['journalctl', '--since', f'{hours} hours ago', '--no-pager']
|
||||||
|
|
||||||
if log_type == 'kernel':
|
if log_type == 'kernel':
|
||||||
cmd = ['journalctl', '-k', '-n', lines, '--no-pager']
|
cmd.extend(['-k'])
|
||||||
filename = 'kernel.log'
|
filename = 'kernel.log'
|
||||||
elif log_type == 'auth':
|
elif log_type == 'auth':
|
||||||
cmd = ['journalctl', '-u', 'ssh', '-u', 'sshd', '-n', lines, '--no-pager']
|
cmd.extend(['-u', 'ssh', '-u', 'sshd'])
|
||||||
filename = 'auth.log'
|
filename = 'auth.log'
|
||||||
else:
|
else:
|
||||||
cmd = ['journalctl', '-n', lines, '--no-pager']
|
|
||||||
filename = 'system.log'
|
filename = 'system.log'
|
||||||
|
|
||||||
|
# Apply level filter
|
||||||
|
if level != 'all':
|
||||||
|
cmd.extend(['-p', level])
|
||||||
|
|
||||||
|
# Apply service filter
|
||||||
|
if service != 'all':
|
||||||
|
cmd.extend(['-u', service])
|
||||||
|
|
||||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
|
||||||
|
|
||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
# Create a temporary file
|
|
||||||
import tempfile
|
import tempfile
|
||||||
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.log') as f:
|
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.log') as f:
|
||||||
f.write(result.stdout)
|
f.write(result.stdout)
|
||||||
@@ -3550,6 +3560,47 @@ def api_notifications():
|
|||||||
'total': 0
|
'total': 0
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@app.route('/api/notifications/download', methods=['GET'])
|
||||||
|
def api_notifications_download():
|
||||||
|
"""Download complete log for a specific notification"""
|
||||||
|
try:
|
||||||
|
timestamp = request.args.get('timestamp', '')
|
||||||
|
|
||||||
|
if not timestamp:
|
||||||
|
return jsonify({'error': 'Timestamp parameter required'}), 400
|
||||||
|
|
||||||
|
# Get logs around the notification timestamp (1 hour before and after)
|
||||||
|
cmd = [
|
||||||
|
'journalctl',
|
||||||
|
'--since', f'{timestamp}',
|
||||||
|
'--until', f'{timestamp}',
|
||||||
|
'-n', '1000',
|
||||||
|
'--no-pager'
|
||||||
|
]
|
||||||
|
|
||||||
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
|
||||||
|
|
||||||
|
if result.returncode == 0:
|
||||||
|
import tempfile
|
||||||
|
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.log') as f:
|
||||||
|
f.write(f"Notification Log - {timestamp}\n")
|
||||||
|
f.write("=" * 80 + "\n\n")
|
||||||
|
f.write(result.stdout)
|
||||||
|
temp_path = f.name
|
||||||
|
|
||||||
|
return send_file(
|
||||||
|
temp_path,
|
||||||
|
mimetype='text/plain',
|
||||||
|
as_attachment=True,
|
||||||
|
download_name=f'notification_{timestamp.replace(":", "_").replace(" ", "_")}.log'
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return jsonify({'error': 'Failed to generate notification log'}), 500
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error downloading notification log: {e}")
|
||||||
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
@app.route('/api/backups', methods=['GET'])
|
@app.route('/api/backups', methods=['GET'])
|
||||||
def api_backups():
|
def api_backups():
|
||||||
"""Get list of all backup files from Proxmox storage"""
|
"""Get list of all backup files from Proxmox storage"""
|
||||||
|
|||||||
Reference in New Issue
Block a user