Commit Graph
127 Commits
Author SHA1 Message Date
alientim 357bb4caa5 Fix: iputils-ping fehlte in install.sh-Paketliste
Auf der Test-VM war 'ping' nicht vorhanden (Minimal-Ubuntu-Image liefert
es nicht immer vor) -- fuer die Erreichbarkeitspruefung der Geraete
(Pruefintervall / rpi-check-Dienst) zwingend noetig. Live auf dem
Testsystem nachinstalliert und verifiziert.
2026-08-11 13:30:45 +02:00
alientim 3195df0fd3 Fix: nginx blockierte SSH-Terminal-WebSocket + DHCP-Stop bei Netzwerkaenderung
Auf der frisch installierten Test-VM zeigten sich zwei weitere reale Probleme:

1. "Verbindung testen" im Switch-Modal blieb dauerhaft auf "Getrennt"
   haengen, sobald ueber nginx (Port 80) statt direkt gegen Flask (Port
   5000) zugegriffen wurde. Ursache: etc/nginx/sites-available/poe_manager
   reichte den "Upgrade"/"Connection"-Header fuer /ws/ssh_terminal nicht
   durch -- Flask/Werkzeug lehnte den WebSocket-Handshake dadurch mit
   400 Bad Request ab, statt mit 101 Switching Protocols zu antworten.
   Live reproduziert und verifiziert (curl-Handshake direkt vs. durch
   nginx). Alle bisherigen Terminal-Tests dieser Session liefen ausschliesslich
   direkt gegen Flask, nie durch nginx, daher bis jetzt unentdeckt.
   Fix: eigener location /ws/ Block mit Upgrade-Map (Standard-nginx-Pattern)
   und grosszuegigen Timeouts fuer die lang offene Terminal-Verbindung.

2. Der Kea-DHCP-Server lief nach einer Aenderung der Host-Netzwerkkonfiguration
   unveraendert weiter, obwohl die konfigurierte Range nach einem
   IP-/Subnetzwechsel nicht mehr zum neuen Netz passen muss. Neue Funktion
   _stop_dhcp_service_if_active() stoppt Kea jetzt sowohl beim Anwenden
   als auch beim Auto-Rollback einer Netzwerkaenderung -- bewusst nur
   stoppen statt automatisch neu zu starten, die Range muss der Admin
   aktiv auf der DHCP-Seite pruefen und den Dienst dort bewusst wieder
   starten (gleiche Philosophie wie beim Rest der DHCP-Dienststeuerung).

Beide Fixes live auf der Test-VM verifiziert (WebSocket-Handshake per
curl, Netzwerk-Reapply ohne Fehler im journalctl-Log trotz nicht
installiertem Kea).
2026-08-11 13:29:02 +02:00
alientim 2b5d9d1b48 Netzwerkeinstellungen: netplan/systemd-networkd als drittes Backend + DHCP-Interface-Default-Fix
Auf einer echten, frisch installierten Ubuntu-24.04-VM (192.168.80.114,
Standard-Setup, Netz per DHCP) zeigten sich zwei reale Bugs:

1. Systemeinstellungen > Netzwerkeinstellungen: 'Kein unterstütztes
   Backend erkannt' trotz laufendem Netz -- _detect_network_backend()
   kannte nur NetworkManager und dhcpcd, nicht netplan/systemd-networkd,
   den mit Abstand haeufigsten Fall auf einem normalen Ubuntu-Host.
   Neues drittes Backend 'netplan' ergaenzt: Erkennung ueber
   systemd-networkd + vorhandenes netplan-Binary, Moduserkennung ueber
   das 'dynamic'-Flag des Kernels auf der aktuellen Adresse, Anwenden
   ueber eine eigene Override-Datei (/etc/netplan/90-poe-manager.yaml,
   gewinnt alphabetisch gegen z.B. cloud-inits 50-*.yaml) plus
   Aufruf von netplan apply, Backup/Auto-Rollback wie bei den anderen
   Backends.
   Live verifiziert: Anwenden loeste echten DHCP-Lease-Renew aus, nach
   Ablauf der 45s-Bestaetigungsfrist wurde die Override-Datei automatisch
   wieder entfernt (Rollback funktioniert).

2. DHCP-Konfiguration: 'Keine IPv4-Adresse auf eth0 gefunden', obwohl
   das Interface-Dropdown korrekt 'enp1s0' zeigte -- DHCP_DEFAULTS hatte
   'eth0' hart kodiert (Annahme aus einer aelteren Testumgebung), was auf
   Systemen mit Predictable Network Interface Names schlicht nicht
   existiert. _load_dhcp_settings() korrigiert das jetzt: liegt der
   gespeicherte/Default-Wert nicht unter den tatsaechlich vorhandenen
   Schnittstellen, wird automatisch die erste echte vorgeschlagen.

Neue Abhaengigkeit PyYAML fuer das Schreiben der netplan-Override-Datei.
2026-08-11 13:04:43 +02:00
alientim 9e23243872 Fix: rsync fehlte in install.sh-Paketliste (Deploy-Schritt braucht es)
Live auf frischem Testsystem (Ubuntu 24.04, 192.168.80.114) verifiziert:
Skript brach beim Kopieren nach /srv/poe_manager mit 'rsync: command
not found' ab, weil rsync nirgends in der apt-get-install-Zeile stand.
2026-08-11 12:35:40 +02:00
alientimandClaude Sonnet 5 4d7433e832 Fix: SSH-Terminal-Login (No auth methods), kaputtes DOM in Bearbeiten-Modals
Quelltext-Huerde wieder entfernt (auf Wunsch) - siehe vorherigen Commit,
hier nur der Revert von initSourceProtection() und der README-Erwaehnung.

