Audit-Log, Import/Export, eigenes Profil & anpassbare Navigation
- Änderungslog: audit_log-Tabelle + last_modified_by/at an Devices und Switches; log_action()/touch_record() in allen Verwaltungsrouten (Devices, Switches, Zugangsdaten, Benutzer, Gruppen, Settings) verdrahtet. Neue Admin-Seite "Änderungslog" unter /logs/aenderungen. PoE-Neustarts werden bewusst nicht geloggt. - Verschlüsseltes Import/Export für Devices/Switches/Zugangsdaten unter Settings, passphrasenbasiert (PBKDF2 + Fernet), für Umzug auf neue Umgebungen. Referenziert Zugangsdaten/Switche über Name/Hostname statt interner ID für stabilen Re-Import. - Eigenes Profil: Klick auf den Namen in der Sidebar öffnet ein Modal zum Ändern von Vor-/Nachname, eigenem Passwort (mit Prüfung des aktuellen Passworts) und Profilbild-Upload (Anzeige in Sidebar + Änderungslog). - Anpassbare Navigation: Reihenfolge der Sidebar-Punkte ist unter Settings per Auf-/Ab-Buttons konfigurierbar (settings.nav_order); "Devices"/ "Users"/"Settings" umbenannt zu "Geräte"/"Benutzer"/"Einstellungen"; "Live-Log" und "Änderungslog" zu aufklappbarer "Logs"-Gruppe zusammengefasst. Jeder Benutzer sieht weiterhin nur, wofür er berechtigt ist. - create_db.py mit allen Schema-Erweiterungen synchronisiert (audit_log, last_modified_by/at, avatar_filename) für Frischinstallationen. - venv-Umgebung im WSL neu aufgesetzt (war fälschlich unter dem Erstellungspfad venv-linux verankert und daher nicht aktivierbar). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,41 @@
|
||||
{% extends "base.html" %}
|
||||
{% set active_page = "settings" %}
|
||||
{% block page_title %}Settings{% endblock %}
|
||||
{% block page_title %}Einstellungen{% endblock %}
|
||||
{% block page_sub %}<div class="topbar-sub">Prüfintervall für das Monitoring</div>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card card-pad" style="max-width:420px;">
|
||||
<div class="section-head" style="margin-bottom:16px;">
|
||||
<div>
|
||||
<h2 style="font-size:16px;">Navbar-Reihenfolge</h2>
|
||||
<div class="hint">Bestimmt, in welcher Reihenfolge die Menüpunkte in der Sidebar erscheinen. Jeder Benutzer sieht davon nur, wofür er auch berechtigt ist.</div>
|
||||
</div>
|
||||
</div>
|
||||
<form method="post" action="{{ url_for('save_nav_order') }}" id="navOrderForm">
|
||||
<ul class="nav-order-list" id="navOrderList">
|
||||
{% for item in nav_items_ordered %}
|
||||
<li data-key="{{ item.key }}">
|
||||
<span>{{ item.label }}</span>
|
||||
<div class="nav-order-actions">
|
||||
<button type="button" class="icon-btn" title="Nach oben" onclick="moveNavItem(this,-1)">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19V5M5 12l7-7 7 7"/></svg>
|
||||
</button>
|
||||
<button type="button" class="icon-btn" title="Nach unten" onclick="moveNavItem(this,1)">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M5 12l7 7 7-7"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<input type="hidden" name="nav_order" value="{{ item.key }}">
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<button type="submit" class="btn btn-primary btn-block">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>
|
||||
Reihenfolge speichern
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card card-pad" style="max-width:420px; margin-top:24px;">
|
||||
<div class="section-head" style="margin-bottom:16px;">
|
||||
<div>
|
||||
<h2 style="font-size:16px;">Prüfintervall</h2>
|
||||
@@ -23,4 +54,60 @@
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card card-pad" style="max-width:420px; margin-top:24px;">
|
||||
<div class="section-head" style="margin-bottom:16px;">
|
||||
<div>
|
||||
<h2 style="font-size:16px;">Export</h2>
|
||||
<div class="hint">Geräte, Switche und Zugangsdaten als verschlüsseltes Bundle sichern — z.B. für einen Umzug auf eine neue Umgebung.</div>
|
||||
</div>
|
||||
</div>
|
||||
<form method="post" action="{{ url_for('export_data') }}">
|
||||
<div class="field">
|
||||
<label for="export_passphrase">Passphrase</label>
|
||||
<input type="password" name="export_passphrase" id="export_passphrase" required>
|
||||
<div class="field-hint">Wird zum Verschlüsseln der Export-Datei benötigt — für den späteren Import dieselbe Passphrase erneut eingeben.</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-secondary btn-block">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><path d="M7 10l5 5 5-5"/><path d="M12 15V3"/></svg>
|
||||
Export herunterladen
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card card-pad" style="max-width:420px; margin-top:24px;">
|
||||
<div class="section-head" style="margin-bottom:16px;">
|
||||
<div>
|
||||
<h2 style="font-size:16px;">Import</h2>
|
||||
<div class="hint">Ein zuvor exportiertes Bundle einlesen. Bestehende Einträge mit gleichem Namen/Hostname/MAC werden aktualisiert, neue werden angelegt.</div>
|
||||
</div>
|
||||
</div>
|
||||
<form method="post" action="{{ url_for('import_data') }}" enctype="multipart/form-data"
|
||||
data-confirm="Import wirklich starten? Bestehende Einträge mit gleichem Namen/Hostname/MAC werden überschrieben.">
|
||||
<div class="field">
|
||||
<label for="import_file">Export-Datei</label>
|
||||
<input type="file" name="import_file" id="import_file" accept=".json" required>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="import_passphrase">Passphrase</label>
|
||||
<input type="password" name="import_passphrase" id="import_passphrase" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-secondary btn-block">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 9V5a2 2 0 00-2-2H5a2 2 0 00-2 2v4"/><path d="M7 14l5-5 5 5"/><path d="M12 9v12"/></svg>
|
||||
Import starten
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
function moveNavItem(btn, dir) {
|
||||
const li = btn.closest("li");
|
||||
const target = dir === -1 ? li.previousElementSibling : li.nextElementSibling;
|
||||
if (!target) return;
|
||||
if (dir === -1) li.parentNode.insertBefore(li, target);
|
||||
else li.parentNode.insertBefore(target, li);
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user