167 Zeilen
7.6 KiB
HTML
167 Zeilen
7.6 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Session-Tracking{% endblock %}
|
|
|
|
{% macro active_sortable_header(label, field, current_sort, current_order) %}
|
|
<th>
|
|
{% if current_sort == field %}
|
|
<a href="{{ url_for('sessions', active_sort=field, active_order='desc' if current_order == 'asc' else 'asc', ended_sort=ended_sort, ended_order=ended_order) }}"
|
|
class="server-sortable">
|
|
{% else %}
|
|
<a href="{{ url_for('sessions', active_sort=field, active_order='asc', ended_sort=ended_sort, ended_order=ended_order) }}"
|
|
class="server-sortable">
|
|
{% endif %}
|
|
{{ label }}
|
|
<span class="sort-indicator{% if current_sort == field %} active{% endif %}">
|
|
{% if current_sort == field %}
|
|
{% if current_order == 'asc' %}↑{% else %}↓{% endif %}
|
|
{% else %}
|
|
↕
|
|
{% endif %}
|
|
</span>
|
|
</a>
|
|
</th>
|
|
{% endmacro %}
|
|
|
|
{% macro ended_sortable_header(label, field, current_sort, current_order) %}
|
|
<th>
|
|
{% if current_sort == field %}
|
|
<a href="{{ url_for('sessions', active_sort=active_sort, active_order=active_order, ended_sort=field, ended_order='desc' if current_order == 'asc' else 'asc') }}"
|
|
class="server-sortable">
|
|
{% else %}
|
|
<a href="{{ url_for('sessions', active_sort=active_sort, active_order=active_order, ended_sort=field, ended_order='asc') }}"
|
|
class="server-sortable">
|
|
{% endif %}
|
|
{{ label }}
|
|
<span class="sort-indicator{% if current_sort == field %} active{% endif %}">
|
|
{% if current_sort == field %}
|
|
{% if current_order == 'asc' %}↑{% else %}↓{% endif %}
|
|
{% else %}
|
|
↕
|
|
{% endif %}
|
|
</span>
|
|
</a>
|
|
</th>
|
|
{% endmacro %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
.session-active { background-color: #d4edda; }
|
|
.session-warning { background-color: #fff3cd; }
|
|
.session-inactive { background-color: #f8f9fa; }
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container py-5">
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h2>Session-Tracking</h2>
|
|
<div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Aktive Sessions -->
|
|
<div class="card mb-4">
|
|
<div class="card-header bg-success text-white">
|
|
<h5 class="mb-0">🟢 Aktive Sessions ({{ active_sessions|length }})</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if active_sessions %}
|
|
<div class="table-responsive">
|
|
<table class="table table-hover">
|
|
<thead>
|
|
<tr>
|
|
{{ active_sortable_header('Kunde', 'customer', active_sort, active_order) }}
|
|
{{ active_sortable_header('Lizenz', 'license', active_sort, active_order) }}
|
|
{{ active_sortable_header('IP-Adresse', 'ip', active_sort, active_order) }}
|
|
{{ active_sortable_header('Gestartet', 'started', active_sort, active_order) }}
|
|
{{ active_sortable_header('Letzter Heartbeat', 'last_heartbeat', active_sort, active_order) }}
|
|
{{ active_sortable_header('Inaktiv seit', 'inactive', active_sort, active_order) }}
|
|
<th>Aktion</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for session in active_sessions %}
|
|
<tr class="{% if session[8] > 5 %}session-warning{% else %}session-active{% endif %}">
|
|
<td>{{ session[3] }}</td>
|
|
<td><small><code>{{ session[2][:12] }}...</code></small></td>
|
|
<td>{{ session[4] or '-' }}</td>
|
|
<td>{{ session[6].strftime('%d.%m %H:%M') }}</td>
|
|
<td>{{ session[7].strftime('%d.%m %H:%M') }}</td>
|
|
<td>
|
|
{% if session[8] < 1 %}
|
|
<span class="badge bg-success">Aktiv</span>
|
|
{% elif session[8] < 5 %}
|
|
<span class="badge bg-warning">{{ session[8]|round|int }} Min.</span>
|
|
{% else %}
|
|
<span class="badge bg-danger">{{ session[8]|round|int }} Min.</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
<form method="post" action="/session/end/{{ session[0] }}" style="display: inline;">
|
|
<button type="submit" class="btn btn-sm btn-outline-danger"
|
|
onclick="return confirm('Session wirklich beenden?');">
|
|
⏹️ Beenden
|
|
</button>
|
|
</form>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<small class="text-muted">
|
|
Sessions gelten als inaktiv nach 5 Minuten ohne Heartbeat
|
|
</small>
|
|
{% else %}
|
|
<p class="text-muted mb-0">Keine aktiven Sessions vorhanden.</p>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Beendete Sessions -->
|
|
<div class="card">
|
|
<div class="card-header bg-secondary text-white">
|
|
<h5 class="mb-0">⏸️ Beendete Sessions (letzte 24 Stunden)</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if recent_sessions %}
|
|
<div class="table-responsive">
|
|
<table class="table table-sm">
|
|
<thead>
|
|
<tr>
|
|
{{ ended_sortable_header('Kunde', 'customer', ended_sort, ended_order) }}
|
|
{{ ended_sortable_header('Lizenz', 'license', ended_sort, ended_order) }}
|
|
{{ ended_sortable_header('IP-Adresse', 'ip', ended_sort, ended_order) }}
|
|
{{ ended_sortable_header('Gestartet', 'started', ended_sort, ended_order) }}
|
|
{{ ended_sortable_header('Beendet', 'ended_at', ended_sort, ended_order) }}
|
|
{{ ended_sortable_header('Dauer', 'duration', ended_sort, ended_order) }}
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for session in recent_sessions %}
|
|
<tr class="session-inactive">
|
|
<td>{{ session[3] }}</td>
|
|
<td><small><code>{{ session[2][:12] }}...</code></small></td>
|
|
<td>{{ session[4] or '-' }}</td>
|
|
<td>{{ session[5].strftime('%d.%m %H:%M') }}</td>
|
|
<td>{{ session[6].strftime('%d.%m %H:%M') }}</td>
|
|
<td>
|
|
{% if session[7] < 60 %}
|
|
{{ session[7]|round|int }} Min.
|
|
{% else %}
|
|
{{ (session[7]/60)|round(1) }} Std.
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% else %}
|
|
<p class="text-muted mb-0">Keine beendeten Sessions in den letzten 24 Stunden.</p>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %} |