217 Zeilen
7.6 KiB
HTML
217 Zeilen
7.6 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>{% block title %}Admin Panel{% endblock %} - Lizenzverwaltung</title>
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
|
|
<link href="https://cdn.jsdelivr.net/npm/select2-bootstrap-5-theme@1.3.0/dist/select2-bootstrap-5-theme.min.css" rel="stylesheet" />
|
|
{% block extra_css %}{% endblock %}
|
|
<style>
|
|
#session-timer {
|
|
font-family: monospace;
|
|
font-weight: bold;
|
|
font-size: 1.1rem;
|
|
padding: 0.25rem 0.75rem;
|
|
border-radius: 0.25rem;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.timer-normal {
|
|
background-color: #28a745;
|
|
color: white;
|
|
}
|
|
|
|
.timer-warning {
|
|
background-color: #ffc107;
|
|
color: #000;
|
|
}
|
|
|
|
.timer-danger {
|
|
background-color: #dc3545;
|
|
color: white;
|
|
animation: pulse 1s infinite;
|
|
}
|
|
|
|
.timer-critical {
|
|
background-color: #dc3545;
|
|
color: white;
|
|
animation: blink 0.5s infinite;
|
|
}
|
|
|
|
@keyframes pulse {
|
|
0% { opacity: 1; }
|
|
50% { opacity: 0.7; }
|
|
100% { opacity: 1; }
|
|
}
|
|
|
|
@keyframes blink {
|
|
0%, 49% { opacity: 1; }
|
|
50%, 100% { opacity: 0; }
|
|
}
|
|
|
|
.session-warning-modal {
|
|
position: fixed;
|
|
top: 20px;
|
|
right: 20px;
|
|
z-index: 9999;
|
|
max-width: 400px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body class="bg-light">
|
|
<nav class="navbar navbar-dark bg-dark">
|
|
<div class="container-fluid">
|
|
<a href="/" class="navbar-brand text-decoration-none">🎛️ AccountForger - Admin Panel</a>
|
|
<div class="d-flex align-items-center">
|
|
<div id="session-timer" class="timer-normal me-3">
|
|
⏱️ <span id="timer-display">5:00</span>
|
|
</div>
|
|
<span class="text-white me-3">Angemeldet als: {{ username }}</span>
|
|
<a href="/logout" class="btn btn-outline-light btn-sm">Abmelden</a>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
{% block content %}{% endblock %}
|
|
|
|
<!-- Session Warning Modal -->
|
|
<div id="session-warning" class="session-warning-modal" style="display: none;">
|
|
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
|
<strong>⚠️ Session läuft ab!</strong><br>
|
|
Ihre Session läuft in weniger als 1 Minute ab.<br>
|
|
<button type="button" class="btn btn-sm btn-success mt-2" onclick="extendSession()">
|
|
Session verlängern
|
|
</button>
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
|
|
|
<script>
|
|
// Session-Timer Konfiguration
|
|
const SESSION_TIMEOUT = 5 * 60; // 5 Minuten in Sekunden
|
|
let timeRemaining = SESSION_TIMEOUT;
|
|
let timerInterval;
|
|
let warningShown = false;
|
|
let lastActivity = Date.now();
|
|
|
|
// Timer Display Update
|
|
function updateTimerDisplay() {
|
|
const minutes = Math.floor(timeRemaining / 60);
|
|
const seconds = timeRemaining % 60;
|
|
const display = `${minutes}:${seconds.toString().padStart(2, '0')}`;
|
|
document.getElementById('timer-display').textContent = display;
|
|
|
|
// Timer-Farbe ändern
|
|
const timerElement = document.getElementById('session-timer');
|
|
timerElement.className = timerElement.className.replace(/timer-\w+/, '');
|
|
|
|
if (timeRemaining <= 30) {
|
|
timerElement.classList.add('timer-critical');
|
|
} else if (timeRemaining <= 60) {
|
|
timerElement.classList.add('timer-danger');
|
|
if (!warningShown) {
|
|
showSessionWarning();
|
|
warningShown = true;
|
|
}
|
|
} else if (timeRemaining <= 120) {
|
|
timerElement.classList.add('timer-warning');
|
|
} else {
|
|
timerElement.classList.add('timer-normal');
|
|
warningShown = false;
|
|
hideSessionWarning();
|
|
}
|
|
}
|
|
|
|
// Session Warning anzeigen
|
|
function showSessionWarning() {
|
|
document.getElementById('session-warning').style.display = 'block';
|
|
}
|
|
|
|
// Session Warning verstecken
|
|
function hideSessionWarning() {
|
|
document.getElementById('session-warning').style.display = 'none';
|
|
}
|
|
|
|
// Timer zurücksetzen
|
|
function resetTimer() {
|
|
timeRemaining = SESSION_TIMEOUT;
|
|
lastActivity = Date.now();
|
|
updateTimerDisplay();
|
|
}
|
|
|
|
// Session verlängern
|
|
function extendSession() {
|
|
fetch('/heartbeat', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.status === 'ok') {
|
|
resetTimer();
|
|
}
|
|
})
|
|
.catch(error => console.error('Heartbeat error:', error));
|
|
}
|
|
|
|
// Timer Countdown
|
|
function countdown() {
|
|
timeRemaining--;
|
|
updateTimerDisplay();
|
|
|
|
if (timeRemaining <= 0) {
|
|
clearInterval(timerInterval);
|
|
window.location.href = '/logout';
|
|
}
|
|
}
|
|
|
|
// Aktivitäts-Tracking
|
|
function trackActivity() {
|
|
const now = Date.now();
|
|
// Nur wenn mehr als 5 Sekunden seit letzter Aktivität
|
|
if (now - lastActivity > 5000) {
|
|
lastActivity = now;
|
|
extendSession(); // resetTimer() wird in extendSession nach erfolgreicher Response aufgerufen
|
|
}
|
|
}
|
|
|
|
// Event Listeners für Benutzeraktivität
|
|
document.addEventListener('click', trackActivity);
|
|
document.addEventListener('keypress', trackActivity);
|
|
document.addEventListener('mousemove', () => {
|
|
const now = Date.now();
|
|
// Mausbewegung nur alle 30 Sekunden tracken
|
|
if (now - lastActivity > 30000) {
|
|
trackActivity();
|
|
}
|
|
});
|
|
|
|
// AJAX Interceptor für automatische Session-Verlängerung
|
|
const originalFetch = window.fetch;
|
|
window.fetch = function(...args) {
|
|
// Nur für non-heartbeat requests den Timer verlängern
|
|
if (!args[0].includes('/heartbeat')) {
|
|
trackActivity();
|
|
}
|
|
return originalFetch.apply(this, args);
|
|
};
|
|
|
|
// Timer starten
|
|
timerInterval = setInterval(countdown, 1000);
|
|
updateTimerDisplay();
|
|
|
|
// Initial Heartbeat
|
|
extendSession();
|
|
</script>
|
|
|
|
{% block extra_js %}{% endblock %}
|
|
</body>
|
|
</html> |