"feat: Update admin UI layout in backend"

This commit is contained in:
2025-05-29 18:25:29 +02:00
parent deda6d6c38
commit d2a9a42651
2 changed files with 269 additions and 0 deletions

View File

@@ -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():