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>
99 lines
4.1 KiB
HTML
99 lines
4.1 KiB
HTML
<!-- Live-Update-fähiges Fragment: wird von index.html initial eingebunden UND
|
|
unverändert von /dashboard/tiles (AJAX) nachgeladen, damit das Dashboard
|
|
ohne vollen Seiten-Reload aktuell bleibt (siehe dashboard-tiles.js im
|
|
scripts-Block von index.html). -->
|
|
<div id="dashboard-tiles" data-last-run-ms="{{ last_run_epoch_ms or '' }}">
|
|
|
|
<div class="stat-row">
|
|
<div class="stat-card" data-status-filter="online">
|
|
<div class="stat-label">Online</div>
|
|
<div class="stat-value online">{{ stats.online }}</div>
|
|
</div>
|
|
<div class="stat-card" data-status-filter="offline">
|
|
<div class="stat-label">Offline</div>
|
|
<div class="stat-value offline">{{ stats.offline }}</div>
|
|
</div>
|
|
{% if current_user.is_authenticated %}
|
|
<div class="stat-card" data-status-filter="disabled">
|
|
<div class="stat-label">Deaktiviert</div>
|
|
<div class="stat-value disabled">{{ stats.disabled }}</div>
|
|
</div>
|
|
{% endif %}
|
|
<div class="stat-card" data-status-filter="">
|
|
<div class="stat-label">Gesamt</div>
|
|
<div class="stat-value">{{ stats.total }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% macro device_tile(d) %}
|
|
{% set st = status.get(d['mac'], 'unbekannt') %}
|
|
<div class="device-card {% if d['is_active'] == 0 %}is-disabled{% endif %} {% if not current_user.is_authenticated %}is-readonly{% endif %}"
|
|
data-status="{{ 'disabled' if d['is_active'] == 0 else st }}"
|
|
data-mac="{{ d['mac'] }}"
|
|
data-name="{{ d['name'] }}"
|
|
data-ip="{{ d['rpi_ip'] }}"
|
|
data-switch="{{ d['switch_hostname'] or '-' }}"
|
|
data-port="{{ d['port'] or '-' }}"
|
|
data-active="{{ d['is_active'] }}"
|
|
data-checked="{{ last_checked.get(d['mac']) or '-' }}"
|
|
{% if last_seen.get(d['mac']) %}title="{{ last_seen[d['mac']] }}"{% endif %}>
|
|
<div class="dc-top">
|
|
<div class="dc-icon">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="2.5"/><path d="M8 2v3M16 2v3M8 19v3M16 19v3M2 8h3M2 16h3M19 8h3M19 16h3"/></svg>
|
|
</div>
|
|
{% if d['is_active'] == 0 %}
|
|
<span class="pill disabled">Deaktiviert</span>
|
|
{% else %}
|
|
<span class="pill {{ st }}">{{ {'online':'Online','offline':'Offline','unbekannt':'Unbekannt'}[st] }}</span>
|
|
{% endif %}
|
|
</div>
|
|
<div class="dc-name">{{ d['name'] }}</div>
|
|
<div class="dc-ip">{{ d['rpi_ip'] }}</div>
|
|
</div>
|
|
{% endmacro %}
|
|
|
|
{% if device_count %}
|
|
|
|
{% macro section_chevron() %}<svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9l6 6 6-6"/></svg>{% endmacro %}
|
|
|
|
{% if offline_devices %}
|
|
<div class="dash-section" data-status="offline">
|
|
<div class="dash-section-title">{{ section_chevron() }}Offline <span class="pill offline">{{ offline_devices|length }}</span></div>
|
|
<div class="device-grid">
|
|
{% for d in offline_devices %}{{ device_tile(d) }}{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if online_devices %}
|
|
<div class="dash-section" data-status="online">
|
|
<div class="dash-section-title">{{ section_chevron() }}Online <span class="pill online">{{ online_devices|length }}</span></div>
|
|
<div class="device-grid">
|
|
{% for d in online_devices %}{{ device_tile(d) }}{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if disabled_devices %}
|
|
<div class="dash-section" data-status="disabled">
|
|
<div class="dash-section-title">{{ section_chevron() }}Deaktiviert <span class="pill disabled">{{ disabled_devices|length }}</span></div>
|
|
<div class="device-grid">
|
|
{% for d in disabled_devices %}{{ device_tile(d) }}{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<p id="noSearchResults" class="text-faint hidden" style="text-align:center; padding:30px 0;">Kein Gerät gefunden.</p>
|
|
|
|
{% else %}
|
|
<div class="card card-pad" style="text-align:center; color:var(--text-faint);">
|
|
{% if current_user.is_authenticated %}
|
|
Noch keine Geräte angelegt. Füge welche unter <a href="{{ url_for('devices') }}" style="color:var(--accent-strong); font-weight:600;">Devices</a> hinzu.
|
|
{% else %}
|
|
Aktuell keine Geräte verfügbar.
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
</div>
|