🎉 Improved backend structure & documentation, added new GLASSMORPHISM_NOTIFICATIONS feature 🎨

This commit is contained in:
2025-06-01 04:36:33 +02:00
parent 19eeed46fb
commit f0fe4c29d5
31 changed files with 1349 additions and 181 deletions

View File

@@ -1254,46 +1254,133 @@ def user_update_settings():
finally:
db_session.close()
@app.route("/api/user/settings", methods=["GET"])
@app.route("/api/user/settings", methods=["GET", "POST"])
@login_required
def get_user_settings():
"""Holt die aktuellen Benutzereinstellungen"""
try:
# Einstellungen aus Session oder Datenbank laden
user_settings = session.get('user_settings', {})
# Standard-Einstellungen falls keine vorhanden
default_settings = {
"theme": "system",
"reduced_motion": False,
"contrast": "normal",
"notifications": {
"new_jobs": True,
"job_updates": True,
"system": True,
"email": False
},
"privacy": {
"activity_logs": True,
"two_factor": False,
"auto_logout": 60
"""Holt die aktuellen Benutzereinstellungen (GET) oder speichert sie (POST)"""
if request.method == "GET":
try:
# Einstellungen aus Session oder Datenbank laden
user_settings = session.get('user_settings', {})
# Standard-Einstellungen falls keine vorhanden
default_settings = {
"theme": "system",
"reduced_motion": False,
"contrast": "normal",
"notifications": {
"new_jobs": True,
"job_updates": True,
"system": True,
"email": False
},
"privacy": {
"activity_logs": True,
"two_factor": False,
"auto_logout": 60
}
}
}
# Merge mit Standard-Einstellungen
settings = {**default_settings, **user_settings}
return jsonify({
"success": True,
"settings": settings
})
except Exception as e:
user_logger.error(f"Fehler beim Laden der Benutzereinstellungen: {str(e)}")
return jsonify({
"success": False,
"error": "Fehler beim Laden der Einstellungen"
}), 500
# Merge mit Standard-Einstellungen
settings = {**default_settings, **user_settings}
return jsonify({
"success": True,
"settings": settings
})
except Exception as e:
user_logger.error(f"Fehler beim Laden der Benutzereinstellungen: {str(e)}")
return jsonify({
"success": False,
"error": "Fehler beim Laden der Einstellungen"
}), 500
elif request.method == "POST":
"""Benutzereinstellungen über API aktualisieren"""
db_session = get_db_session()
try:
# JSON-Daten extrahieren
if not request.is_json:
return jsonify({"error": "Anfrage muss im JSON-Format sein"}), 400
data = request.get_json()
if not data:
return jsonify({"error": "Keine Daten empfangen"}), 400
# Einstellungen aus der Anfrage extrahieren
theme = data.get("theme", "system")
reduced_motion = bool(data.get("reduced_motion", False))
contrast = data.get("contrast", "normal")
notifications = data.get("notifications", {})
privacy = data.get("privacy", {})
# Validierung der Eingaben
valid_themes = ["light", "dark", "system"]
if theme not in valid_themes:
theme = "system"
valid_contrasts = ["normal", "high"]
if contrast not in valid_contrasts:
contrast = "normal"
# Benutzer aus der Datenbank laden
user = db_session.query(User).filter(User.id == int(current_user.id)).first()
if not user:
return jsonify({"error": "Benutzer nicht gefunden"}), 404
# Einstellungen-Dictionary erstellen
settings = {
"theme": theme,
"reduced_motion": reduced_motion,
"contrast": contrast,
"notifications": {
"new_jobs": bool(notifications.get("new_jobs", True)),
"job_updates": bool(notifications.get("job_updates", True)),
"system": bool(notifications.get("system", True)),
"email": bool(notifications.get("email", False))
},
"privacy": {
"activity_logs": bool(privacy.get("activity_logs", True)),
"two_factor": bool(privacy.get("two_factor", False)),
"auto_logout": max(5, min(480, int(privacy.get("auto_logout", 60)))) # 5-480 Minuten
},
"last_updated": datetime.now().isoformat()
}
# Prüfen, ob User-Tabelle eine settings-Spalte hat
if hasattr(user, 'settings'):
# Einstellungen in der Datenbank speichern
import json
user.settings = json.dumps(settings)
else:
# Fallback: In Session speichern (temporär)
session['user_settings'] = settings
user.updated_at = datetime.now()
db_session.commit()
user_logger.info(f"Benutzer {current_user.username} hat seine Einstellungen über die API aktualisiert")
return jsonify({
"success": True,
"message": "Einstellungen erfolgreich aktualisiert",
"settings": settings
})
except ValueError as e:
error = f"Ungültige Eingabedaten: {str(e)}"
user_logger.warning(f"Ungültige Einstellungsdaten von Benutzer {current_user.username}: {str(e)}")
return jsonify({"error": error}), 400
except Exception as e:
db_session.rollback()
error = f"Fehler beim Aktualisieren der Einstellungen: {str(e)}"
user_logger.error(f"Fehler beim Aktualisieren der Einstellungen für Benutzer {current_user.username}: {str(e)}")
return jsonify({"error": "Interner Serverfehler"}), 500
finally:
db_session.close()
@app.route("/user/change-password", methods=["POST"])
@login_required
@@ -1523,8 +1610,6 @@ def kiosk_deactivate():
except Exception as e:
kiosk_logger.error(f"Unerwarteter Fehler bei Kiosk-Deaktivierung: {str(e)}")
return jsonify({"error": "Unerwarteter Fehler"}), 500
@app.route('/api/kiosk/activate', methods=['POST'])
@login_required
def kiosk_activate():
"""Kiosk-Modus aktivieren (nur für Admins)."""
try:
@@ -6396,6 +6481,99 @@ def api_admin_system_status():
'health_status': 'error'
}), 500
# ===== ÖFFENTLICHE STATISTIK-API =====
@app.route("/api/statistics/public", methods=['GET'])
def api_public_statistics():
"""
Öffentliche Statistiken ohne Authentifizierung.
Stellt grundlegende, nicht-sensible Systemstatistiken bereit,
die auf der Startseite angezeigt werden können.
Returns:
JSON: Öffentliche Statistiken
"""
try:
db_session = get_db_session()
# Grundlegende, nicht-sensible Statistiken
total_jobs = db_session.query(Job).count()
completed_jobs = db_session.query(Job).filter(Job.status == "finished").count()
total_printers = db_session.query(Printer).count()
active_printers = db_session.query(Printer).filter(
Printer.active == True,
Printer.status.in_(["online", "available", "idle"])
).count()
# Erfolgsrate berechnen
success_rate = round((completed_jobs / total_jobs * 100) if total_jobs > 0 else 0, 1)
# Anonymisierte Benutzerstatistiken
total_users = db_session.query(User).filter(User.active == True).count()
# Letzte 30 Tage Aktivität (anonymisiert)
thirty_days_ago = datetime.now() - timedelta(days=30)
recent_jobs = db_session.query(Job).filter(
Job.created_at >= thirty_days_ago
).count()
db_session.close()
public_stats = {
"system_info": {
"total_jobs": total_jobs,
"completed_jobs": completed_jobs,
"success_rate": success_rate,
"total_printers": total_printers,
"active_printers": active_printers,
"active_users": total_users,
"recent_activity": recent_jobs
},
"health_indicators": {
"system_status": "operational",
"printer_availability": round((active_printers / total_printers * 100) if total_printers > 0 else 0, 1),
"last_updated": datetime.now().isoformat()
},
"features": {
"multi_location_support": True,
"real_time_monitoring": True,
"automated_scheduling": True,
"advanced_reporting": True
}
}
return jsonify(public_stats)
except Exception as e:
app_logger.error(f"Fehler bei öffentlichen Statistiken: {str(e)}")
# Fallback-Statistiken bei Fehler
return jsonify({
"system_info": {
"total_jobs": 0,
"completed_jobs": 0,
"success_rate": 0,
"total_printers": 0,
"active_printers": 0,
"active_users": 0,
"recent_activity": 0
},
"health_indicators": {
"system_status": "maintenance",
"printer_availability": 0,
"last_updated": datetime.now().isoformat()
},
"features": {
"multi_location_support": True,
"real_time_monitoring": True,
"automated_scheduling": True,
"advanced_reporting": True
},
"error": "Statistiken temporär nicht verfügbar"
}), 200 # 200 statt 500 um Frontend nicht zu brechen
@app.route("/api/stats", methods=['GET'])
@login_required
def api_stats():