"feat: Update admin UI layout in backend"
This commit is contained in:
parent
deda6d6c38
commit
d2a9a42651
@ -1656,6 +1656,219 @@ def admin_page():
|
||||
logs=logs
|
||||
)
|
||||
|
||||
# ===== ERROR MONITORING SYSTEM =====
|
||||
|
||||
@app.route("/api/admin/system-health", methods=['GET'])
|
||||
@login_required
|
||||
def api_admin_system_health():
|
||||
"""API-Endpunkt für System-Gesundheitscheck."""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({"error": "Berechtigung verweigert"}), 403
|
||||
|
||||
db_session = get_db_session()
|
||||
critical_errors = []
|
||||
warnings = []
|
||||
|
||||
try:
|
||||
# 1. Datenbank-Schema-Integrität prüfen
|
||||
try:
|
||||
# Test verschiedene kritische Tabellen und Spalten
|
||||
db_session.execute("SELECT COUNT(*) FROM guest_requests WHERE duration_minutes IS NOT NULL")
|
||||
schema_integrity = "OK"
|
||||
except Exception as e:
|
||||
critical_errors.append({
|
||||
"type": "database_schema",
|
||||
"message": f"Datenbank-Schema-Fehler: {str(e)}",
|
||||
"severity": "critical",
|
||||
"suggested_fix": "Datenbank-Migration ausführen",
|
||||
"timestamp": datetime.now().isoformat()
|
||||
})
|
||||
schema_integrity = "FEHLER"
|
||||
|
||||
# 2. Prüfe kritische Spalten in wichtigen Tabellen
|
||||
schema_checks = [
|
||||
("guest_requests", "duration_minutes"),
|
||||
("guest_requests", "file_name"),
|
||||
("guest_requests", "processed_by"),
|
||||
("users", "updated_at"),
|
||||
("jobs", "duration_minutes")
|
||||
]
|
||||
|
||||
missing_columns = []
|
||||
for table, column in schema_checks:
|
||||
try:
|
||||
db_session.execute(f"SELECT {column} FROM {table} LIMIT 1")
|
||||
except Exception:
|
||||
missing_columns.append(f"{table}.{column}")
|
||||
|
||||
if missing_columns:
|
||||
critical_errors.append({
|
||||
"type": "missing_columns",
|
||||
"message": f"Fehlende Datenbank-Spalten: {', '.join(missing_columns)}",
|
||||
"severity": "critical",
|
||||
"suggested_fix": "python utils/database_schema_migration.py ausführen",
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"details": missing_columns
|
||||
})
|
||||
|
||||
# 3. Prüfe auf wiederkehrende Datenbankfehler in den Logs
|
||||
import os
|
||||
log_file = os.path.join("logs", "app", f"myp_app_{datetime.now().strftime('%Y_%m_%d')}.log")
|
||||
recent_db_errors = 0
|
||||
|
||||
if os.path.exists(log_file):
|
||||
try:
|
||||
with open(log_file, 'r', encoding='utf-8') as f:
|
||||
last_lines = f.readlines()[-100:] # Letzte 100 Zeilen
|
||||
for line in last_lines:
|
||||
if "OperationalError" in line or "no such column" in line:
|
||||
recent_db_errors += 1
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if recent_db_errors > 5:
|
||||
critical_errors.append({
|
||||
"type": "frequent_db_errors",
|
||||
"message": f"{recent_db_errors} Datenbankfehler in letzter Zeit erkannt",
|
||||
"severity": "high",
|
||||
"suggested_fix": "System-Logs überprüfen und Migration ausführen",
|
||||
"timestamp": datetime.now().isoformat()
|
||||
})
|
||||
|
||||
# 4. Prüfe Drucker-Konnektivität
|
||||
offline_printers = db_session.query(Printer).filter(
|
||||
Printer.status == "offline",
|
||||
Printer.active == True
|
||||
).count()
|
||||
|
||||
if offline_printers > 0:
|
||||
warnings.append({
|
||||
"type": "printer_offline",
|
||||
"message": f"{offline_printers} aktive Drucker sind offline",
|
||||
"severity": "warning",
|
||||
"suggested_fix": "Drucker-Status überprüfen",
|
||||
"timestamp": datetime.now().isoformat()
|
||||
})
|
||||
|
||||
# 5. System-Performance Metriken
|
||||
import psutil
|
||||
cpu_usage = psutil.cpu_percent(interval=1)
|
||||
memory_usage = psutil.virtual_memory().percent
|
||||
disk_usage = psutil.disk_usage('/').percent
|
||||
|
||||
if cpu_usage > 90:
|
||||
warnings.append({
|
||||
"type": "high_cpu",
|
||||
"message": f"Hohe CPU-Auslastung: {cpu_usage:.1f}%",
|
||||
"severity": "warning",
|
||||
"suggested_fix": "System-Ressourcen überprüfen",
|
||||
"timestamp": datetime.now().isoformat()
|
||||
})
|
||||
|
||||
if memory_usage > 85:
|
||||
warnings.append({
|
||||
"type": "high_memory",
|
||||
"message": f"Hohe Speicher-Auslastung: {memory_usage:.1f}%",
|
||||
"severity": "warning",
|
||||
"suggested_fix": "Speicher-Verbrauch optimieren",
|
||||
"timestamp": datetime.now().isoformat()
|
||||
})
|
||||
|
||||
# 6. Letzte Migration info
|
||||
try:
|
||||
backup_dir = os.path.join("database", "backups")
|
||||
if os.path.exists(backup_dir):
|
||||
backup_files = [f for f in os.listdir(backup_dir) if f.endswith('.backup')]
|
||||
if backup_files:
|
||||
latest_backup = max(backup_files, key=lambda x: os.path.getctime(os.path.join(backup_dir, x)))
|
||||
last_migration = latest_backup.replace('.backup', '').replace('myp.db.backup_', '')
|
||||
else:
|
||||
last_migration = "Keine Backups gefunden"
|
||||
else:
|
||||
last_migration = "Backup-Verzeichnis nicht gefunden"
|
||||
except Exception:
|
||||
last_migration = "Unbekannt"
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"health_status": "critical" if critical_errors else ("warning" if warnings else "healthy"),
|
||||
"critical_errors": critical_errors,
|
||||
"warnings": warnings,
|
||||
"schema_integrity": schema_integrity,
|
||||
"last_migration": last_migration,
|
||||
"recent_errors_count": recent_db_errors,
|
||||
"system_metrics": {
|
||||
"cpu_usage": cpu_usage,
|
||||
"memory_usage": memory_usage,
|
||||
"disk_usage": disk_usage
|
||||
},
|
||||
"timestamp": datetime.now().isoformat()
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
app_logger.error(f"Fehler beim System-Gesundheitscheck: {str(e)}")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": "Fehler beim System-Gesundheitscheck",
|
||||
"critical_errors": [{
|
||||
"type": "system_check_failed",
|
||||
"message": f"System-Check fehlgeschlagen: {str(e)}",
|
||||
"severity": "critical",
|
||||
"suggested_fix": "System-Logs überprüfen",
|
||||
"timestamp": datetime.now().isoformat()
|
||||
}]
|
||||
}), 500
|
||||
finally:
|
||||
db_session.close()
|
||||
|
||||
@app.route("/api/admin/fix-errors", methods=['POST'])
|
||||
@login_required
|
||||
def api_admin_fix_errors():
|
||||
"""API-Endpunkt um automatische Fehler-Reparatur auszuführen."""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({"error": "Berechtigung verweigert"}), 403
|
||||
|
||||
try:
|
||||
# Automatische Migration ausführen
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
# Migration in separatem Prozess ausführen
|
||||
result = subprocess.run(
|
||||
[sys.executable, "utils/database_schema_migration.py"],
|
||||
cwd=os.path.dirname(os.path.abspath(__file__)),
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=60
|
||||
)
|
||||
|
||||
if result.returncode == 0:
|
||||
app_logger.info(f"Automatische Migration erfolgreich ausgeführt von Admin {current_user.email}")
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"message": "Automatische Reparatur erfolgreich durchgeführt",
|
||||
"details": result.stdout
|
||||
})
|
||||
else:
|
||||
app_logger.error(f"Automatische Migration fehlgeschlagen: {result.stderr}")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": "Automatische Reparatur fehlgeschlagen",
|
||||
"details": result.stderr
|
||||
}), 500
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": "Migration-Timeout - Vorgang dauerte zu lange"
|
||||
}), 500
|
||||
except Exception as e:
|
||||
app_logger.error(f"Fehler bei automatischer Reparatur: {str(e)}")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": f"Fehler bei automatischer Reparatur: {str(e)}"
|
||||
}), 500
|
||||
|
||||
# Direkter Zugriff auf Logout-Route (für Fallback)
|
||||
@app.route("/logout", methods=["GET", "POST"])
|
||||
def logout_redirect():
|
||||
|
@ -264,6 +264,62 @@
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- Tab Content -->
|
||||
<!-- Critical Errors Alert System -->
|
||||
<div id="critical-errors-alert" class="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-xl p-6 mb-8 hidden">
|
||||
<div class="flex items-start space-x-4">
|
||||
<div class="flex-shrink-0">
|
||||
<svg class="w-8 h-8 text-red-500 animate-pulse" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.732-.833-2.464 0L4.35 16.5c-.77.833.192 2.5 1.732 2.5z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<h3 class="text-lg font-semibold text-red-800 dark:text-red-200 mb-2">
|
||||
🚨 Kritische Systemfehler erkannt
|
||||
</h3>
|
||||
<div id="error-list" class="space-y-2">
|
||||
<!-- Error items will be populated here -->
|
||||
</div>
|
||||
<div class="mt-4 flex space-x-3">
|
||||
<button id="fix-errors-btn" class="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors text-sm font-medium">
|
||||
🔧 Automatisch reparieren
|
||||
</button>
|
||||
<button id="dismiss-errors-btn" class="px-4 py-2 bg-gray-200 dark:bg-gray-700 text-gray-800 dark:text-gray-200 rounded-lg hover:bg-gray-300 dark:hover:bg-gray-600 transition-colors text-sm font-medium">
|
||||
❌ Verwerfen
|
||||
</button>
|
||||
<button id="view-error-details-btn" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors text-sm font-medium">
|
||||
📊 Details anzeigen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Database Health Status -->
|
||||
<div id="db-health-status" class="bg-white/60 dark:bg-slate-800/60 backdrop-blur-sm rounded-xl border border-slate-200 dark:border-slate-600 p-6 mb-8 shadow-lg">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h3 class="text-lg font-semibold text-slate-900 dark:text-white">🗄️ Datenbank-Gesundheitsstatus</h3>
|
||||
<div class="flex items-center space-x-2">
|
||||
<div id="db-status-indicator" class="w-3 h-3 bg-green-400 rounded-full animate-pulse"></div>
|
||||
<span id="db-status-text" class="text-sm font-medium text-slate-600 dark:text-slate-400">Gesund</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div class="bg-white/40 dark:bg-slate-700/40 rounded-lg p-4">
|
||||
<div class="text-sm text-slate-500 dark:text-slate-400">Letzte Migration</div>
|
||||
<div id="last-migration" class="text-lg font-semibold text-slate-900 dark:text-white">Lädt...</div>
|
||||
</div>
|
||||
<div class="bg-white/40 dark:bg-slate-700/40 rounded-lg p-4">
|
||||
<div class="text-sm text-slate-500 dark:text-slate-400">Schema-Integrität</div>
|
||||
<div id="schema-integrity" class="text-lg font-semibold text-green-600 dark:text-green-400">Lädt...</div>
|
||||
</div>
|
||||
<div class="bg-white/40 dark:bg-slate-700/40 rounded-lg p-4">
|
||||
<div class="text-sm text-slate-500 dark:text-slate-400">Letzte Fehler</div>
|
||||
<div id="recent-errors-count" class="text-lg font-semibold text-slate-900 dark:text-white">Lädt...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tab Content -->
|
||||
<div class="bg-white/80 dark:bg-slate-800/80 backdrop-blur-xl rounded-3xl border border-white/20 dark:border-slate-700/50 shadow-xl overflow-hidden">
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user