SSH-Terminal-Login war komplett kaputt (von dir gemeldet + Screenshot):
- Root Cause: das Init-Payload vom Browser enthielt nie ein Passwort,
  SSHClient.connect() bekam also weder Passwort noch Key noch Agent und
  scheiterte sofort mit "No authentication methods available" - noch
  bevor ueberhaupt eine interaktive Passwortabfrage moeglich gewesen
  waere (die High-Level-API erledigt Host-Key-Pruefung UND
  Authentifizierung in einem blockierenden Aufruf).
- Fix: Umstieg auf die Low-Level paramiko.Transport-API. Nach
  Host-Key-Bestaetigung wird aktiv erfragt, welche Auth-Methoden der
  Server anbietet (auth_none), und bei Bedarf interaktiv ueber das
  Browser-Terminal nach Passwort/keyboard-interactive-Prompts gefragt
  -- genau das Verhalten, das der bestehende Hinweistext im Modal schon
  immer versprach, aber nie tatsaechlich implementiert war.
  Host-Key-Verifikation dabei manuell nachgebaut (_verify_host_key_interactive)
  inkl. hartem Ablehnen bei GEAENDERTEM (nicht nur unbekanntem) Host-Key,
  wie ein echtes ssh-CLI bei einer moeglichen MITM-Situation.
- Waehrend der Live-Verifikation gegen ein echtes Geraet zwei weitere
  Bugs gefunden und gefixt: ws.receive() wirft in diesem Setup
  ConnectionClosed statt None zurueckzugeben (crashte
  _terminal_read_line unbehandelt -> "Invalid frame header" beim
  Client); _send_and_close() crashte ebenso, wenn der Client bereits weg
  war. Beide jetzt defensiv abgefangen.
- Live gegen ein echtes Zielgeraet verifiziert (Host-Key-Bestaetigung,
  Passwort-Prompt, erfolgreicher Login) sowie manuell von dir bestaetigt.

Kaputtes DOM in zwei Bearbeiten-Modals (von dir gemeldet: "Verbindung
testen" oeffnete beim Switch bearbeiten kein Fenster, obwohl es beim
Neuanlegen funktionierte):
- Root Cause: <div class="modal-overlay">...</div> stand direkt in
  <tbody> (nur <tr> ist dort gueltig). Browser "foster-parenten"
  ungueltigen Tbody-Inhalt aus der Tabelle heraus und zerreissen dabei
  teils die Eltern-Kind-Beziehung zwischen <form> und seinen Buttons --
  this.closest("form") lieferte dadurch null statt des Formulars.
  Betroffen: editSwitchModal (switches.html), deviceOptionsModal
  (settings_dhcp.html). Fix: beide Modal-Bloecke aus der Tabelle heraus
  in eine eigene Schleife direkt danach verschoben (gleiches Muster wie
  die bereits korrekten Neuanlegen-Modals).
- Per DOM-Inspektion verifiziert: this.closest("form") lieferte vorher
  null, danach das korrekte Formular fuer alle Zeilen; End-to-End-Test
  bestaetigt, dass sich das Terminal-Modal jetzt oeffnet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-11 12:00:22 +02:00
alientimandClaude Sonnet 5 6803929140 Hostname-Einstellung, DHCP-Range-Pflicht, Topbar-Ampel, Text-Straffung, Quelltext-Huerde
Hostname (Systemeinstellungen, neben Pruefintervall):
- hostnamectl set-hostname ueber die App, Validierung (RFC-1123-artiges
  Label), kein Revert-Timer noetig (kappt die Erreichbarkeit nicht wie
  eine IP-Aenderung). Live getestet inkl. Validierung und Revert.

DHCP: Range ist jetzt Pflicht, kein geratener Default mehr:
- dhcp_range_start/end/dns starten leer (Platzhaltertext statt fake-
  echt aussehendem Default) - ein Zufalls-Range haette den Dienst sonst
  unbemerkt mit einer zum echten Netz nicht passenden Konfiguration
  starten lassen koennen.
- write_dhcp_file und dhcp_service_action=enable_restart verweigern sich
  ohne eingetragene Range; _render_kea_config laesst "pools" ohne Range
  komplett weg statt einen kaputten Pool-String zu erzeugen.
- Neues, separates dhcp_gateway-Feld (optional) fuer einen vom
  automatisch erkannten Gateway abweichenden Router fuer die Clients.

Topbar-Ampel fuer den Kea-Dienst:
- Gruener/roter Punkt neben dem Pruefintervall-Timer, nur sichtbar mit
  settings_dhcp.view (ein einzelner, kurzer systemctl-Aufruf pro Request,
  nicht die volle Status-Erkennung). Live verifiziert (rot wenn gestoppt,
  gruen wenn gestartet).

Reservierungstabelle zeigt jetzt pro Client, welche eigene DHCP-Option
greift (global oder Client-Override, mit Wert im Tooltip).

Text-Straffung: die laengsten Hint-Texte und Code-Kommentare in
Templates/app.py gekuerzt (u.a. groups.html, settings_dhcp.html,
account.html, devices/switches/users/credentials.html, zwei grosse
Migrations-/DHCP-Kommentarbloecke in app.py) - Kernaussagen erhalten,
Redundanz entfernt.

Quelltext-Huerde (KEINE echte Sicherheit, nur Abschreckung): Rechtsklick
und DevTools-/Quelltext-Shortcuts per JS blockiert. Klar dokumentiert
in Kommentar + README, dass der Browser HTML/CSS/JS immer vollstaendig
ausliefert und das in Sekunden umgehbar ist - echte Absicherung bleiben
ausschliesslich die serverseitigen Rechteprüfungen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-11 11:28:29 +02:00
alientimandClaude Sonnet 5 82edf7628b Neu: Netzwerkeinstellungen des Hosts unter Systemeinstellungen (IP/DNS/DHCP)
Bisher enthielt "Systemeinstellungen" nur das Pruefintervall. Jetzt
zusaetzlich Verwaltung der Netzwerkkonfiguration DIESES Hosts (getrennt
vom Kea-DHCP-*Server* fuer Clients):

