"feat: Implement auto logout functionality via static JS
This commit is contained in:
parent
f4fbf92055
commit
d26f8b0d93
@ -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__":
|
||||
|
143
backend/app/static/js/auto-logout.js
Normal file
143
backend/app/static/js/auto-logout.js
Normal file
@ -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 = `
|
||||
<div class="bg-white dark:bg-slate-800 rounded-lg p-6 max-w-md mx-4 shadow-xl">
|
||||
<h3 class="text-lg font-medium text-slate-900 dark:text-white mb-4">Automatische Abmeldung</h3>
|
||||
<p class="text-sm text-slate-600 dark:text-slate-300 mb-4">
|
||||
Sie werden in ${this.warningTime} Minuten aufgrund von Inaktivität abgemeldet.
|
||||
</p>
|
||||
<div class="flex space-x-3">
|
||||
<button id="stay-logged-in" class="bg-blue-600 text-white px-4 py-2 rounded-lg">
|
||||
Angemeldet bleiben
|
||||
</button>
|
||||
<button id="logout-now" class="bg-gray-300 text-slate-700 px-4 py-2 rounded-lg">
|
||||
Jetzt abmelden
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(modal);
|
||||
|
||||
document.getElementById('stay-logged-in').onclick = () => {
|
||||
this.closeWarning();
|
||||
this.sendKeepAlive();
|
||||
this.resetTimer();
|
||||
};
|
||||
|
||||
document.getElementById('logout-now').onclick = () => {
|
||||
this.performLogout();
|
||||
};
|
||||
}
|
||||
|
||||
closeWarning() {
|
||||
const modal = document.getElementById('auto-logout-warning');
|
||||
if (modal) modal.remove();
|
||||
this.isWarningShown = false;
|
||||
}
|
||||
|
||||
async sendKeepAlive() {
|
||||
try {
|
||||
await fetch('/api/auth/keep-alive', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': this.getCSRFToken()
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.warn('Keep-Alive fehlgeschlagen:', error);
|
||||
}
|
||||
}
|
||||
|
||||
getCSRFToken() {
|
||||
const metaTag = document.querySelector('meta[name="csrf-token"]');
|
||||
return metaTag ? metaTag.getAttribute('content') : '';
|
||||
}
|
||||
|
||||
async performLogout() {
|
||||
this.closeWarning();
|
||||
this.clearTimers();
|
||||
window.location.href = '/auth/logout';
|
||||
}
|
||||
}
|
||||
|
||||
// Initialisierung
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
if (!window.location.pathname.includes('/login')) {
|
||||
window.autoLogoutManager = new AutoLogoutManager();
|
||||
}
|
||||
});
|
@ -584,6 +584,7 @@
|
||||
<script src="{{ url_for('static', filename='js/csp-violation-handler.js') }}"></script>
|
||||
{% if current_user.is_authenticated %}
|
||||
<script src="{{ url_for('static', filename='js/notifications.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/auto-logout.js') }}"></script>
|
||||
{% endif %}
|
||||
|
||||
<!-- Additional JavaScript Functions -->
|
||||
|
@ -621,33 +621,32 @@
|
||||
function setupAutoLogout() {
|
||||
const autoLogoutSelect = document.getElementById('auto-logout');
|
||||
|
||||
function resetLogoutTimer() {
|
||||
if (logoutTimer) {
|
||||
clearTimeout(logoutTimer);
|
||||
}
|
||||
// Event-Listener für Änderungen der Auto-Logout-Einstellung
|
||||
autoLogoutSelect.addEventListener('change', async function() {
|
||||
const newTimeout = this.value;
|
||||
|
||||
const minutes = parseInt(autoLogoutSelect.value);
|
||||
if (minutes && minutes !== 'never') {
|
||||
logoutTimer = setTimeout(() => {
|
||||
if (confirm('Sie werden aufgrund von Inaktivität abgemeldet. Möchten Sie angemeldet bleiben?')) {
|
||||
resetLogoutTimer();
|
||||
} else {
|
||||
window.location.href = '/logout';
|
||||
try {
|
||||
const response = await fetch('/api/user/setting', {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || ''
|
||||
},
|
||||
body: JSON.stringify({ auto_logout: newTimeout })
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
// Globales Auto-Logout-System benachrichtigen
|
||||
if (window.autoLogoutManager) {
|
||||
window.autoLogoutManager.updateSettings(newTimeout);
|
||||
}
|
||||
}, minutes * 60 * 1000);
|
||||
showFlashMessage('Auto-Logout-Einstellung aktualisiert', 'success');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Aktualisieren der Auto-Logout-Einstellung:', error);
|
||||
showFlashMessage('Fehler beim Speichern der Einstellung', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// Reset timer on any user activity
|
||||
['mousedown', 'mousemove', 'keypress', 'scroll', 'touchstart', 'click'].forEach(event => {
|
||||
document.addEventListener(event, resetLogoutTimer, { passive: true });
|
||||
});
|
||||
|
||||
// Initial setup
|
||||
resetLogoutTimer();
|
||||
|
||||
// Update timer when setting changes
|
||||
autoLogoutSelect.addEventListener('change', resetLogoutTimer);
|
||||
}
|
||||
|
||||
// Enhanced toggle switches with keyboard support
|
||||
|
Loading…
x
Reference in New Issue
Block a user