Dateien
Hetzner-Backup/v2_adminpanel/templates/audit_log.html
2025-06-18 20:07:32 +02:00

396 Zeilen
23 KiB
HTML
Originalformat Blame Verlauf

Diese Datei enthält mehrdeutige Unicode-Zeichen
Diese Datei enthält Unicode-Zeichen, die mit anderen Zeichen verwechselt werden können. Wenn du glaubst, dass das absichtlich so ist, kannst du diese Warnung ignorieren. Benutze den „Escape“-Button, um versteckte Zeichen anzuzeigen.
{% extends "base.html" %}
{% block title %}Log{% endblock %}
{% macro sortable_header(label, field, current_sort, current_order) %}
<th>
{% if current_sort == field %}
<a href="{{ url_for('admin.audit_log', sort=field, order='desc' if current_order == 'asc' else 'asc', user=filter_user, action=filter_action, entity=filter_entity, page=1) }}"
class="server-sortable">
{% else %}
<a href="{{ url_for('admin.audit_log', sort=field, order='asc', user=filter_user, action=filter_action, entity=filter_entity, page=1) }}"
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>
.audit-details {
font-size: 0.85em;
max-width: 300px;
overflow: hidden;
text-overflow: ellipsis;
}
.json-display {
background-color: #f8f9fa;
padding: 5px;
border-radius: 3px;
font-family: monospace;
font-size: 0.8em;
max-height: 100px;
overflow-y: auto;
}
/* CRUD-Operationen */
.action-CREATE { color: #28a745; }
.action-UPDATE { color: #007bff; }
.action-DELETE { color: #dc3545; }
.action-IMPORT { color: #6f42c1; }
/* Authentifizierung */
.action-LOGIN { color: #17a2b8; }
.action-LOGIN_SUCCESS { color: #28a745; }
.action-LOGIN_FAILED { color: #dc3545; }
.action-LOGIN_BLOCKED { color: #b91c1c; }
.action-LOGOUT { color: #6c757d; }
.action-AUTO_LOGOUT { color: #fd7e14; }
/* 2FA */
.action-LOGIN_2FA_SUCCESS { color: #00a86b; }
.action-LOGIN_2FA_BACKUP { color: #059862; }
.action-LOGIN_2FA_FAILED { color: #e53e3e; }
.action-2FA_ENABLED { color: #38a169; }
.action-2FA_DISABLED { color: #e53e3e; }
/* Lizenzverwaltung */
.action-GENERATE_KEY { color: #20c997; }
.action-CREATE_BATCH { color: #6610f2; }
.action-BATCH_UPDATE { color: #17a2b8; }
.action-TOGGLE { color: #ffc107; }
.action-QUICK_EDIT { color: #00bcd4; }
.action-BULK_ACTIVATE { color: #4caf50; }
.action-BULK_DEACTIVATE { color: #ff5722; }
.action-BULK_DELETE { color: #f44336; }
/* Geräteverwaltung */
.action-DEVICE_REGISTER { color: #2196f3; }
.action-DEVICE_DEACTIVATE { color: #ff9800; }
/* Ressourcenverwaltung */
.action-RESOURCE_ALLOCATE { color: #009688; }
.action-QUARANTINE { color: #ff6b6b; }
.action-RELEASE { color: #51cf66; }
.action-BULK_CREATE { color: #845ef7; }
/* Session-Verwaltung */
.action-SESSION_TERMINATE { color: #e91e63; }
.action-SESSION_TERMINATE_ALL { color: #c2255c; }
.action-SESSION_CLEANUP { color: #868e96; }
/* System-Operationen */
.action-BACKUP { color: #5a67d8; }
.action-BACKUP_DOWNLOAD { color: #4299e1; }
.action-BACKUP_DELETE { color: #e53e3e; }
.action-RESTORE { color: #4299e1; }
.action-EXPORT { color: #ffc107; }
.action-PASSWORD_CHANGE { color: #805ad5; }
.action-UNBLOCK_IP { color: #22b8cf; }
.action-CLEAR_LOGIN_ATTEMPTS { color: #37b24d; }
</style>
{% endblock %}
{% block content %}
<div class="container py-5">
<div class="mb-4">
<h2>📝 Log</h2>
</div>
<!-- Filter -->
<div class="card mb-3">
<div class="card-body">
<form method="get" action="{{ url_for('admin.audit_log') }}" id="auditFilterForm">
<div class="row g-3 align-items-end">
<div class="col-md-3">
<label for="user" class="form-label">Benutzer</label>
<input type="text" class="form-control" id="user" name="user"
placeholder="Benutzername..." value="{{ filter_user }}">
</div>
<div class="col-md-3">
<label for="action" class="form-label">Aktion</label>
<select class="form-select" id="action" name="action">
<option value="">Alle Aktionen</option>
<optgroup label="CRUD-Operationen">
<option value="CREATE" {% if filter_action == 'CREATE' %}selected{% endif %}> Erstellt</option>
<option value="UPDATE" {% if filter_action == 'UPDATE' %}selected{% endif %}>✏️ Bearbeitet</option>
<option value="DELETE" {% if filter_action == 'DELETE' %}selected{% endif %}>🗑️ Gelöscht</option>
<option value="IMPORT" {% if filter_action == 'IMPORT' %}selected{% endif %}>📤 Import</option>
</optgroup>
<optgroup label="Authentifizierung">
<option value="LOGIN" {% if filter_action == 'LOGIN' %}selected{% endif %}>🔑 Anmeldung</option>
<option value="LOGIN_SUCCESS" {% if filter_action == 'LOGIN_SUCCESS' %}selected{% endif %}>✅ Anmeldung erfolgreich</option>
<option value="LOGIN_FAILED" {% if filter_action == 'LOGIN_FAILED' %}selected{% endif %}>❌ Anmeldung fehlgeschlagen</option>
<option value="LOGIN_BLOCKED" {% if filter_action == 'LOGIN_BLOCKED' %}selected{% endif %}>🚫 Login blockiert</option>
<option value="LOGOUT" {% if filter_action == 'LOGOUT' %}selected{% endif %}>🚪 Abmeldung</option>
<option value="AUTO_LOGOUT" {% if filter_action == 'AUTO_LOGOUT' %}selected{% endif %}>⏰ Auto-Logout</option>
</optgroup>
<optgroup label="2FA-Authentifizierung">
<option value="LOGIN_2FA_SUCCESS" {% if filter_action == 'LOGIN_2FA_SUCCESS' %}selected{% endif %}>🔐 2FA-Anmeldung</option>
<option value="LOGIN_2FA_FAILED" {% if filter_action == 'LOGIN_2FA_FAILED' %}selected{% endif %}>⛔ 2FA fehlgeschlagen</option>
<option value="LOGIN_2FA_BACKUP" {% if filter_action == 'LOGIN_2FA_BACKUP' %}selected{% endif %}>🔒 2FA-Backup-Code</option>
<option value="2FA_ENABLED" {% if filter_action == '2FA_ENABLED' %}selected{% endif %}>✅ 2FA aktiviert</option>
<option value="2FA_DISABLED" {% if filter_action == '2FA_DISABLED' %}selected{% endif %}>❌ 2FA deaktiviert</option>
</optgroup>
<optgroup label="Lizenzverwaltung">
<option value="GENERATE_KEY" {% if filter_action == 'GENERATE_KEY' %}selected{% endif %}>🔑 Key generiert</option>
<option value="CREATE_BATCH" {% if filter_action == 'CREATE_BATCH' %}selected{% endif %}>📦 Batch erstellt</option>
<option value="BATCH_UPDATE" {% if filter_action == 'BATCH_UPDATE' %}selected{% endif %}>🔄 Batch Update</option>
<option value="TOGGLE" {% if filter_action == 'TOGGLE' %}selected{% endif %}>🔄 Status geändert</option>
<option value="QUICK_EDIT" {% if filter_action == 'QUICK_EDIT' %}selected{% endif %}>⚡ Schnellbearbeitung</option>
<option value="BULK_ACTIVATE" {% if filter_action == 'BULK_ACTIVATE' %}selected{% endif %}>✅ Bulk-Aktivierung</option>
<option value="BULK_DEACTIVATE" {% if filter_action == 'BULK_DEACTIVATE' %}selected{% endif %}>❌ Bulk-Deaktivierung</option>
<option value="BULK_DELETE" {% if filter_action == 'BULK_DELETE' %}selected{% endif %}>🗑️ Bulk-Löschung</option>
</optgroup>
<optgroup label="Geräteverwaltung">
<option value="DEVICE_REGISTER" {% if filter_action == 'DEVICE_REGISTER' %}selected{% endif %}>📱 Gerät registriert</option>
<option value="DEVICE_DEACTIVATE" {% if filter_action == 'DEVICE_DEACTIVATE' %}selected{% endif %}>📵 Gerät deaktiviert</option>
</optgroup>
<optgroup label="Ressourcenverwaltung">
<option value="RESOURCE_ALLOCATE" {% if filter_action == 'RESOURCE_ALLOCATE' %}selected{% endif %}>📎 Ressource zugewiesen</option>
<option value="QUARANTINE" {% if filter_action == 'QUARANTINE' %}selected{% endif %}>⚠️ In Quarantäne</option>
<option value="RELEASE" {% if filter_action == 'RELEASE' %}selected{% endif %}>✅ Freigegeben</option>
<option value="BULK_CREATE" {% if filter_action == 'BULK_CREATE' %}selected{% endif %}> Bulk-Erstellung</option>
</optgroup>
<optgroup label="Session-Verwaltung">
<option value="SESSION_TERMINATE" {% if filter_action == 'SESSION_TERMINATE' %}selected{% endif %}>🛑 Session beendet</option>
<option value="SESSION_TERMINATE_ALL" {% if filter_action == 'SESSION_TERMINATE_ALL' %}selected{% endif %}>🛑 Alle Sessions beendet</option>
<option value="SESSION_CLEANUP" {% if filter_action == 'SESSION_CLEANUP' %}selected{% endif %}>🧹 Session-Bereinigung</option>
</optgroup>
<optgroup label="System-Operationen">
<option value="BACKUP" {% if filter_action == 'BACKUP' %}selected{% endif %}>💾 Backup</option>
<option value="BACKUP_DOWNLOAD" {% if filter_action == 'BACKUP_DOWNLOAD' %}selected{% endif %}>⬇️ Backup Download</option>
<option value="BACKUP_DELETE" {% if filter_action == 'BACKUP_DELETE' %}selected{% endif %}>🗑️ Backup gelöscht</option>
<option value="RESTORE" {% if filter_action == 'RESTORE' %}selected{% endif %}>🔄 Wiederhergestellt</option>
<option value="EXPORT" {% if filter_action == 'EXPORT' %}selected{% endif %}>📥 Export</option>
<option value="PASSWORD_CHANGE" {% if filter_action == 'PASSWORD_CHANGE' %}selected{% endif %}>🔐 Passwort geändert</option>
<option value="UNBLOCK_IP" {% if filter_action == 'UNBLOCK_IP' %}selected{% endif %}>🔓 IP entsperrt</option>
<option value="CLEAR_LOGIN_ATTEMPTS" {% if filter_action == 'CLEAR_LOGIN_ATTEMPTS' %}selected{% endif %}>🧹 Login-Versuche gelöscht</option>
</optgroup>
</select>
</div>
<div class="col-md-3">
<label for="entity" class="form-label">Entität</label>
<select class="form-select" id="entity" name="entity">
<option value="">Alle Entitäten</option>
<option value="license" {% if filter_entity == 'license' %}selected{% endif %}>Lizenz</option>
<option value="customer" {% if filter_entity == 'customer' %}selected{% endif %}>Kunde</option>
<option value="user" {% if filter_entity == 'user' %}selected{% endif %}>Benutzer</option>
<option value="session" {% if filter_entity == 'session' %}selected{% endif %}>Session</option>
<option value="resource" {% if filter_entity == 'resource' %}selected{% endif %}>Ressource</option>
<option value="backup" {% if filter_entity == 'backup' %}selected{% endif %}>Backup</option>
<option value="database" {% if filter_entity == 'database' %}selected{% endif %}>Datenbank</option>
<option value="system" {% if filter_entity == 'system' %}selected{% endif %}>System</option>
</select>
</div>
<div class="col-md-3">
<div class="d-flex gap-2">
<a href="{{ url_for('admin.audit_log') }}" class="btn btn-outline-secondary">Zurücksetzen</a>
<div class="dropdown">
<button class="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown">
<i class="bi bi-download"></i> Export
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="{{ url_for('export.export_audit', format='excel', user=filter_user, action=filter_action, entity=filter_entity) }}">
<i class="bi bi-file-earmark-excel text-success"></i> Excel Export</a></li>
<li><a class="dropdown-item" href="{{ url_for('export.export_audit', format='csv', user=filter_user, action=filter_action, entity=filter_entity) }}">
<i class="bi bi-file-earmark-text"></i> CSV Export</a></li>
</ul>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
<!-- Audit Log Tabelle -->
<div class="card">
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>Zeitstempel</th>
<th>Benutzer</th>
<th>Aktion</th>
<th>Entität</th>
<th>Details</th>
<th>IP-Adresse</th>
</tr>
</thead>
<tbody>
{% for log in logs %}
<tr>
<td>{{ log.timestamp.strftime('%d.%m.%Y %H:%M:%S') }}</td>
<td><strong>{{ log.username }}</strong></td>
<td>
<span class="action-{{ log.action }}">
{% if log.action == 'CREATE' %} Erstellt
{% elif log.action == 'UPDATE' %}✏️ Bearbeitet
{% elif log.action == 'DELETE' %}🗑️ Gelöscht
{% elif log.action == 'LOGIN' %}🔑 Anmeldung
{% elif log.action == 'LOGOUT' %}🚪 Abmeldung
{% elif log.action == 'AUTO_LOGOUT' %}⏰ Auto-Logout
{% elif log.action == 'EXPORT' %}📥 Export
{% elif log.action == 'GENERATE_KEY' %}🔑 Key generiert
{% elif log.action == 'CREATE_BATCH' %}🔑 Batch erstellt
{% elif log.action == 'BACKUP' %}💾 Backup erstellt
{% elif log.action == 'LOGIN_2FA_SUCCESS' %}🔐 2FA-Anmeldung
{% elif log.action == 'LOGIN_2FA_BACKUP' %}🔒 2FA-Backup-Code
{% elif log.action == 'LOGIN_2FA_FAILED' %}⛔ 2FA-Fehlgeschlagen
{% elif log.action == 'LOGIN_BLOCKED' %}🚫 Login-Blockiert
{% elif log.action == 'RESTORE' %}🔄 Wiederhergestellt
{% elif log.action == 'PASSWORD_CHANGE' %}🔐 Passwort geändert
{% elif log.action == '2FA_ENABLED' %}✅ 2FA aktiviert
{% elif log.action == '2FA_DISABLED' %}❌ 2FA deaktiviert
{% else %}{{ log.action }}
{% endif %}
</span>
</td>
<td>
{{ log.entity_type }}
{% if log.entity_id %}
<small class="text-muted">#{{ log.entity_id }}</small>
{% endif %}
</td>
<td class="audit-details">
{% if log.additional_info %}
<div class="mb-1"><small class="text-muted">{{ log.additional_info }}</small></div>
{% endif %}
{% if log.old_values and log.action == 'DELETE' %}
<details>
<summary>Gelöschte Werte</summary>
<div class="json-display">
{% for key, value in log.old_values.items() %}
<strong>{{ key }}:</strong> {{ value }}<br>
{% endfor %}
</div>
</details>
{% elif log.old_values and log.new_values and log.action == 'UPDATE' %}
<details>
<summary>Änderungen anzeigen</summary>
<div class="json-display">
<strong>Vorher:</strong><br>
{% for key, value in log.old_values.items() %}
{% if log.new_values[key] != value %}
{{ key }}: {{ value }}<br>
{% endif %}
{% endfor %}
<hr class="my-1">
<strong>Nachher:</strong><br>
{% for key, value in log.new_values.items() %}
{% if log.old_values[key] != value %}
{{ key }}: {{ value }}<br>
{% endif %}
{% endfor %}
</div>
</details>
{% elif log.new_values and log.action == 'CREATE' %}
<details>
<summary>Erstellte Werte</summary>
<div class="json-display">
{% for key, value in log.new_values.items() %}
<strong>{{ key }}:</strong> {{ value }}<br>
{% endfor %}
</div>
</details>
{% endif %}
</td>
<td>
<small class="text-muted">{{ log.ip_address or '-' }}</small>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if not logs %}
<div class="text-center py-5">
<p class="text-muted">Keine Audit-Log-Einträge gefunden.</p>
</div>
{% endif %}
</div>
<!-- Pagination -->
{% if total_pages > 1 %}
<nav aria-label="Seitennavigation" class="mt-3">
<ul class="pagination justify-content-center">
<!-- Erste Seite -->
<li class="page-item {% if page == 1 %}disabled{% endif %}">
<a class="page-link" href="{{ url_for('admin.audit_log', page=1, user=filter_user, action=filter_action, entity=filter_entity, sort=sort, order=order) }}">Erste</a>
</li>
<!-- Vorherige Seite -->
<li class="page-item {% if page == 1 %}disabled{% endif %}">
<a class="page-link" href="{{ url_for('admin.audit_log', page=page-1, user=filter_user, action=filter_action, entity=filter_entity, sort=sort, order=order) }}"></a>
</li>
<!-- Seitenzahlen -->
{% for p in range(1, total_pages + 1) %}
{% if p >= page - 2 and p <= page + 2 %}
<li class="page-item {% if p == page %}active{% endif %}">
<a class="page-link" href="{{ url_for('admin.audit_log', page=p, user=filter_user, action=filter_action, entity=filter_entity, sort=sort, order=order) }}">{{ p }}</a>
</li>
{% endif %}
{% endfor %}
<!-- Nächste Seite -->
<li class="page-item {% if page == total_pages %}disabled{% endif %}">
<a class="page-link" href="{{ url_for('admin.audit_log', page=page+1, user=filter_user, action=filter_action, entity=filter_entity, sort=sort, order=order) }}"></a>
</li>
<!-- Letzte Seite -->
<li class="page-item {% if page == total_pages %}disabled{% endif %}">
<a class="page-link" href="{{ url_for('admin.audit_log', page=total_pages, user=filter_user, action=filter_action, entity=filter_entity, sort=sort, order=order) }}">Letzte</a>
</li>
</ul>
<p class="text-center text-muted">
Seite {{ page }} von {{ total_pages }} | Gesamt: {{ total }} Einträge
</p>
</nav>
{% endif %}
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
// Live Filtering für Audit Log
document.addEventListener('DOMContentLoaded', function() {
const filterForm = document.getElementById('auditFilterForm');
const userInput = document.getElementById('user');
const actionSelect = document.getElementById('action');
const entitySelect = document.getElementById('entity');
// Debounce timer für Textfelder
let searchTimeout;
// Live-Filter für Benutzer-Textfeld (mit 300ms Verzögerung)
userInput.addEventListener('input', function() {
clearTimeout(searchTimeout);
searchTimeout = setTimeout(() => {
filterForm.submit();
}, 300);
});
// Live-Filter für Dropdowns (sofort)
actionSelect.addEventListener('change', function() {
filterForm.submit();
});
entitySelect.addEventListener('change', function() {
filterForm.submit();
});
});
</script>
{% endblock %}