Logging & Log-Rotation, Zeitzone, Dashboard-UX, Design-Politur

Logging-Infrastruktur (Logs können nach einem Import mit vielen Geräten
schnell vollaufen):
- Vier eigene, rotierte Log-Dateien statt allem in /var/log vermischt:
  Live (poe.sh, jetzt eine einzige durchgehend fortgeschriebene Datei
  statt vorher ein neues rpi-<timestamp>.log pro Dienst-Neustart),
  Änderungen (Spiegel der audit_log-Tabelle), App (Flask-Fehler/
  Warnungen, vorher nur im journalctl-Output), Kea-DHCP (eigene
  Logging-Konfiguration in der generierten kea-dhcp4.conf)
- Kea-DHCP-Log bewusst NICHT im gemeinsamen Verzeichnis: Keas
  AppArmor-Profil erlaubt Schreibzugriff exklusiv auf
  /var/log/kea/kea-dhcp4.log (kein Verzeichnis-Wildcard) -- live als
  "DENIED ... mknod ... .lock" reproduziert; die App nutzt Keas eigenen,
  bereits erlaubten Pfad statt das AppArmor-Profil eines Systemdienstes
  aufzuweichen. logrotate deckt trotzdem beide Verzeichnisse mit einer
  Regel ab
- Neue Log-Seite "Kea-DHCP" (Logs-Bereich, eigenes Recht logs_kea.view)
- Rotation (Intervall täglich/wöchentlich/monatlich, Aufbewahrung als
  Anzahl Rotationen) unter Systemeinstellungen → Logs einstellbar,
  generiert /etc/logrotate.d/poe-manager; copytruncate, da keiner der
  drei schreibenden Prozesse ein Reopen-Signal implementiert
- Einmalige Migration räumt alte, verwaiste rpi-*.log-Dateien auf

Zeitzone (Systemeinstellungen, mit Hostname in einer Kachel "Host"):
setzt die System-Zeitzone per timedatectl, damit Logs/Änderungsverlauf
die tatsächliche lokale Zeit statt UTC zeigen. time.tzset() direkt nach
dem Setzen lässt die bereits laufende poe_web.service-Instanz die neue
Zeitzone sofort übernehmen, ohne Neustart.

Systemeinstellungen neu sortiert: Host + Netzwerkeinstellungen (beide
Host-Konfiguration) nebeneinander, dann Logs, dann Prüfintervall.

Netzwerkeinstellungen: DNS-Anzeige zeigte bisher den lokalen
systemd-resolved-Stub (127.0.0.53) aus /etc/resolv.conf statt der
echten Server -- jetzt über "resolvectl dns <interface>", inkl. Fix
für eine falsch geparste IPv6-Fortsetzungszeile ohne Label, und
gefiltert auf IPv4 (diese Seite verwaltet nur IPv4-Konfiguration, per
IPv6-Router-Advertisement gelernte Server sind hier irreführend).

Dashboard: die vier Stat-Kacheln (Online/Offline/Deaktiviert/Gesamt)
sind jetzt klickbare Filter (blenden alle anderen Abschnitte aus, Klick
auf die aktive Kachel oder "Gesamt" hebt den Filter auf); jeder
Abschnitt lässt sich unabhängig davon per Klick auf seinen Titel ein-/
ausklappen. Beide Zustände bleiben über die automatischen Live-Updates
hinweg erhalten.

