🎉 Improved backend structure & documentation, added new GLASSMORPHISM_NOTIFICATIONS feature 🎨
This commit is contained in:
256
backend/app.py
256
backend/app.py
@@ -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():
|
||||
|
Reference in New Issue
Block a user