- Backend-Erkennung (NetworkManager vs. dhcpcd via systemctl is-active).
  Kein erkanntes Backend -> Seite bleibt rein lesend statt zu raten.
- Status-Anzeige: Interface, IP/Prefix, Gateway, DNS, Modus (DHCP/Statisch).
- Aenderung: Interface, Modus, bei Statisch IP/Prefix/Gateway, DNS
  unabhaengig von Modus setzbar (IP per DHCP + feste DNS moeglich).
- Sicherheitsnetz analog "netplan try": Backup vor jeder Aenderung,
  automatisches Rollback nach 45s ohne explizite Bestaetigung (nur
  moeglich, wenn die Seite ueber die neue Config noch erreichbar ist) --
  verhindert Aussperren durch einen Tippfehler bei IP/Gateway.
- Bugfix waehrend der Implementierung gefunden: der Auto-Revert-Timer
  laeuft in einem Hintergrund-Thread ohne Request-Kontext: log_action()
  griff auf current_user zu und warf dort einen AttributeError (die
  Config wurde trotzdem korrekt zurueckgerollt, nur der Audit-Log-Eintrag
  fehlte und ein Fehler landete im Server-Log). Fix: log_action_system()
  ohne current_user-Abhaengigkeit fuer Code ausserhalb des Request-
  Kontexts.
- Getestet: Backend-Erkennung + rein lesender Fallback live (WSL hat
  weder NetworkManager noch dhcpcd aktiv, korrekt erkannt), Rechte-Gating,
  Anwenden-/Rollback-Logik fuer beide Backends per gemocktem
  subprocess.run verifiziert (inkl. echtem dhcpcd.conf-Rewrite und
  vollständigem Anwenden-dann-Auto-Rollback-Durchlauf mit Audit-Log-
  Eintrag).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-11 10:50:33 +02:00
alientimandClaude Sonnet 5 197af26d72 Navbar-Reihenfolge: Unterpunkte je Gruppe editierbar, Karte unter Profil/Passwort verschoben
- Neuer Settings-Schluessel nav_child_order (JSON dict group_key ->
  geordnete Liste der Kind-Keys), analog zu nav_order fuer die Top-Level-
  Reihenfolge. _ordered_nav_items() liefert die komplette, ungefilterte
  Navbar-Struktur inkl. angewandter Kind-Reihenfolge; inject_nav() filtert
  das anschliessend weiterhin nach Berechtigung wie bisher.
- save_nav_order() verarbeitet zusaetzlich ein Feld
  nav_child_order_<group_key> pro Gruppe mit Kindern.
- account.html: Navbar-Reihenfolge-Karte jetzt UNTER Profil/Passwort
  aendern (vorher darueber), zeigt die komplette Navbar inkl.
  eingerueckter, separat sortierbarer Unterpunkte pro Gruppe
  (verschachtelte <ul>, moveNavItem() bewegt automatisch nur innerhalb
  der eigenen Gruppe dank DOM-Nesting).
- Live per Playwright verifiziert: Zugangsdaten innerhalb Geraete nach
  oben verschoben, gespeichert, Sidebar zeigt danach tatsaechlich die
  neue Reihenfolge (Zugangsdaten vor Clients/Switche).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-11 10:34:31 +02:00
alientimandClaude Sonnet 5 9a390e740d Gruppen-Dropdown: kompaktere Rechtetabelle, Modal bleibt unveraendert
- permission_tree() bekommt einen dritten Parameter compact=false, der
  nur im Dropdown der Hauptseite (Admin-/System-/editierbare Gruppen)
  auf true gesetzt wird, nicht im "Neue Gruppe"-Modal.
- .permission-groups-row--compact verkleinert Zeilen-/Spalten-Padding
  und die Einrueckung der Unterpunkte (32px statt 48px) NUR fuer diese
  Instanzen - das Modal behaelt exakt sein bisheriges Aussehen.
- Modal und Dropdown nutzen weiterhin denselben Code/dieselbe Struktur
  (permission_table()-Macro, Kill-Switch-Logik, JS-Gating), nur die
  Abstaende unterscheiden sich ueber die Zusatzklasse.
- Verifiziert per Screenshot: Dropdown jetzt sichtbar kompakter (engere
  Zellen, kuerzere Zeilen), Modal unveraendert; Gruppen anlegen-mit-
  Rechten und Gruppen-Bearbeiten-Regression funktionieren weiterhin.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-11 10:25:13 +02:00
alientimandClaude Sonnet 5 3d17728e75 Alle Anlegen-Modals einheitlich auf max-width:1000px
- addGroupModal, addCredentialModal, addOptionModal (DHCP), addSwitchModal,
  deviceModal (Gerät anlegen), userModal (Benutzer anlegen) — bisher
  unterschiedliche/keine explizite Breite, jetzt einheitlich 1000px für
  ein konsistentes Erscheinungsbild. Bearbeiten-/Zuweisen-Modals bleiben
  bei ihrer bisherigen (kleineren) Breite, da dort keine so breite
  Rechtetabelle wie bei Neue Gruppe eingebettet ist.
- Live verifiziert: Modal-Breite tatsächlich 1000px (vorher lieferte ein
  vergessener Flask-Neustart fälschlich 900px trotz bereits geänderter
  Vorlage — Erinnerung: Template-Änderungen brauchen ohne
  TEMPLATES_AUTO_RELOAD einen Neustart, anders als CSS/JS).
- Gruppen-Rechtetabelle bei 1440px (typische Breite dieser App) identisch
  zum Neue-Gruppe-Modal nebeneinander, ohne Umbruch.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-11 10:05:58 +02:00
