391 Zeilen
21 KiB
HTML
391 Zeilen
21 KiB
HTML
{% 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=action_filter, entity=entity_filter, page=1) }}"
|
||
class="server-sortable">
|
||
{% else %}
|
||
<a href="{{ url_for('admin.audit_log', sort=field, order='asc', user=filter_user, action=action_filter, entity=entity_filter, 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>
|
||
<select class="form-select" id="user" name="user">
|
||
<option value="">Alle Benutzer</option>
|
||
<option value="rac00n" {% if filter_user == 'rac00n' %}selected{% endif %}>rac00n</option>
|
||
<option value="w@rh@mm3r" {% if filter_user == 'w@rh@mm3r' %}selected{% endif %}>w@rh@mm3r</option>
|
||
<option value="system" {% if filter_user == 'system' %}selected{% endif %}>System</option>
|
||
</select>
|
||
</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>
|
||
{% for action in actions %}
|
||
<option value="{{ action }}" {% if action_filter == action %}selected{% endif %}>
|
||
{% if action == 'CREATE' %}➕ Erstellt
|
||
{% elif action == 'UPDATE' %}✏️ Bearbeitet
|
||
{% elif action == 'DELETE' %}🗑️ Gelöscht
|
||
{% elif action == 'IMPORT' %}📤 Import
|
||
{% elif action == 'LOGIN' %}🔑 Anmeldung
|
||
{% elif action == 'LOGIN_SUCCESS' %}✅ Anmeldung erfolgreich
|
||
{% elif action == 'LOGIN_FAILED' %}❌ Anmeldung fehlgeschlagen
|
||
{% elif action == 'LOGIN_BLOCKED' %}🚫 Login blockiert
|
||
{% elif action == 'LOGOUT' %}🚪 Abmeldung
|
||
{% elif action == 'AUTO_LOGOUT' %}⏰ Auto-Logout
|
||
{% elif action == 'LOGIN_2FA_SUCCESS' %}🔐 2FA-Anmeldung
|
||
{% elif action == 'LOGIN_2FA_FAILED' %}⛔ 2FA fehlgeschlagen
|
||
{% elif action == 'LOGIN_2FA_BACKUP' %}🔒 2FA-Backup-Code
|
||
{% elif action == '2FA_ENABLED' %}✅ 2FA aktiviert
|
||
{% elif action == '2FA_DISABLED' %}❌ 2FA deaktiviert
|
||
{% elif action == 'GENERATE_KEY' %}🔑 Key generiert
|
||
{% elif action == 'CREATE_BATCH' %}📦 Batch erstellt
|
||
{% elif action == 'BATCH_UPDATE' %}🔄 Batch Update
|
||
{% elif action == 'TOGGLE' %}🔄 Status geändert
|
||
{% elif action == 'QUICK_EDIT' %}⚡ Schnellbearbeitung
|
||
{% elif action == 'BULK_ACTIVATE' %}✅ Bulk-Aktivierung
|
||
{% elif action == 'BULK_DEACTIVATE' %}❌ Bulk-Deaktivierung
|
||
{% elif action == 'BULK_DELETE' %}🗑️ Bulk-Löschung
|
||
{% elif action == 'DEVICE_REGISTER' %}📱 Gerät registriert
|
||
{% elif action == 'DEVICE_DEACTIVATE' %}📵 Gerät deaktiviert
|
||
{% elif action == 'RESOURCE_ALLOCATE' %}📎 Ressource zugewiesen
|
||
{% elif action == 'QUARANTINE' %}⚠️ In Quarantäne
|
||
{% elif action == 'RELEASE' %}✅ Freigegeben
|
||
{% elif action == 'BULK_CREATE' %}➕ Bulk-Erstellung
|
||
{% elif action == 'SESSION_TERMINATE' %}🛑 Session beendet
|
||
{% elif action == 'SESSION_TERMINATE_ALL' %}🛑 Alle Sessions beendet
|
||
{% elif action == 'SESSION_CLEANUP' %}🧹 Session-Bereinigung
|
||
{% elif action == 'BACKUP' %}💾 Backup
|
||
{% elif action == 'BACKUP_DOWNLOAD' %}⬇️ Backup Download
|
||
{% elif action == 'BACKUP_DELETE' %}🗑️ Backup gelöscht
|
||
{% elif action == 'RESTORE' %}🔄 Wiederhergestellt
|
||
{% elif action == 'EXPORT' %}📥 Export
|
||
{% elif action == 'PASSWORD_CHANGE' %}🔐 Passwort geändert
|
||
{% elif action == 'UNBLOCK_IP' %}🔓 IP entsperrt
|
||
{% elif action == 'CLEAR_LOGIN_ATTEMPTS' %}🧹 Login-Versuche gelöscht
|
||
{% else %}{{ action }}
|
||
{% endif %}
|
||
</option>
|
||
{% endfor %}
|
||
</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>
|
||
{% for entity in entities %}
|
||
<option value="{{ entity }}" {% if entity_filter == entity %}selected{% endif %}>
|
||
{% if entity == 'license' %}Lizenz
|
||
{% elif entity == 'customer' %}Kunde
|
||
{% elif entity == 'user' %}Benutzer
|
||
{% elif entity == 'session' %}Session
|
||
{% elif entity == 'resource' %}Ressource
|
||
{% elif entity == 'backup' %}Backup
|
||
{% elif entity == 'database' %}Datenbank
|
||
{% elif entity == 'system' %}System
|
||
{% else %}{{ entity|title }}
|
||
{% endif %}
|
||
</option>
|
||
{% endfor %}
|
||
</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>
|
||
<!-- Export Buttons -->
|
||
<div class="btn-group" role="group">
|
||
<a href="{{ url_for('export.export_audit', format='excel', user=filter_user, action=action_filter, entity=entity_filter) }}" class="btn btn-success btn-sm">
|
||
<i class="bi bi-file-earmark-excel"></i> Excel
|
||
</a>
|
||
<a href="{{ url_for('export.export_audit', format='csv', user=filter_user, action=action_filter, entity=entity_filter) }}" class="btn btn-secondary btn-sm">
|
||
<i class="bi bi-file-earmark-text"></i> CSV
|
||
</a>
|
||
</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=action_filter, entity=entity_filter, 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=action_filter, entity=entity_filter, 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=action_filter, entity=entity_filter, 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=action_filter, entity=entity_filter, 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=action_filter, entity=entity_filter, 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-Dropdown (sofort)
|
||
userInput.addEventListener('change', function() {
|
||
filterForm.submit();
|
||
});
|
||
|
||
// Live-Filter für Dropdowns (sofort)
|
||
actionSelect.addEventListener('change', function() {
|
||
filterForm.submit();
|
||
});
|
||
|
||
entitySelect.addEventListener('change', function() {
|
||
filterForm.submit();
|
||
});
|
||
});
|
||
</script>
|
||
{% endblock %} |