diff --git a/backend/app/app.py b/backend/app/app.py index a3ad40b0..8bd5f050 100644 --- a/backend/app/app.py +++ b/backend/app/app.py @@ -5866,6 +5866,108 @@ def export_guest_requests(): # ===== ENDE ADMIN GASTAUFTRÄGE API-ENDPUNKTE ===== +@app.route("/api/user/settings/auto-logout", methods=["GET"]) +@login_required +def get_auto_logout_settings(): + """Holt nur die Auto-Logout-Einstellungen des Benutzers""" + try: + user_settings = session.get('user_settings', {}) + auto_logout = user_settings.get('privacy', {}).get('auto_logout', 60) + + return jsonify({ + "success": True, + "auto_logout": auto_logout + }) + + except Exception as e: + user_logger.error(f"Fehler beim Laden der Auto-Logout-Einstellungen: {str(e)}") + return jsonify({ + "success": False, + "error": "Fehler beim Laden der Auto-Logout-Einstellungen" + }), 500 + +@app.route("/api/user/setting", methods=["PATCH"]) +@login_required +def update_single_setting(): + """Aktualisiert eine einzelne Benutzereinstellung""" + try: + 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 erhalten"}), 400 + + # Aktuelle Einstellungen laden + user_settings = session.get('user_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 + } + }) + + # Einzelne Einstellung aktualisieren + for key, value in data.items(): + if key == "auto_logout": + # Validierung für Auto-Logout + try: + timeout = int(value) if value != "never" else "never" + if timeout != "never" and (timeout < 5 or timeout > 480): + return jsonify({"error": "Auto-Logout muss zwischen 5 und 480 Minuten liegen"}), 400 + user_settings.setdefault('privacy', {})['auto_logout'] = timeout + except (ValueError, TypeError): + return jsonify({"error": "Ungültiger Auto-Logout-Wert"}), 400 + + user_settings['last_updated'] = datetime.now().isoformat() + session['user_settings'] = user_settings + + user_logger.info(f"Benutzer {current_user.username} hat Einstellung '{key}' aktualisiert") + + return jsonify({ + "success": True, + "message": "Einstellung erfolgreich aktualisiert" + }) + + except Exception as e: + user_logger.error(f"Fehler beim Aktualisieren der Einzeleinstellung: {str(e)}") + return jsonify({ + "success": False, + "error": "Fehler beim Aktualisieren der Einstellung" + }), 500 + +@app.route("/api/auth/keep-alive", methods=["POST"]) +@login_required +def keep_alive(): + """Keep-Alive-Endpunkt für Auto-Logout-System""" + try: + # Session-Timestamp aktualisieren + session.permanent = True + session.modified = True + + auth_logger.info(f"Keep-Alive für Benutzer {current_user.username}") + + return jsonify({ + "success": True, + "message": "Session verlängert", + "timestamp": datetime.now().isoformat() + }) + + except Exception as e: + auth_logger.error(f"Fehler beim Keep-Alive: {str(e)}") + return jsonify({ + "success": False, + "error": "Fehler beim Verlängern der Session" + }), 500 # ===== STARTUP UND MAIN ===== if __name__ == "__main__": diff --git a/backend/app/static/js/auto-logout.js b/backend/app/static/js/auto-logout.js new file mode 100644 index 00000000..d36fc86e --- /dev/null +++ b/backend/app/static/js/auto-logout.js @@ -0,0 +1,143 @@ +class AutoLogoutManager { + constructor() { + this.timer = null; + this.warningTimer = null; + this.timeout = 60; // Standard: 60 Minuten + this.warningTime = 5; // Warnung 5 Minuten vor Logout + this.isWarningShown = false; + + this.init(); + } + + async init() { + await this.loadSettings(); + this.setupActivityListeners(); + this.startTimer(); + } + + async loadSettings() { + try { + const response = await fetch('/api/user/settings'); + if (response.ok) { + const data = await response.json(); + if (data.success && data.settings.privacy?.auto_logout) { + const timeout = parseInt(data.settings.privacy.auto_logout); + if (timeout > 0 && timeout !== 'never') { + this.timeout = timeout; + } else { + this.timeout = 0; // Deaktiviert + } + } + } + } catch (error) { + console.warn('Auto-Logout-Einstellungen konnten nicht geladen werden:', error); + } + } + + setupActivityListeners() { + const events = ['mousedown', 'mousemove', 'keypress', 'scroll', 'touchstart', 'click']; + + events.forEach(event => { + document.addEventListener(event, () => this.resetTimer(), { passive: true }); + }); + } + + startTimer() { + if (this.timeout <= 0) return; + + this.clearTimers(); + + const timeoutMs = this.timeout * 60 * 1000; + const warningMs = this.warningTime * 60 * 1000; + + this.warningTimer = setTimeout(() => this.showWarning(), timeoutMs - warningMs); + this.timer = setTimeout(() => this.performLogout(), timeoutMs); + } + + resetTimer() { + if (this.isWarningShown) { + this.closeWarning(); + } + this.startTimer(); + } + + clearTimers() { + if (this.timer) clearTimeout(this.timer); + if (this.warningTimer) clearTimeout(this.warningTimer); + } + + showWarning() { + if (this.isWarningShown) return; + this.isWarningShown = true; + + const modal = document.createElement('div'); + modal.id = 'auto-logout-warning'; + modal.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50'; + modal.innerHTML = ` +
+ Sie werden in ${this.warningTime} Minuten aufgrund von Inaktivität abgemeldet. +
+