alientimandClaude Sonnet 5 bb3a6ab3a5 Gruppen-Rechtetabelle: Einrückung der Unterpunkte auf 48px erhöht
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-11 09:53:27 +02:00
alientimandClaude Sonnet 5 7d7239e1af Fix: Benutzer-Löschen im Browser kaputt (JS), Gruppen-Rechtetabelle überarbeitet
Bugfix (von dir gemeldet, live auf Port 5000 gefunden):
- initConfirmables() in app.js rief beim bestätigten Absenden
  form.requestSubmit() OHNE den ursprünglichen Submitter auf. Laut Spec
  wird dann KEIN Submit-Button-Name/Value mitgeschickt. users.html trug
  die Benutzer-ID als name/value des Löschen-Buttons selbst (statt eines
  verstecken Inputs wie überall sonst) — dadurch kam "delete_user" nie
  im Request an, der Klick tat scheinbar nichts. Fix: e.submitter beim
  ersten Klick merken und an requestSubmit(submitter) weiterreichen
  (behebt es grundsätzlich für alle Formulare dieser Art); zusätzlich
  users.html auf das robustere hidden-input-Muster umgestellt wie
  credentials/devices/groups. Mit echtem Playwright-Browserklick
  (nicht nur curl) verifiziert.

Gruppen-Rechtetabelle, mehrere Korrekturrunden nach deinem Mockup:
- Bereichsname + Kill-Switch-Kästchen jetzt eng nebeneinander in der
  Tabellen-Kopfzeile (nicht mehr an den Rand gestellt), R/W/E/D-Spalten
  in derselben Zeile statt einer zweiten.
- Nur die von einem Bereich tatsächlich genutzten Spalten werden gezeigt
  (GROUP_ROW_TYPES) — Logs zeigt nur R, Einstellungen nur R/W/E, nur
  Geräte zeigt zusätzlich D.
- Bugfix: D-Spalte bei Geräte verschwand durch zu schmale Flex-Breite der
  Spalte im Overflow, obwohl serverseitig korrekt gerendert — behoben
  durch flex:0 0 auto statt fester Breite.
- Checkbox-Zentrierung robust per display:block+margin:auto statt sich
  auf native Checkbox-Ränder zu verlassen (waren leicht asymmetrisch).
- Unterpunkte deutlich weiter eingerückt (34px) für klare Hierarchie.
- Sichtbares Ausgrauen (nicht nur natives disabled) der Unterpunkte,
  solange der Bereich-Kill-Switch aus ist (JS setzt .permission-locked).
- Legende jetzt UNTER der Rechtetabelle statt darüber.
- Rechte lassen sich jetzt direkt beim Anlegen einer neuen Gruppe setzen
  (Modal zeigt dieselbe Rechtetabelle, add_group-Route verarbeitet
  mitgesendete permissions[] im selben Request) statt sie erst danach
  über die Gruppentabelle nachzupflegen.

Alles inkl. Erstellen-mit-Rechten und Bearbeiten-Regression live per
Playwright verifiziert.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-11 09:52:01 +02:00
alientimandClaude Sonnet 5 8e4a874943 DHCP: Subnet/Netzmaske/Gateway immer aus System-Netzwerkkonfiguration ableiten
- Neue Helfer _list_network_interfaces() (echte Interfaces aus
  /sys/class/net) und _detect_interface_network() (IPv4/Prefix per
  "ip addr show", Gateway per "ip route show default") — Subnet/
  Netzmaske/Router werden dadurch bei jeder Anzeige/Generierung live vom
  System gelesen statt manuell gepflegt zu werden.
- dhcp_subnet/dhcp_netmask als manuelle Settings entfernt; Interface ist
  jetzt ein Dropdown mit den tatsächlich vorhandenen Interfaces statt
  Freitext, serverseitig zusätzlich gegen die echte Liste validiert.
- _dhcp_reservation_candidates()/_render_kea_config() nehmen jetzt das
  erkannte Netz (ipaddress.IPv4Network) bzw. net_info entgegen statt
  Subnet/Netzmaske aus der Konfiguration zu lesen; Router kommt vom
  erkannten Gateway (Fallback: erster DNS-Eintrag, wie zuvor).
- UI zeigt das erkannte Netz (IP/Prefix, Subnet, Gateway) read-only an;
  Schreiben/Vorschau werden blockiert bzw. liefern eine leere
  Reservierungsliste, wenn die Erkennung fehlschlägt, statt eine mit
  Sicherheit falsche Konfiguration zu erzeugen.
- Live getestet: Erkennung liefert korrekt das tatsächliche WSL-NAT-Netz
  (172.25.64.0/20) samt Gateway; generierte Config besteht kea-dhcp4 -t;
  ein Testgerät mit IP im erkannten Netz erscheint korrekt als
  Reservierung, Geräte außerhalb werden weiterhin sauber übersprungen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-11 08:28:11 +02:00
alientimandClaude Sonnet 5 c3d69a9870 DHCP: Backend auf Kea umgestellt, eigene Options (global + pro Client), echte Install-/Dienststeuerung
- Backend-Wechsel von isc-dhcp-server (seit 2022 EOL) auf Kea DHCP
  (kea-dhcp4-server) — aktiv weiterentwickelt, bildet "globaler Wert, pro
  Client überschreibbar" nativ über Kea-Reservation-Option-Data ab.
- Neue Tabellen dhcp_option_defs/dhcp_option_values: eigene/herstellerspezifische
  Options mit Code/Name/Typ/Beschreibung, Wert global sowie optional pro
  Client (device_mac="" für global, siehe Kommentar zur SQLite-NULL-
  UNIQUE-Falle). UI dafür: Optionen-Tabelle + "Neue Option"-Modal +
  Pro-Client-Overrides-Modal je Reservierung.
- Kea-JSON-Generator (_render_kea_config): option-def für jede eigene
  Option, globale Werte im Top-Level option-data, Client-Overrides im
  option-data der jeweiligen Reservierung.
