"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 =====
|
# ===== 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 =====
|
# ===== STARTUP UND MAIN =====
|
||||||
if __name__ == "__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>
|
<script src="{{ url_for('static', filename='js/csp-violation-handler.js') }}"></script>
|
||||||
{% if current_user.is_authenticated %}
|
{% if current_user.is_authenticated %}
|
||||||
<script src="{{ url_for('static', filename='js/notifications.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/notifications.js') }}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/auto-logout.js') }}"></script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<!-- Additional JavaScript Functions -->
|
<!-- Additional JavaScript Functions -->
|
||||||
|
@ -621,33 +621,32 @@
|
|||||||
function setupAutoLogout() {
|
function setupAutoLogout() {
|
||||||
const autoLogoutSelect = document.getElementById('auto-logout');
|
const autoLogoutSelect = document.getElementById('auto-logout');
|
||||||
|
|
||||||
function resetLogoutTimer() {
|
// Event-Listener für Änderungen der Auto-Logout-Einstellung
|
||||||
if (logoutTimer) {
|
autoLogoutSelect.addEventListener('change', async function() {
|
||||||
clearTimeout(logoutTimer);
|
const newTimeout = this.value;
|
||||||
}
|
|
||||||
|
|
||||||
const minutes = parseInt(autoLogoutSelect.value);
|
try {
|
||||||
if (minutes && minutes !== 'never') {
|
const response = await fetch('/api/user/setting', {
|
||||||
logoutTimer = setTimeout(() => {
|
method: 'PATCH',
|
||||||
if (confirm('Sie werden aufgrund von Inaktivität abgemeldet. Möchten Sie angemeldet bleiben?')) {
|
headers: {
|
||||||
resetLogoutTimer();
|
'Content-Type': 'application/json',
|
||||||
} else {
|
'X-CSRFToken': document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || ''
|
||||||
window.location.href = '/logout';
|
},
|
||||||
|
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
|
// Enhanced toggle switches with keyboard support
|
||||||
|
Loading…
x
Reference in New Issue
Block a user