Design-Politur:
- Hauptinhaltsbereich nutzt jetzt die volle verfügbare Breite (kein
  Bootstrap-typisches festes max-width mehr) -- Modals bleiben bewusst
  bei ihrer kompakten, festen Breite
  Akzentfarbe exakt auf das Orange aus dem WiS-Logo abgestimmt
  (#ff7100, per Pixel-Sampling ermittelt) statt einer nur ähnlichen,
  frei gewählten Nuance
- Abmelden führt zurück auf das (auch ohne Login einsehbare) Dashboard
  statt auf die Login-Seite

Live gegen die echte Test-VM verifiziert: alle vier Logs werden
geschrieben, logrotate-Konfiguration korrekt generiert, ein
Zeitzonenwechsel (UTC → Europe/Berlin) wirkte sofort, DNS-Anzeige zeigt
jetzt die echten IPv4-Server (1.1.1.1, 8.8.8.8) statt Stub/IPv6,
Dashboard-Filter/-Collapse und Logout-Redirect per Playwright bestätigt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-12 13:30:56 +02:00
co-authored by Claude Sonnet 5
parent d525ef2e0a
commit 7b30ebce7b
9 changed files with 714 additions and 93 deletions
+29 -9
View File
@@ -13,10 +13,13 @@
--text: #eef0f4;
--text-dim: #9aa2b4;
--text-faint: #656d80;
--accent: #ff7a1a;
--accent-strong: #ff8f3d;
--accent-dim: rgba(255, 122, 26, 0.14);
--accent-ring: rgba(255, 122, 26, 0.35);
/* #ff7100 = exakte Farbe aus dem WiS-Logo (static/images/logo.png,
per Pixel-Sampling ermittelt) — Buttons/Akzente sollen genau dieses
Orange treffen statt einer nur ähnlichen, frei gewählten Nuance. */
--accent: #ff7100;
--accent-strong: #ff8d33;
--accent-dim: rgba(255, 113, 0, 0.14);
--accent-ring: rgba(255, 113, 0, 0.35);
--success: #2fd07a;
--success-dim: rgba(47, 208, 122, 0.14);
--danger: #f0475c;
@@ -48,7 +51,7 @@
--text: #1a1d24;
--text-dim: #565d6d;
--text-faint: #8a90a0;
--accent-dim: rgba(255, 122, 26, 0.1);
--accent-dim: rgba(255, 113, 0, 0.1);
--muted-dim: rgba(107, 114, 128, 0.12);
--shadow: 0 10px 30px -14px rgba(20, 20, 30, 0.18);
--shadow-sm: 0 4px 14px -8px rgba(20, 20, 30, 0.14);
@@ -358,7 +361,6 @@ button { font-family: inherit; }
.content {
padding: 24px 28px 48px;
max-width: 1320px;
width: 100%;
}
@@ -387,7 +389,7 @@ button { font-family: inherit; }
.btn-primary {
background: var(--accent);
color: #1a0e03;
box-shadow: 0 6px 18px -6px rgba(255, 122, 26, 0.55);
box-shadow: 0 6px 18px -6px rgba(255, 113, 0, 0.55);
}
.btn-primary:hover { background: var(--accent-strong); }
@@ -471,7 +473,12 @@ button { font-family: inherit; }
border: 1px solid var(--border-soft);
border-radius: var(--radius);
padding: 16px 18px;
cursor: pointer;
user-select: none;
transition: border-color 0.12s ease, background 0.12s ease;
}
.stat-card:hover { background: var(--bg-card-hover); }
.stat-card.active { border-color: var(--accent); background: var(--accent-dim); }
.stat-card .stat-label { font-size: 12px; color: var(--text-faint); font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; }
.stat-card .stat-value { font-size: 26px; font-weight: 700; margin-top: 6px; letter-spacing: -0.02em; }
.stat-card .stat-value.online { color: var(--success); }
@@ -581,6 +588,8 @@ button { font-family: inherit; }
@media (min-width: 720px) { .device-grid { grid-template-columns: repeat(4, 1fr); } }
@media (min-width: 960px) { .device-grid { grid-template-columns: repeat(5, 1fr); } }
@media (min-width: 1200px) { .device-grid { grid-template-columns: repeat(6, 1fr); } }
@media (min-width: 1600px) { .device-grid { grid-template-columns: repeat(8, 1fr); } }
@media (min-width: 2000px) { .device-grid { grid-template-columns: repeat(10, 1fr); } }
.dash-section { margin-bottom: 26px; }
.dash-section-title {
@@ -593,7 +602,18 @@ button { font-family: inherit; }
text-transform: uppercase;
letter-spacing: 0.04em;
margin-bottom: 12px;
cursor: pointer;
user-select: none;
width: fit-content;
}
.dash-section-title:hover { color: var(--text); }
.dash-section-title .chevron {
width: 14px; height: 14px;
transition: transform 0.15s ease;
color: var(--text-faint);
}
.dash-section.collapsed .dash-section-title .chevron { transform: rotate(-90deg); }
.dash-section.collapsed .device-grid { display: none; }
.device-card {
position: relative;
@@ -994,8 +1014,8 @@ select {
align-items: center;
justify-content: center;
background:
radial-gradient(circle at 15% 15%, rgba(255, 122, 26, 0.14), transparent 45%),
radial-gradient(circle at 85% 85%, rgba(255, 122, 26, 0.08), transparent 40%),
radial-gradient(circle at 15% 15%, rgba(255, 113, 0, 0.14), transparent 45%),
radial-gradient(circle at 85% 85%, rgba(255, 113, 0, 0.08), transparent 40%),
var(--bg);
padding: 20px;
}