- Reservierungen werden jetzt zusätzlich auf Zugehörigkeit zum
  konfigurierten Subnet gefiltert (_dhcp_reservation_candidates) — beim
  Live-Test gegen echtes Kea gefunden: Kea lehnt Reservierungen außerhalb
  ihres Subnets als Konfigurationsfehler ab, das muss also schon bei der
  Generierung berücksichtigt werden statt erst beim Laden zu crashen.
- Neue, einzeln bestätigte Aktionen: "kea-dhcp4-server installieren"
  (apt-get), "Aktivieren & (neu) starten" sowie "Stoppen"
  (systemctl) — bewusst getrennt von "Konfiguration speichern"/"In
  Datei schreiben".
- Live gegen eine echte, frisch installierte Kea-3.0.3-Instanz verifiziert:
  Installation erfolgreich, generierte Config besteht "kea-dhcp4 -t",
  Dienst übernimmt sie beim Neustart fehlerfrei (DHCP4_CONFIG_COMPLETE),
  globaler Options-Wert und Client-Override erscheinen korrekt getrennt.
  Danach wieder gestoppt; Paket bleibt installiert.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 21:47:54 +02:00
alientimandClaude Sonnet 5 9eb9cfdda6 Neu: DHCP-Reservierungen aus Client-Stammdaten (Systemeinstellungen -> DHCP)
- Neue Unterseite unter Einstellungen (eigenes Rechtepaar
  settings_dhcp.view/settings_dhcp.edit, in PERMISSIONS/NAV_ITEMS/
  _nav_key_visible integriert, Kill-Switch über settings_group.view
  greift wie bei den anderen Einstellungen-Unterpunkten).
- Rein lesende Installations-/Status-Erkennung (shutil.which("dhcpd"),
  systemctl is-active isc-dhcp-server) — die App installiert/startet nie
  selbst einen DHCP-Dienst, sondern zeigt bei fehlender Installation den
  passenden manuellen Befehl an.
- Konfigurierbare Netzwerkparameter (Interface/Subnet/Netzmaske/Range/
  DNS/Domain/Lease-Zeiten/Ausgabepfad), gespeichert als dhcp_*-Schlüssel
  in der bestehenden settings-Tabelle.
- Reservierungen werden aus aktiven Geräten mit gültiger MAC+IP generiert
  (Hostname aus Gerätename abgeleitet, Kollisionen automatisch
  durchnummeriert, Geräte ohne MAC/IP werden übersprungen).
- "In Datei schreiben" (nur mit settings_dhcp.edit) schreibt eine
  separate Include-Datei statt der aktiven dhcpd.conf; kein automatischer
  Dienst-Reload/-Restart durch die App.
- Live getestet: Status-Erkennung, Config speichern, Reservierungs-
  Generierung inkl. Namenskollisionen, Datei-Schreiben in sicheren
  Testpfad, View-only-Gating (Formular ausgeblendet, POST blockiert),
  Kill-Switch über settings_group.view.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 21:10:05 +02:00
alientimandClaude Sonnet 5 06db4c1382 Switche: optionaler SSH-Port statt fest 22
- Neue Spalte switches.ssh_port (nullable INTEGER), Migration für
  bestehende DBs in _ensure_schema(), Feld in create_db.py ergänzt.
- Formular (Anlegen/Bearbeiten): optionales SSH-Port-Feld, Validierung
  1-65535, leer -> NULL (= Standard 22 überall, SWITCH_DEFAULT_SSH_PORT).
  Switch-Liste zeigt den effektiven Port inkl. "(Standard)"-Hinweis.
- generate_ips.py liefert den effektiven Port (Fallback 22) als eigenes
  Pipe-Feld an poe.sh; poe.sh übernimmt es in disable_poe/enable_poe und
  reicht es als "ssh -p <port>" an die expect-Skripte durch (Default
  weiterhin 22 falls Parameter fehlt).
- Web-Terminal (Verbindungstest) sendet den im Formular eingetragenen
  Port statt hartkodiert 22.
- Live getestet: gültiger/leerer/ungültiger Port beim Anlegen, korrekte
  Weitergabe durch generate_ips.py inkl. Feldreihenfolge, die poe.sh
'read' erwartet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 19:56:23 +02:00
alientimandClaude Sonnet 5 813b17d002 Rechtesystem: Löschen überall in Ändern falten, Gruppen-Tabelle transponieren
- "Ändern" deckt jetzt ausnahmslos überall Löschen mit ab (auch bei
  Clients) — es gibt kein eigenes *.delete-Recht mehr. Betroffen:
  devices/switches/credentials/users/groups.
- "D" in der Rechtetabelle bedeutet ausschließlich Dashboard-Neustart
  (devices.restart, PoE-Neustart über das Dashboard-Popup) und existiert
  nur bei Clients. Automatische Neustarts bei Ausfall (poe.sh) bleiben
  davon komplett unabhängig.
- Gruppen-Rechtetabelle transponiert: Kategorien jetzt als Zeilen,
  Rechte R(ead)/W(rite)/E(dit)/D(ashboard-Neustart) nebeneinander als
  Spalten statt untereinander. Deckt sich besser mit dem Legenden-Format
  und braucht weniger vertikalen Platz.
- Neue, einmalig laufende Migration (_migrated_permission_delete_fold_v2)
  überführt bestehende *.delete-Grants verlustfrei in *.edit, bevor die
  veralteten Rechte-Zeilen entfernt werden.
- Routen (delete_device/delete_switch/delete_credential/delete_user/
  delete_group) und die zugehörigen can_delete-Template-Variablen prüfen
  jetzt einheitlich *.edit statt *.delete.
- README-Rechtesystem-Abschnitt und Legende an das neue Tabellen-Layout
  angepasst.
- Live getestet: Kill-Switch, alle 5 Delete-Foldings (mit/ohne Recht),
  devices.restart-Gating unabhängig von devices.edit, Eskalationsschutz
  (Admin-Löschschutz, Systemgruppen-Schutz) — alles bestätigt korrekt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 19:49:28 +02:00
alientimandClaude Sonnet 5 4b7403f476 Vollständig granulares, an der Navbar gespiegeltes Rechtesystem
Kompletter Umbau des Permission-Systems weg von groben is_admin-Gates hin zu
einem zweistufigen, an die Sidebar-Struktur gespiegelten Rechtebaum:

  Geräte (devices_group.view)
   ├─ Clients      Lesen/Schreiben/Ändern/Löschen (+ PoE-Neustart)
   ├─ Switche      Lesen/Schreiben/Ändern/Löschen
   └─ Zugangsdaten Lesen/Schreiben/Ändern/Löschen (jetzt eigene Rechte,
                    vorher an switches.* gekoppelt)
  Logs (logs_group.view)
   ├─ Live         Lesen
   └─ Änderungen   Lesen
  Einstellungen (settings_group.view)
   ├─ Benutzer            Lesen/Schreiben/Ändern/Löschen
   ├─ Gruppen             Lesen/Schreiben/Ändern/Löschen
   ├─ Systemeinstellungen Lesen/Ändern
   └─ Im-/Export          Lesen (Export)/Ändern (Import)

- User.has_permission() ist jetzt hierarchisch: das "Bereich anzeigen"-Recht
  einer Top-Level-Gruppe wirkt als Kill-Switch für alle Kind-Rechte
  darunter, auch wenn ein Kind-Recht einzeln noch gesetzt ist. Mit
  Testgruppe verifiziert (devices.view ohne devices_group.view -> /devices
  liefert 302, "Geräte" verschwindet komplett aus der Sidebar; nach
  Zurücksetzen sofort wieder 200).
- devices.toggle entfällt, ist jetzt Teil von devices.edit (Ändern).
- Neue eigenständige credentials.*-Rechte statt Kopplung an switches.*.
- Benutzer- und Gruppenverwaltung sind jetzt ebenfalls granular/delegierbar
  (users.*/groups.*) statt fest is_admin-exklusiv — dafür neue,
  fest einprogrammierte Eskalationsschranken: Admin-Konten anlegen/ändern/
  löschen sowie Admin-Zuweisung bleiben unabhängig von delegierten Rechten
  echten Admins vorbehalten (mit Testgruppe verifiziert: Anlegen als Admin,
  Bearbeiten/Löschen bestehender Admin-Konten und Zuweisen zu "admin"
  wurden alle korrekt blockiert, normale Benutzerverwaltung funktioniert).
- "Admin" (virtuell) und "Benutzer" (Standardgruppe, neues is_system-Flag)
  sind jetzt echte Systemgruppen: weder umbenennbar noch in ihren Rechten
  änderbar, auch nicht durch Admins über die UI — Mitgliedschaft bleibt frei
  verwaltbar. Mit direktem POST verifiziert: Umbenennen/Löschen/Rechte-Reset
  von "Benutzer" werden blockiert, Mitgliederverwaltung funktioniert weiter.
- groups.html zeigt den Baum jetzt als 3 Zeilen (Geräte/Logs/Einstellungen)
  mit eingerückten Unterpunkten statt einer flachen Liste von Kategorien mit
  wiederholtem Bereichsnamen im Label.
- Migration in _ensure_schema() (Altrechte übertragen, neue Bereichs-Rechte
  für bestehende Gruppen nachtragen) läuft jetzt über einen Einmal-Guard in
  der settings-Tabelle — lief anfangs bei jedem Neustart erneut und hat
  damit den Kill-Switch-Mechanismus untergraben (ein deaktiviertes
  Bereichs-Recht wäre bei jedem Neustart automatisch wieder gesetzt worden,
  solange irgendein Kind-Recht noch aktiv war); im Test entdeckt und behoben.
- create_db.py synchronisiert (is_system-Spalte, neuer Rechtesatz für
  Frischinstallationen).
- README: Rechtesystem-Abschnitt komplett neu beschrieben (Baum, Kill-Switch,
  Systemgruppen, Eskalationsschutz).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 18:28:42 +02:00
alientimandClaude Sonnet 5 52b14b8ef7 Rechtesystem-Lücke schließen: Live-Log war nur @login_required statt admin-geschützt
Vollständiges Audit aller Routen gegen das Rechtesystem durchgeführt. Die
Nav-Sichtbarkeit stufte "Live" (Live-Log) bereits korrekt als admin-only ein
(_nav_key_visible: logs_live -> user.is_admin), die zugehörigen Routen
/logs und /get_log prüften das aber nie tatsächlich — nur @login_required.
Jeder eingeloggte Benutzer, unabhängig von seinen Gruppen-Rechten, konnte
das Live-Log damit direkt per URL aufrufen, obwohl die Sidebar es korrekt
ausblendete.

- /logs: fehlende is_admin-Prüfung ergänzt (Flash + Redirect, konsistent zu
  activity_log()/users()/groups()/settings()).
- /get_log: fehlende is_admin-Prüfung ergänzt (403, da per AJAX abgerufen).

Alle anderen neu hinzugekommenen Seiten (Benutzer, Gruppen, Systemein-
stellungen, Im-/Export, Änderungslog, Jetzt-prüfen-Trigger) waren bereits
korrekt und bewusst exklusiv auf is_admin geprüft — dort gibt es keine
sinnvolle granularere Rechte-Stufe unterhalb von Admin (Gruppenverwaltung
steuert das Rechtesystem selbst, Systemeinstellungen/Import-Export/manuelle
Prüfung wirken global). Zugangsdaten bleiben bewusst an switches.*-Rechte
gekoppelt (bestehende, dokumentierte Design-Entscheidung). README um eine
Übersicht ergänzt, welche Bereiche bewusst ohne granulare Rechte auskommen
und warum.

Mit Testbenutzer ohne jede Gruppenzugehörigkeit verifiziert: /logs liefert
jetzt 302 (Redirect), /get_log liefert 403 statt beide zuvor 200.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 16:01:39 +02:00
alientimandClaude Sonnet 5 c4f682b5de UI-Fixes (Button-Wrap, Suchfeld-Icon, Log-Farben, Nav-Persistenz), manueller Check-Trigger, echter Live-Refresh
- section-head: Button ("+ Neue Zugangsdaten" etc.) rutscht bei langer
  Beschreibung nicht mehr in die nächste Zeile — bleibt immer rechts oben,
  der Beschreibungstext wickelt stattdessen innerhalb seiner eigenen Spalte.
- Suchfeld-Icon überlappte den Platzhaltertext: eine spätere, generische
  Input-Regel mit gleicher Spezifität hat die padding-left-Regel des
  Lupe-Icons überschrieben (reine Reihenfolge-im-Stylesheet-Sache) — Selektor
  spezifischer gemacht.
- Live-Log: Neustart-bezogene Zeilen (manueller Neustart, automatischer
  PoE-Restart bei Ausfall) werden jetzt orange markiert statt farblos.
- Aufgeklappte Nav-Gruppen bleiben über Seitenwechsel hinweg erhalten
  (localStorage), zusätzlich zur automatischen Aufklappung der Gruppe der
  aktuell aktiven Seite.
- Datei-Auswahl (Import) im Dark Mode gestylt (Text + Button), inkl.
  color-scheme-bewusster Browser-Widgets.
- Manueller "Jetzt prüfen"-Trigger (Icon neben dem globalen Timer, nur
  Admins): startet rpi-check.service neu -> frisches Logfile + sofortiger
  Durchlauf, Countdown synchronisiert sich auf den manuellen Zeitpunkt.
- Dashboard und Live-Log aktualisieren sich jetzt per AJAX im Hintergrund
  (kein voller Seiten-Reload mehr): neue Route /dashboard/tiles liefert das
  Kachel-Fragment (_dashboard_tiles.html, von index.html eingebunden und vom
  Live-Update nachgeladen), get_log() liefert den aktuellen Dateinamen als
  Header mit, damit die Anzeige nach einem Service-Neustart aktuell bleibt.
- Root-Cause für "Intervall bleibt immer bei 300s": in der WSL-Testumgebung
  lief poe_wrapper.py nur als manuell gestarteter Hintergrundprozess statt
  als echter rpi-check.service — "systemctl restart" lief dadurch ins Leere.
  Echten systemd-Service in WSL eingerichtet und Intervalländerung end-to-end
  verifiziert (Log-Zeilenabstand folgt jetzt dem neu gesetzten Intervall).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 15:53:15 +02:00
alientimandClaude Sonnet 5 0fcb6c94cb Topbar auf globalen Prüf-Timer reduziert, Aktionen+Beschreibung über Tabellen, sortierbare Listen
- Topbar zeigt rechts jetzt ausschließlich den "Nächste Prüfung"-Countdown,
  konsistent auf jeder Seite (auch anonymes Dashboard) statt nur auf dem
  Dashboard. Neuer Context-Processor inject_check_timer()/get_last_run_at()
  liefert last_run/interval global, ohne dass jede Route das selbst
  berechnen muss.
- "+ Neu ..."-Buttons (Devices, Switches, Zugangsdaten, Benutzer, Gruppen)
  aus der Topbar entfernt und stattdessen in einen .section-head direkt
  über der jeweiligen Tabelle verschoben, zusammen mit einer kurzen
  Beschreibung der Seite (bisher nur bei Zugangsdaten/Gruppen vorhanden,
  jetzt auch bei Geräte/Switche/Benutzer).
- Live-Log: eigene lokale Timer-Pill entfernt (redundant zum globalen
  Timer), "Aktualisieren"-Button in denselben section-head verschoben.
  Dashboard: Suchfeld aus der Topbar in den Seiteninhalt verschoben, lokale
  Timer-Anzeige entfernt (übernimmt die globale Topbar-Pill), Reload-bei-
  Intervallende-Logik bleibt als separater, unsichtbarer Scheduler erhalten.
- Neue generische Tabellen-Sortierung (app.js: initSortableTables): Klick
  auf eine Spaltenüberschrift mit data-sort-key sortiert die Zeilen anhand
  von data-sort-<key>-Attributen. Unterstützt auch Akkordeon-Tabellen mit
  mehreren <tbody> (Gruppen: Haupt- + Detail-Zeile bleiben als Einheit
  zusammen, die virtuelle "Admin"-Zeile bleibt über data-sort-pinned immer
  oben). Angewendet auf Geräte, Switche, Zugangsdaten, Benutzer, Gruppen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 15:21:30 +02:00
alientimandClaude Sonnet 5 e4cceb4091 poe.sh: CRLF-Zeilenenden, Feldanzahl und Switch-Gating fixen
- Datei hatte CRLF-Zeilenenden (vermutlich aus einer Windows-Bearbeitung),
  wodurch das Skript unter Linux/WSL mit "command not found: $'\r'" und
  Syntaxfehlern abbrach. Auf LF normalisiert.
- Die Haupt-Check-Schleife las per `read` nur 7 Felder aus
  generate_ips.py, das aber inzwischen 8 Felder liefert (MAC am Ende
  angehängt) — dadurch landete die MAC-Adresse an $switch_pass
  angehängt. `mac` als zusätzliche (ungenutzte) read-Variable ergänzt.
- PoE-Neustart wurde bei nicht erreichbaren Geräten ausgelöst, sobald ein
  `port`-Wert gesetzt war — unabhängig davon, ob dem Gerät überhaupt ein
  Switch zugeordnet ist. Das führte zu sinnlosen SSH-Versuchen mit leeren
  Zugangsdaten (ssh @) und irreführenden "PoE deaktiviert/aktiviert"
  Log-Einträgen für Geräte ohne Switch. Prüft jetzt zusätzlich, dass
  switch_ip nicht leer ist, bevor ein Neustart versucht wird (gilt für
  den regulären Check-Loop und den manuellen Neustart).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 14:56:53 +02:00
alientimandClaude Sonnet 5 37dac64abc Live-Log-Fix, Sidebar-Untermenüs für Geräte/Einstellungen, eigenes Konto
- Live-Log-Bug behoben: _latest_log_file() sortierte nach Datei-ctime statt
  nach Dateiname — auf manchen Dateisystemen (u.a. unter WSL2 auf einem
  gemounteten Windows-Laufwerk) unzuverlässig und lieferte nicht immer das
  tatsächlich neueste Logfile. Sortiert jetzt wie der Rest des Codes über
  den chronologisch sortierbaren Dateinamen (rpi-YYYYMMDDHHMMSS.log).
- Navbar umstrukturiert in aufklappbare Gruppen mit Unterpunkten:
  - "Geräte": Clients (bisherige Devices-Seite), Switche, Zugangsdaten
  - "Einstellungen": Benutzer, Gruppen, Systemeinstellungen (Prüfintervall),
    Im-/Export
  - "Logs": Live, Änderungen (vormals Live-Log/Änderungslog)
  Sichtbarkeit gilt jetzt auch pro Unterpunkt: eine Gruppe erscheint nur,
  wenn mindestens ein Unterpunkt für den Benutzer sichtbar ist, und zeigt
  dann auch nur die sichtbaren Unterpunkte.
- Im-/Export als eigene Unterseite mit Export/Import nebeneinander
  (.settings-grid).
- Neue Seite "Mein Konto" (/account, erreichbar über ein Zahnrad-Symbol
  neben dem eigenen Namen in der Sidebar) ersetzt das bisherige Profil-Modal:
  Profil/Passwort/Profilbild als volle Seite, dazu für Admins die
  Navbar-Reihenfolge (aus den Systemeinstellungen hierher verschoben).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 14:44:02 +02:00
alientimandClaude Sonnet 5 a7a23b7c12 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>
2026-08-10 14:27:46 +02:00
alientimandClaude Sonnet 5 fa6bb2935f Zugangsdaten-Entitaet, Gruppen-Tabelle mit Aufklappansicht, Vor-/Nachname, Dashboard-Sektionen
- Zugangsdaten (SSH-Logins) als eigenstaendige, wiederverwendbare Entitaet
  statt direkt am Switch; inline Neuanlage beim Switch-Erstellen moeglich;
  automatische Migration bestehender Switch-Logins
- Gruppen-Seite als Tabelle mit Aufklapp-Zeile fuer Rechte (Akkordeon),
  Admin als feste Systemzeile (Mitgliederverwaltung ueber is_admin),
  Standardgruppe 'Benutzer' mit allen Ansichtsrechten (devices.view,
  switches.view), automatische Zuordnung neuer/verwaister Benutzer
- Users-Seite: Icon-Buttons statt Text, Bearbeiten+Passwortaenderung in
  einem Modal zusammengefuehrt, Gruppe/Admin-Zuweisung ueber eigenen
  Zuweisen-Button (wie Switch-Zuordnung bei Devices), Vor-/Nachname
- Dashboard: einheitliche Kachelansicht mit/ohne Login, drei sortierte
  Abschnitte (Offline/Online/Deaktiviert), Kachel-Suchfilter, Bootstrap-
  artiges Grid (max. 6 Spalten), Aktivieren-Option im Popup fuer
  deaktivierte Geraete, Countdown serverseitig korrekt geseedet
- Sidebar dauerhaft einklappbar (Desktop, persistent via localStorage)
- Devices-Tabelle: Aktions-Buttons nebeneinander statt untereinander

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 13:30:47 +02:00
alientimandClaude Sonnet 5 08d8d1f738 Dashboard-Kacheln, Ansichtsrechte, Standardgruppe, Countdown-Fix, Deploy-Pfad
- Dashboard zeigt jetzt in beiden Zustaenden (mit/ohne Login) Kacheln statt
  Tabelle; ohne Login nur aktive Geraete + Online/Offline/Gesamt, eingeloggt
  alle Geraete + zusaetzliche Deaktiviert-Kachel
- Sortierung ueberall: erst alle Nicht-Online-Geraete, dann Online, jeweils
  alphabetisch
- Neue Rechte devices.view / switches.view; Standardgruppe 'Benutzer' wird
  automatisch angelegt (alle Ansichtsrechte) und jedem neuen Benutzer
  zugeordnet; bestehende Benutzer ohne Gruppe werden migriert
- Gruppen-Seite zeigt zusaetzlich virtuelle 'Admin'-Karte (informativ) und
  markiert die Standardgruppe (nicht loeschbar)
- 'Naechste Pruefung'-Countdown wird serverseitig aus dem tatsaechlichen
  letzten Log-Eintrag geseedet statt bei jedem Reload neu zu starten
- Deployment-Doku korrigiert: Checkout getrennt von /srv/poe_manager, damit
  kein verschachteltes srv/poe_manager/srv/poe_manager entsteht

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 12:41:21 +02:00
alientimandClaude Sonnet 5 82bfeb17ed Initial commit: PoE Manager modern UI rebuild
- Neues, eigenständiges Frontend (Sidebar, zentriertes Logo in der Topbar,
  Dark/Light-Theme, Karten-Dashboard, Toasts/Modals statt Bootstrap)
- Oeffentliches Kurz-Dashboard ohne Login (Status-Uebersicht)
- Browser-SSH-Terminal (paramiko, plattformunabhaengig) zum Testen von
  Switch-Zugangsdaten inkl. interaktiver Host-Key-Bestaetigung
- Granulares Rechtesystem mit Gruppen (Devices/Switches-Berechtigungen)
- Aufgeraeumtes Backend mit konfigurierbaren Pfaden, auto-generierten
  Secrets statt hart codierter Werte im Original

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 12:18:49 +02:00