c9ac0bf69b8c7e9d94f80c086b024e19dee77cf5
97
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c9ac0bf69b |
Lizenz-/Modulsystem Phase 1: TESM-seitiges Gating, Aktivierung, Topbar (v1.2.0)
Baut auf licensing.py (Phase 0) auf und schaltet TESM erstmals auf ein lizenziertes Modulmodell um. Standard-Funktionen (Dashboard, Clients, Switche, Zugangsdaten, Benutzer, Gruppen, Auditlog, Live-Log, Verlauf, Import) bleiben immer frei nutzbar. - license_active()/module_licensed()-Gate-Funktionen + In-Memory- Lizenzcache (_load_license, _write_license_file, _delete_license_file), Jinja-Globals fuer Templates. - Vollstaendige Module (DHCP inkl. Kea-Log, Fileshare, Wartung) werden ohne passendes Modul in der Lizenz komplett aus der Navigation ausgeblendet (_nav_key_visible) UND serverseitig auf Routenebene abgewiesen (_require_module) -- ein direkter URL-Aufruf hilft nicht. - 'Sichtbar, aber ohne Lizenz inaktiv': LDAP- und NGINX-Einstellungen, Papierkorb-Bearbeiten, Export sowie das Pruefintervall-Feld (aus dem gemeinsamen Systemeinstellungen-Gate herausgeloest, Hostname/Zeitzone/ Netzwerk/Logs bleiben frei) zeigen ohne Lizenz einen Hinweis statt der Aktion. - Export bleibt als bewusste Ausnahme auch im lizenzlosen Zustand nutzbar, wenn dieser durch eine selbst ausgeloeste Deaktivierung (Systemwechsel) entstanden ist (license_export_grace). - Automatischer PoE-Neustart bei Ausfall wird in poe.sh (nicht in Python) gegatet -- einmal pro Schleifendurchlauf gelesen, an check_device() durchgereicht; der manuelle Neustart-Button (devices.restart) bleibt komplett unangetastet. - Topbar-Lizenzanzeige (Prioritaet: fehlt > abgelaufen > laeuft in <=30 Tagen ab > Heartbeat seit >=14 Tagen ausgeblieben (rein informativ) > alles ok), eigene CSS-Blink-Varianten fuer Rot/Orange (die vorhandene pulse-dot-Animation war fest auf Gruen verdrahtet). - Neue Settings-Unterseite 'Lizenz': Status, Lizenzdatei-Upload, Online- Aktivierung/-Deaktivierung mit automatischem Fallback auf einen manuell auszutauschenden Code (ein Protokoll, zwei Transportwege, siehe licensing.py), Anbieter-Kontaktkarte. - 6-stuendiger Heartbeat-Hintergrundthread (_license_heartbeat_loop, exakt nach dem Vorbild von _audit_archive_loop). Isoliert auf dem Testsystem (192.168.82.51) verifiziert: alle fuenf Topbar-/Sichtbarkeitszustaende (keine/gueltige/bald ablaufende/abgelaufene/ heartbeat-stale Lizenz) per Playwright durchgespielt, kompletter Online- Fallback-auf-Offline-Code-Austausch fuer Aktivierung UND Deaktivierung gegen einen simulierten Master erfolgreich durchlaufen (inkl. Export- Grace-Ausnahme nach Deaktivierung). Bewusst NICHT nach main gemergt/getaggt/ausgerollt: ohne den in Phase 2 noch zu bauenden Master-Lizenzserver kann POETEST noch keine echte Lizenz beziehen -- ein Rollout jetzt wuerde dort DHCP/Fileshare/Wartung/LDAP/ NGINX/Papierkorb/Export/automatischen Neustart sofort abschalten. Bleibt auf dev, bis Phase 2 (Master) + Phase 3 (POETEST-Enterprise-Lizenz) so weit sind, dass beides zusammen ausgerollt werden kann. Teil des Lizenz-/Modulsystems, siehe Plan toasty-twirling-hickey.md (Phase 1 von 3). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
b70ebefb17 |
Lizenzsystem Phase 0: Ed25519-Kryptographie, Aktivierungs-/Heartbeat-Protokoll (licensing.py)
Neues gemeinsames Modul fuer Client (TESM) und kuenftigen Master-Lizenzserver: - Zwei-Ebenen-Ed25519-Schluesselhierarchie (Master-Signaturschluessel + Pro-Lizenz-Schluesselpaar), kanonische JSON-Signierung - issue_license/verify_license_file/license_status (inkl. 30-Tage-Gnadenfrist, 30-Tage-Vorwarnung, Modul-Set) - Ein Protokoll fuer Online- (API) und Offline-Aktivierung (Code-Copy-Paste): build_client_request/verify_client_request, build_master_response/verify_master_response, encode_code/decode_code - system_fingerprint (Systembindung), heartbeat_stale_warning (14 Tage, rein informativ) Isoliert getestet (Signatur-Rundlauf, Manipulationserkennung, Ablauf-/ Gnadenfrist-Datumslogik, Online- und Offline-Aktivierung/Deaktivierung inkl. Fingerprint-Bindungspruefung und Reaktivierung nach Deaktivierung, Eingabevalidierung) -- 41/41 Checks erfolgreich. Teil von Phase 0 des Lizenz-/Modulsystems, siehe Plan toasty-twirling-hickey.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
d90c27b8f8 |
LDAP-Export: Sperrstatus + individuelle Gruppenzuweisung von AD-Nutzern (weiterhin v1.1.11)
_export_users()/_import_users() schliessen AD/LDAP-Konten bewusst komplett aus (sie werden ueber Active Directory verwaltet). Das hatte aber eine Luecke: weder eine manuelle Sperrung/Entsperrung noch eine manuell (per "Gruppe zuweisen") vergebene individuelle Gruppenmitgliedschaft eines AD-Benutzers wurden bisher irgendwo gesichert -- bei einem Umzug auf ein neues System (der eigentliche Zweck dieser Seite) waere ein zuvor gesperrtes AD-Konto dort wieder entsperrt gewesen, und individuell vergebene Rechte waeren beim naechsten Login des Nutzers verloren gegangen. Neu in der "ldap"-Kategorie: ad_user_states exportiert fuer jeden bereits bekannten AD-Benutzer Sperrstatus + aktuelle Gruppenmitgliedschaft (inkl. Admin als Sentinel, analog den bestehenden AD-Gruppenzuordnungen). Beim Import wird ein bereits vorhandenes AD-Konto entsprechend aktualisiert; existiert es auf dem Zielsystem noch nicht (Nutzer hat sich dort noch nie eingeloggt), wird ein Platzhalter-Konto mit auth_source='ldap' und korrektem Sperrstatus VORAB angelegt -- der Login-Pfad prueft is_locked bereits vor der eigentlichen AD-Authentifizierung, ein vorab gesperrtes Konto bleibt also gesperrt. Gruppenzuweisungen sind rein additiv (insert or ignore), die naechste automatische AD-Gruppenzuordnung beim Login nimmt nichts davon wieder weg. Ausserdem: - Die pauschalen "Was exportieren?"/"Was importieren?"-Hinweise entfernt (waren durch die praeziseren Kategorie-Hinweise bei Benutzer/LDAP/ Zugangsdaten/NGINX/Auditlog laengst ueberholt und teils sachlich veraltet). - Kategorie-Hinweise jetzt auch in der Import-Vorschau sichtbar (vorher nur beim Export). - "Nur Admin"-Kennzeichnung aus beiden Kategorie-Listen entfernt (rein optisch, die serverseitige Rechtepruefung bleibt unveraendert). Verifiziert per isoliertem Roundtrip-Test: gesperrter AD-Nutzer bleibt nach Reset+Import gesperrt, manuell zugewiesene Gruppe eines AD-Nutzers uebersteht Loeschen+Import, und ein komplett neuer (nie eingeloggter) AD-Nutzer wird korrekt mit Sperrstatus/Gruppen/Admin-Flag vorangelegt -- inkl. Bestaetigung, dass der echte Login-Sperr-Check dieses vorab angelegte Konto tatsaechlich blockiert. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
8577cd24cd |
Export-Kategorien: kuerzere Namen, Auditlog/Benutzer-Hinweise als Icon (weiterhin v1.1.11)
Sichtbare Checkbox-Namen im Export-Formular verkuerzt (nur die Kachel- Ansicht betroffen, Funktionalitaet/Schluessel unveraendert): Custom-Gruppen -> Gruppen, LDAP/AD-Einstellungen -> LDAP/AD, DHCP-Einstellungen -> DHCP, NGINX-Einstellungen -> NGINX, Benutzer (lokal) -> Benutzer. Neue Hinweis-Icons (Klammerzusaetze wandern konsistent zu den bereits bestehenden bei Zugangsdaten/NGINX ins "i"-Icon statt in den Namen): - Auditlog: Hinweis, dass nur die aktuell in der DB stehenden Eintraege exportiert werden (bereits archivierte Tage liegen als eigene Dateien vor). - Benutzer: Hinweis, dass AD/LDAP-Benutzer NICHT gesichert werden (nur lokale Konten sind Teil des Exports -- AD-Konten legen sich beim naechsten Login ohnehin automatisch neu an). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
877903bd77 |
NGINX-Export inkl. Zertifikat/Schluessel, Kategorie-Hinweise als Icon (weiterhin v1.1.11)
- NGINX-Export nimmt jetzt zusaetzlich das aktuell hinterlegte TLS- Zertifikat und den privaten Schluessel selbst mit (Base64, falls vorhanden) -- der gesamte Export ist ohnehin passphrasenverschluesselt, genau wie andere Geheimnisse (Zugangsdaten-Passwoerter, LDAP-Bind- Passwort) bereits im Klartext darin landen. Bewusst weiterhin AUSGESCHLOSSEN bleibt jeglicher Let's-Encrypt-/certbot-Zustand (ssl_source, ACME-Konto, Renewal-Timer) -- an genau diesen Host/diese Domain-Registrierung gebunden, kann nicht mitziehen. Ein Import setzt ssl_source deshalb immer auf "upload", auch wenn urspruenglich per Let's Encrypt ausgestellt. - Die erklaerenden Klammerzusaetze bei "Zugangsdaten" (know_hosts-Hinweis) und "NGINX-Einstellungen" (Zertifikat/Schluessel-Hinweis) sind jetzt ein "i"-Hinweis-Icon statt Fliesstext im Label, passend zum bestehenden Hover-Tooltip-/Klick-Modal-Muster (siehe v1.1.10). Verifiziert per isoliertem Test mit einem echten (selbstsignierten) Test-Zertifikat: Export->Import-Roundtrip liefert bitidentische Cert-/ Key-Dateien mit korrekten Berechtigungen (0600 Schluessel, 0644 Zertifikat) auf einem eigenen Zielpfad, sowie live per Browser gegen POETEST (Hover zeigt korrekten Tooltip-Text, 2 Icons im Export-Formular). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
2b05d012ee |
Im-/Export: fehlende Felder ergaenzt, NGINX neu, known_hosts (v1.1.11)
Vollstaendiger Audit aller Export-/Import-Kategorien gegen das tatsaechliche DB-Schema (7 parallele Pruef-Agents je Kategorie) ergab mehrere Luecken -- alle behoben: - Auditlog-Export (v1.1.10-Nacharbeit): kein Deckel mehr bei 500 Zeilen, exportiert wird das komplette aktuell in der DB stehende Auditlog (bereits archivierte Tage liegen ohnehin als eigene Dateien vor). - Clients: ssh_port und Zugangsdaten-Zuordnung (als portabler credential_name statt roher ID) fehlten komplett im Export. - Switche: ssh_port fehlte komplett im Export. - Zugangsdaten: category (z.B. "Linux-Client" fuer Wartung) fehlte. - Custom-Gruppen: is_default fehlte (war beim Import zusaetzlich hart auf 0 gesetzt statt aus der Importdatei uebernommen). - LDAP-Gruppenzuordnungen: app_group_id wurde als ROHE lokale Datenbank-ID exportiert -- auf einem frisch installierten Zielsystem (der eigentliche Zweck dieser Funktion, siehe Seiten-Untertitel "Umzug auf eine neue Umgebung") haben Gruppen dort andere IDs, die Zuordnung waere kaputt oder auf die falsche Gruppe gezeigt. Jetzt wird der Gruppenname exportiert (bzw. "Admin" als Sonderfall) und beim Import auf dem Zielsystem wieder zur dortigen ID aufgeloest -- unbekannte Zielgruppen werden übersprungen statt eine kaputte Referenz anzulegen. - ldap_fileshare_mappings (AD-Gruppe -> Freigabe) fehlte im Export/Import komplett -- alle Fileshare-Zuordnungen gingen bei jedem Umzug verloren. - ldap_required_login_group (Login-Beschraenkung auf eine AD-Gruppe) fehlte in LDAP_SETTING_KEYS und wurde nie exportiert. - NGINX-Einstellungen (Domain, Ports, SSL/HSTS-Schalter) hatten bisher gar keine Export-/Import-Kategorie. Bewusst AUSGESCHLOSSEN bleiben ssl_source, das TLS-Zertifikat und der private Schluessel sowie jeglicher Let's-Encrypt-/certbot-Zustand -- das sind host-gebundene reale Dateien bzw. eine ACME-Registrierung fuer genau diesen Host, kein portabler Einstellungswert. - known_hosts: sobald Zugangsdaten exportiert werden, wird zusaetzlich der Inhalt von known_hosts mitexportiert (jeder per SSH bereits bestaetigte Host-Key gehoert inhaltlich zu den Zugangsdaten) und beim Import ergaenzend (nicht ueberschreibend) mit Zeilen-Deduplizierung in die eigene known_hosts-Datei eingespielt. avatar_filename (Benutzer) bleibt bewusst vom Export ausgeschlossen -- reiner Dateiname ohne die zugehoerige Bilddatei waere auf dem Zielsystem nur ein kaputter Verweis, analog zur bewussten Nichtaufnahme von TLS-Zertifikat/ Schluessel bei NGINX. Verifiziert per isoliertem Roundtrip-Test gegen eine Kopie der echten Datenbank (nie die Produktivdaten selbst): Export -> Loeschen -> Re-Import fuer jede geaenderte Kategorie plus known_hosts-Merge (inkl. Deduplizierungs-Probe durch zweifachen Import derselben Datei), sowie ein echter Browser-Exportlauf gegen POETEST zur Bestaetigung der UI/Label-Texte. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
0f0fa99e53 |
Feld-/Funktionserklaerungen als Hover-Tooltip + Klick-Modal (v1.1.10)
Statisch sichtbare Erklaerungstexte (.hint/.field-hint) app-weit auf ein
einheitliches "i"-Icon umgestellt: Hover zeigt den Text neben dem
Mauszeiger, Klick/Tap oeffnet ein wiederverwendetes Modal mit dem vollen
Text. Zentral in app.js (initHintIcons, ensureHintTooltip/-Modal) via
Event-Delegation, kein Aufwand pro Seite. Hover ist bewusst per
matchMedia("(hover: hover) and (pointer: fine)") gated -- auf Touch-
Geraeten faellt automatisch nur Tap-zu-Modal an, da echte
mouseenter/mousemove-Events dort ohnehin nicht zuverlaessig auftreten.
Nicht jede .hint/.field-hint wurde umgestellt -- nach folgenden Kriterien
entschieden:
- Live-Status-Anzeigen (aktuelle Log-Pfade, Netzwerkstatus, Auditlog-
Schwellenwerte, aktive Reservierungs-/Lease-Zaehler, certbot-Timer-
Status usw.) bleiben sichtbar, da sie aktuellen Zustand zeigen, keine
generische Erklaerung.
- Kurze, einzeilige Abschnitts-Untertitel (z.B. "Server, Bind-Konto und
Suchparameter fuer die Anbindung an AD/LDAP.") bleiben als Orientierung
direkt unter der Ueberschrift sichtbar.
- Bereits JS-live-aktualisierte Status-Felder (id-tragend, z.B.
"Fragt live... ab." -> "N Gruppen geladen.") bleiben unangetastet.
- Laengere, mehrsaetzige Abschnitts-Erklaerungen (z.B. AD-Gruppenzu-
ordnungen, Fileshare-Gruppenzuordnung, Subnetze, DHCP-Options, Wartung)
sowie so gut wie alle Feld-Erklaerungen wandern ins Icon.
Nebenbei: Karte "Fileshare-Gruppen" in LDAP-Einstellungen zu "Fileshare-
Gruppenzuordnung" umbenannt (passt jetzt zur bereits bestehenden
log_action-Zielbezeichnung in app.py).
Neue gemeinsame Macro-Datei templates/_hint_icon.html, importiert von
15 Templates. Verifiziert: alle 18 betroffenen Seiten laden fehlerfrei
(200, keine JS-Fehler), Icon-Anzahl je Seite geprueft, Hover-Tooltip und
Klick-Modal auf Desktop, Tap-Modal auf simuliertem Mobilgeraet (390x844,
Touch), sowie stichprobenartig bestaetigt dass die bewusst belassenen
Hinweise unveraendert sichtbar bleiben.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||
|
|
9b4cf8c2e1 |
Auditlog: Statistik-Kacheln zeigen geladen/gesamt (weiterhin v1.1.9)
Alle vier Kacheln (Alle, Hinzufuegen, Aenderungen, Loeschungen) zeigen jetzt "geladen / gesamt" (z.B. "300 / 466"), solange noch nicht alles geladen ist -- Gesamtzahlen je Kategorie kommen aus einer einzigen aggregierten SQL-Abfrage (SUM/CASE ueber die bestehenden LIKE-Muster von category_of), nicht aus einem vollstaendigen Tabellen-Scan. Sobald alles geladen ist, wird wieder nur die reine Zahl angezeigt (client- wie serverseitig identische Formatierungslogik). Dieser Fix wird per Nutzervorgabe unter der GLEICHEN Releasnummer v1.1.9 ausgeliefert (Tag wird bewusst weitergezogen statt neu vergeben, da v1.1.9 zum Zeitpunkt dieses Commits zwar schon getaggt, aber die Kachel-Anzeige noch nicht ausgereift war). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
1c0082d3d4 |
Auditlog: Nachladen aelterer Eintraege in 300er-Schritten (v1.1.9)
Die Auditlog-Seite zeigte bisher unveraenderlich nur die neuesten 500 Zeilen an -- alles Aeltere war komplett unsichtbar (auch wenn es noch nicht archiviert war, siehe v1.1.6). Jetzt: - Seitengroesse von 500 auf 300 reduziert (Erstladung UND jeder Nachlade- Schritt). - Neuer Button "Mehr laden (300)" (orange) laedt per AJAX genau den naechsten 300er-Block aelterer Eintraege nach (Cursor-Pagination ueber die id-Spalte) und haengt ihn ans bestehende tbody an, ohne die Seite neu zu laden. - Neuer Button "Alle laden" ruft denselben Nachlade-Schritt wiederholt auf, bis wirklich alles geladen ist (mit Fortschrittsanzeige im Button-Text), statt in einer Anfrage die komplette (potenziell sehr grosse) Tabelle auf einmal zu rendern. - Suche/Sortierung/Kategorie-Filter sowie die vier Statistik-Kacheln arbeiten transparent ueber ALLE aktuell geladenen Zeilen (nicht nur die urspruenglichen 300) -- Zeilenliste fuer die Filterung wird jetzt bei jedem Aufruf frisch aus dem DOM gelesen statt einmalig zwischengespeichert. - Zeilen-Rendering (Aktions-Label/-Icon/-Farbe) in ein gemeinsames Makro (_audit_log_macros.html) ausgelagert, das sowohl die Erstladung als auch das AJAX-Fragment (_audit_log_rows.html) nutzen -- verhindert Abweichungen zwischen beiden Darstellungen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
fc4b165d23 |
Auditlog: Ziel-Spalte wieder dynamisch statt erzwungen einzeilig (v1.1.8)
Das white-space:nowrap aus v1.1.7 sollte nur den Sonderfall des neuen Auditlog-Export-Eintrags fixen, hat aber als Nebenwirkung die gesamte Ziel-Spalte in die Breite gezogen: der bereits laenger bestehenden Aktion data.export gehoert ein bis zu 135 Zeichen langes Ziel (Liste der exportierten Kategorien), und table-layout:auto bemisst eine Spaltenbreite anhand des LAENGSTEN unzeilenumbrochenen Inhalts ueber alle (auch gerade nicht gefilterte) Zeilen hinweg -- nicht nur der sichtbaren. Der eigentliche Ausloeser (zu langer Wert im Ziel-Feld) ist mit v1.1.7 bereits behoben (Ziel = kurzer Ressourcenname wie "Auditlog", Dateiliste in Details) und macht das erzwungene nowrap ueberfluessig. Zeilenumbruch bei den seltenen laengeren Zielen (wie data.export) ist unproblematisch, genau wie es die Details-Spalte schon immer handhabt. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
e81735d5ec |
Auditlog-Export: Ziel/Details korrekt getrennt (v1.1.7)
Beim Export archivierter Auditlog-Tage (log_action("auditlog.export_delete", ...))
stand faelschlich die komplette Dateiliste im Feld "Ziel" (dadurch wurde die
Spalte in der Auditlog-Tabelle wieder unschoen breit), statt wie bei allen
anderen Aktionen ueblich einen kurzen Ressourcennamen im Ziel und die
ausfuehrliche Beschreibung in Details zu haben. Jetzt: Ziel = "Auditlog",
Details = Anzahl + exakte Dateinamen. Gleiche Korrektur fuer den
automatischen Archivierungslauf selbst (auditlog.archive). "Ziel"-Spalte
zusaetzlich mit white-space:nowrap gegen Umbrueche bei laengeren Werten
abgesichert.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||
|
|
2eaefc9e1f |
Auditlog-Archivierung, Verlauf-RAW mit Zeilennummern, UI-Politur (v1.1.6)
- Auditlog waechst nur bis zu einem Schwellenwert (20.000 Zeilen): danach werden die aeltesten VOLLSTAENDIGEN Kalendertage (der laufende Tag wird nie mitten am Tag angefasst) tageweise in je eine Datei audit-YYYY-MM-DD.log unter /var/log/tesm/audit-archive archiviert und aus der DB geloescht, bis wieder 15.000 Zeilen unterschritten sind. Laeuft als taeglich pruefender Hintergrund-Thread (gate: TESM_WEB_PROCESS, wie beim bestehenden Fileshare-Sweep), zusaetzlich einmalig sofort beim Dienststart. - Archivierte Auditlog-Tage sind unter Verlauf einsehbar (RAW-Ansicht) und koennen dort als ein ZIP exportiert werden, was sie danach vom Server loescht -- bewusst nur manuell ausgeloest statt automatisch, mit eigenem Recht logs_history.edit (neue "E"-Spalte bei Verlauf in der Gruppen-Rechtematrix, ergibt sich automatisch aus GROUP_ROW_TYPES). Aufbewahrung der Archivdateien selbst ist unbegrenzt; bei knappem Speicherplatz (< 1 GiB frei im Log-Verzeichnis) erscheint ein Warnhinweis auf der Verlauf-Seite. - Verlauf -> "Komplettes Log (RAW)": Inhalt jetzt mit Zeilennummern (reine CSS-Counter-Loesung, kein Extra-Markup pro Zeile) -- Live-Log-RAW und nginx-RAW-Konfiguration bleiben bewusst unveraendert (nur an dieser Stelle angefragt). - Auditlog-Tabelle: Avatar-Spalte entfernt, Spaltenbreiten (Zeitpunkt/ Benutzer/Aktion/Ziel) schrumpfen jetzt auf den Inhalt statt zu strecken, "Ziel" nimmt nur noch so viel Platz wie noetig; Aktion-Pills brechen generell nicht mehr um (white-space: nowrap). - "Änderungen" endgueltig zu "Auditlog" umbenannt (Recht-Label, Kommentare); die "Änderungen"-Filterkachel selbst (Bearbeiten-Kategorie) behaelt bewusst ihren Namen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
cb8b929c3c |
Auditlog: Hinzufuegen gruen, Loeschen rot, Bearbeiten orange (v1.1.5)
- Neue Pill-Farbvarianten (.action-pill--create/--delete/--edit) je nach Aktions-Art -- "create"/"upload"/"mkdir" gruen, "delete" rot, alles andere (bearbeiten/aktivieren/zuweisen/...) orange als Sammelkategorie. - Fehlende Klartext-Label fuer die neuen fileshare.*-Aktionen ergaenzt (upload/mkdir/delete/rename/download) -- vorher stand dort noch der rohe Aktionsschluessel wie "fileshare.delete" statt eines lesbaren Textes wie bei allen anderen Aktionen. Live auf POETEST verifiziert: alle drei Farben korrekt (gruen bei "Datei(en) hochgeladen"/"Gruppe angelegt", rot bei "Datei/Ordner gelöscht"/"Gruppe gelöscht", orange bei "Als ZIP heruntergeladen"/ "Gruppe bearbeitet"). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
af5dfe30b4 |
Fileshare: Mehrfachauswahl zum Herunterladen (ZIP) und Loeschen (v1.1.4)
- Checkbox-Spalte in der Dateitabelle (inkl. "alle auswaehlen" im Tabellen- kopf) -- sobald mindestens ein Eintrag markiert ist, erscheint eine Aktionsleiste mit "Herunterladen (ZIP)" und (mit fileshare.edit) "Loeschen". - Herunterladen buendelt die Auswahl serverseitig zu EINEM ZIP statt mehrerer einzelner Downloads (vermeidet Browser-Blockaden bei vielen gleichzeitigen Downloads) -- neue Route /fileshare/download-multi, Ordner werden dabei rekursiv mit aufgenommen (relativer Pfad als Archivpfad). - Loeschen mehrerer Elemente auf einmal ueber eine neue Route /fileshare/delete-multi (ein Bestaetigungsdialog fuer die ganze Auswahl), mit Sammel-Erfolgsmeldung analog zum Mehrfach-Upload. - Die Pfadaufloesung+Validierung fuer ein einzelnes Kind-Element wurde aus dem bestehenden Einzel-Loeschen in _fileshare_resolve_child() extrahiert und von Einzel-Loeschen, Mehrfach-Loeschen UND Mehrfach-Download gemeinsam genutzt (kein duplizierter Sicherheitscode). Live auf POETEST verifiziert: Mehrfachauswahl inkl. "alle auswaehlen", ZIP-Download zweier Dateien (Inhalt geprueft), ZIP-Download eines Ordners (rekursiv, korrekter Archivpfad), Mehrfach-Loeschen mit Sammelmeldung. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
efb0d2aa01 |
Gruppen-Bearbeiten als Modal, Mobile-Fix, Mehrfach-Upload (v1.1.3)
- Gruppen bearbeiten (Name + Rechte) laeuft jetzt ueber ein Modal statt der bisherigen Inline-Ausklapp-Zeile -- gleiche Optik wie "Neue Gruppe". Der Gruppenname war dabei bisher ein verstecktes, nie wirklich editierbares Feld; jetzt ein normales Texteingabefeld (Backend unterstuetzte das Umbenennen inkl. Systemgruppen-Schutz bereits vollstaendig, es fehlte nur die Eingabemoeglichkeit im Formular). Admin-Rechte-Ansicht und die "Freischalten"-Ausnahme fuer die Systemgruppe "Benutzer" ziehen ins jeweilige Modal mit um. - Fileshare-Baum + Tabelle nebeneinander sprengte auf Tablet-/Handybreite die Seite -- stapelt jetzt (Baum oben, Tabelle darunter) ab der bestehenden 900px-Sidebar-Umschaltgrenze. - Mehrfach-Upload: Datei-Eingabefeld erlaubt jetzt echte Mehrfachauswahl (mehrere Dateien in einem Dialog) UND mehrmaliges Hinzufuegen nacheinander (per DataTransfer angesammelt, bevor "Hochladen" gedrueckt wird) -- funktioniert nativ auch auf Mobilgeraeten, da kein Custom- Upload-Mechanismus noetig ist. Backend verarbeitet jetzt eine Liste statt einer einzelnen Datei (request.files.getlist), mit Sammel- Erfolgsmeldung und pro Datei separater Namensvalidierung. Live auf POETEST verifiziert: Umbenennen+Rechte-Speichern ueber das neue Modal, Admin-Modal (readonly), Mobile-Layout (390px, Baum stapelt korrekt), echte Mehrfachauswahl (2 Dateien in einem Dialog, beide korrekt hochgeladen und einzeln in der Sammelmeldung genannt). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
d461f8ca42 |
Fileshare: redundanten Breadcrumb entfernt, Baum/Tabelle gleich hoch (v1.1.2)
- Der Breadcrumb-Link-Zeile ueber der Dateitabelle ("LDAP Share / Test")
war seit der Baum-Navigation redundant -- der Baum zeigt die aktuelle
Position ja schon (aufgeklappt + hervorgehoben). Entfernt.
- .fileshare-layout auf align-items:stretch umgestellt (statt flex-start)
und .fileshare-main als Flex-Spalte mit table-wrap{flex:1}, damit die
Baum- und die Tabellen-Kachel immer exakt gleich hoch sind, unabhaengig
vom jeweiligen Inhalt. Das inline margin-bottom auf .table-wrap musste
dafuer weg (sonst blieb trotzdem eine 16px-Luecke zwischen den unteren
Kanten).
Live auf POETEST verifiziert: beide Kacheln exakt gleiche Hoehe (166.5px
in beiden Fällen), Breadcrumb-Div nicht mehr im DOM.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||
|
|
1b7fe81507 |
Fileshare: Baum-Navigation, Datei-Vorschau, 15MB-Uploads (v1.1.1)
- Freigaben-Auswahl von einem Dropdown auf eine Baum-Navigation (links) umgestellt: alle gemounteten Freigaben als Wurzelknoten, Ordner beliebig aufklappbar. Der Pfad von der Wurzel bis zur aktuellen Position wird serverseitig schon aufgeklappt gerendert (_fileshare_tree_ancestors), alles andere laedt der Client per Klick lazy nach (neue Route /fileshare/subfolders) -- kein Durchlaufen der kompletten Freigabe auf einmal noetig. Dateien stehen bewusst nicht im Baum, nur in der bestehenden Tabelle rechts. - Inline-Vorschau fuer PDF (iframe), Bilder, TXT/CSV/LOG/MD/JSON (per Range- Request in Haeppchen bis 512KB, mit Hinweis bei Abschneiden -- Erkennung ueber den Content-Range-Header, nicht nur den 206-Status, der bei JEDEM Range-Request auftritt) sowie Word/Excel rein clientseitig per mammoth.js bzw. SheetJS (beide lokal vendored, kein Cloud-Viewer -- interne Dokumente verlassen das Netz nicht). Excel-Zellen werden bewusst per textContent statt ueber SheetJS' eigene HTML-Ausgabe gesetzt, um unabhaengig von deren Escaping-Verhalten sicher zu sein. Neue Route /fileshare/view mit fester Endungs-Positivliste, nosniff + restriktiver CSP. - Uploads bis 15MB erlaubt (MAX_CONTENT_LENGTH in app.py) -- dafuer musste auch nginx' client_max_body_size (Standard 1MB) auf 16MB angehoben werden, sowohl im Vorlagen-Code (_NGINX_PROXY_LOCATIONS/_render_nginx_config, fuer die naechste Anwendung ueber Einstellungen->NGINX) als auch in der Fresh-Install-Vorlage. Neuer 413-Errorhandler zeigt eine Flash-Meldung statt Werkzeugs nackter Fehlerseite. - install.sh: known_hosts (liegt unter /srv/tesm) von rsync --delete ausgenommen -- fehlte bisher in der Exclude-Liste (anders als sqlite.db/ fernet.key/secret.key) und waere bei jedem Update verlorengegangen. Live auf POETEST getestet: Baum-Vorbefuellung, Lazy-Load von Geschwisterordnern, alle 5 Vorschautypen (PDF-Route direkt verifiziert, Bild/Text/Word/Excel zusaetzlich visuell), Abschneide-Erkennung sowohl bei kleiner als auch bei >512KB-Datei. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
2c9ec8a100 |
Fileshare: Hostname-Aufloesung fuer UNC-Pfade + Zuordnungen bearbeitbar
- Freigabe-Hostnamen (statt nur IP) werden jetzt unterstuetzt: mount.cifs
bekommt die tatsaechliche Ziel-IP gezielt per "ip="-Option mitgegeben,
aufgeloest ueber _resolve_unc_host_ip(). Deckt zwei reale Faelle ab, die
beim Live-Test mit einem echten Server (Kurzname "s2025") auftraten:
1. Ein bloer Kurzname ohne DNS-Suffixsuche loest auf diesem Server gar
nicht auf -- Fallback probiert zusaetzlich den aus der LDAP-Server-
Einstellung abgeleiteten AD-Domaenensuffix (a la ".ad.eertmoed.net").
2. IPv4 wird bevorzugt aufgeloest, IPv6 nur als Fallback genutzt, falls
fuer den Hostnamen keine IPv4-Adresse existiert.
Der Hostname bleibt dabei unveraendert in der UNC sichtbar/gespeichert.
- AD-Gruppenzuordnungen (App-Rechte) und Fileshare-Gruppen sind jetzt per
Bearbeiten-Button (Stift-Icon) direkt aenderbar statt nur loeschen+neu
anlegen zu koennen -- neue edit_ldap_group_mapping/edit_fileshare_mapping
POST-Routen (UPDATE per id), Bearbeiten-Modals vorbefuellt inkl.
AD-Gruppen-Dropdown (gleiches Lade-Muster wie beim Anlegen).
- UX: "Erforderliche AD-Gruppe fuer Login" ist jetzt ein Dropdown mit
"Gruppen laden" (identisches Muster wie die anderen AD-Gruppenfelder)
statt eines Freitextfelds, in das der volle DN von Hand einzutragen war.
- _normalize_share_unc()-Hilfsfunktion aus dem Add-Handler herausgezogen,
jetzt auch vom Edit-Handler genutzt (kein duplizierter Code).
Live auf POETEST verifiziert: Hostname-Mount ("s2025") erfolgreich nach
Fix, Bearbeiten-Modals korrekt vorbefuellt und persistiert, Dropdown laedt
55 AD-Gruppen und behaelt die aktuelle Auswahl beim Neuladen bei.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||
|
|
09b222615a |
Fileshare-Feature: LDAP-gruppengesteuerte Netzlaufwerke im Browser (v1.1.0)
- Neue ldap_fileshare_mappings-Tabelle (AD-Gruppe -> Freigabe-UNC+Label), verwaltet in Einstellungen -> LDAP wie die bestehende App-Gruppenzuordnung (inkl. AD-Gruppen-Dropdown per "Gruppen laden", UNC-Backslash/Slash- Normalisierung). - Login-Gate: optionale "Erforderliche AD-Gruppe fuer Login" (Dropdown, gleiches Lade-Muster) -- ohne Mitgliedschaft scheitert der Login wie bei falschen Zugangsdaten, ohne Hinweis auf den eigentlichen Grund. - Sessionabhaengiges Mounten: beim LDAP-Login werden alle per AD-Gruppe zugeordneten Freigaben mit den eigenen (nie gespeicherten) Zugangsdaten per mount.cifs unter /mnt/tesm-shares/<session-token>/ gemountet, beim Logout wieder ausgehaengt. Passwort läuft ausschliesslich ueber die PASSWD-Umgebungsvariable von mount.cifs. Aufraeum-Sweep (30 Min-Takt, 12h-Schwelle) und Cleanup-on-Startup als Sicherheitsnetz. - Zwei unabhaengige Gates: TESM-Rechtesystem (fileshare.view/.create/.edit, neue PERMISSIONS-Gruppe fileshare_group) UND AD-Gruppenmitgliedschaft muessen beide zutreffen, sonst wird nicht gemountet und der Menuepunkt "Dateifreigaben" bleibt verborgen. - Neuer Dateibrowser (/fileshare, .../download, .../upload, .../mkdir, .../delete, .../rename) mit Breadcrumbs, Upload/Download, Anlegen/ Umbenennen/Loeschen -- Pfadaufloesung ausschliesslich ueber os.path.realpath-Vergleich gegen die eigene Mount-Root (Path-Traversal- Schutz), zusaetzlich secure_filename() fuer Datei-/Ordnernamen. - KRITISCHER Bugfix waehrend Live-Test gefunden: generate_ips.py importiert app.py als Hilfsmodul (u.a. aus poe.sh/tesm-check.service heraus, alle paar Sekunden) -- das fuehrte Modul-Level-Code erneut aus und hat aktive Fileshare-Mounts anderer Sitzungen sofort wieder ausgehaengt. Neues TESM_WEB_PROCESS-Env-Flag (nur in tesm.service gesetzt) schuetzt Cleanup-on-Startup und den Sweep-Thread davor, bei einem reinen Modul-Import erneut zu laufen. - install.sh: cifs-utils ergaenzt (liefert mount.cifs). - SCHEMA_VERSION 1 -> 2, VERSION 1.0.10 -> 1.1.0. Live auf Testbox (192.168.82.51) und POETEST (192.168.80.202) getestet: Login-Gate (erlauben/verbieten/reset), Rechte-Gate (Mount+Menuepunkt nur mit fileshare.view), vollstaendiger Datei-Workflow (Upload/Download/ Umbenennen/Anlegen/Loeschen) gegen eine echte SMB-Freigabe, Path-Traversal gegen alle 5 Routen (serverseitig verifiziert, kein Escape), sauberes Unmount bei /logout. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
0074e48e09 |
Responsive-Fixes per echtem Playwright/DevTools-Test verifiziert, zwei Bugs behoben
Auf Nachfrage tatsaechlich mit Browser-DevTools (Playwright/Chromium,
lokal installiert) statt nur per CSS-Analyse geprueft -- echte
Screenshots + automatisierte Overflow-Pruefung auf iPhone-16-Pro- (393x852),
iPad- (820x1180 Hoch-/1180x820 Querformat) und Desktop-Viewport (1440x900)
gegen die echte Live-Instanz auf Testsystem-Update-Restart.
Dabei zwei echte Bugs gefunden und behoben:
1. Topbar-Ueberlappung (ALLE Seiten, jeder eingeloggte Nutzer, jedes
iPhone/kleine Handy): .topbar-left (Seitentitel) und .topbar-right
(Pruef-Countdown- + DHCP-Pille) sind beide flex:1 ohne Zeilenumbruch --
auf 393px Breite quetscht das den Titel auf eine Breite, die kleiner
ist als sein eigener Inhalt braucht, wodurch der Titeltext sichtbar
ueber seine Spalte hinauslief und mit der Pille kollidierte (per
getBoundingClientRect nachgewiesen: 24.5px Ueberlauf). Fix: ab 560px
Breite bricht die Topbar um, Titel und Pillen bekommen je eine eigene
Zeile.
2. HTTP-/HTTPS-Port-Felder auf der NGINX-Seite stapelten trotz
entsprechender Media-Query-Regel nicht auf Handy-Breite -- Ursache:
inline style="flex:1" auf den .field-Divs hat Vorrang vor jeder externen
@media-Regel, ganz gleich wie spezifisch. Fix: inline style entfernt,
neue Klasse .field--half stattdessen -- die Mobile-Stapel-Regel greift
jetzt wie vorgesehen.
Zusaetzlich EINE eigene Aenderung aus v1.0.9 wieder zurueckgenommen, weil
sie sich beim Testen als unnoetig und riskant herausstellte:
- .table-wrap: overflow-x:auto (aus v1.0.9) wieder auf overflow:hidden
zurueckgesetzt. Grund: JEDES Template mit .table-wrap hat bereits einen
eigenen <div style="overflow-x:auto;"> exakt um die <table> -- die
zusaetzliche Scroll-Faehigkeit der Karte selbst war komplett redundant
und erzeugte einen verschachtelten doppelten Scroll-Container um
denselben Inhalt. Beim Testen der aufklappbaren Rechte-Tabelle
(Gruppen-Seite) fuehrte das zu Inhalten, die bei negativem x dauerhaft
unsichtbar wurden. table.data-table { min-width: 560px; } bleibt
bestehen (harmlos, der jeweils vorhandene innere Wrapper scrollt
weiterhin korrekt).
Eine dritte vermutete Ursache (aufgeklappte Rechte-Tabelle "verschwindet"
beim Anklicken des Chevrons) stellte sich NICHT als echter App-Bug heraus,
sondern als Artefakt von Playwrights eigenem automatischem
"scroll-into-view-vor-Klick"-Verhalten (der Chevron sitzt in der letzten
Tabellenspalte) -- per direktem JS-Aufruf von toggleDetail() ohne
Playwright-Klick liess sich das nicht reproduzieren, die Seite verhaelt
sich für echte Nutzer korrekt.
Alle 8 getesteten Seiten x 4 Viewports (32 Kombinationen) zeigen nach den
Fixes keinerlei horizontalen Overflow mehr (dokumentseitenweite
scrollWidth == clientWidth in jedem Fall).
VERSION 1.0.9 -> 1.0.10.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||
|
|
a3f5f028f2 |
Responsives CSS ausgebaut: Handy (iPhone) und Tablet (iPad) sauber nutzbar
Bestehendes eigenes Design-System beibehalten (kein Bootstrap-Framework eingebunden), stattdessen die vorhandenen Media Queries/Breakpoints (Sidebar kollabiert bereits ab 900px) um die fehlenden Faelle ergaenzt: - .table-wrap: overflow:hidden -> overflow-x:auto. Tabellen mit vielen Spalten (Geraete, Switche, Papierkorb, Benutzer, ...) haben auf schmalen Screens bisher entweder Spalten lautlos abgeschnitten oder die ganze Seite waagerecht aufgerissen -- jetzt scrollt NUR die Tabelle selbst (gleiches Prinzip wie Bootstraps .table-responsive). table.data-table bekommt zusaetzlich eine min-width als sinnvolle Scroll-Schwelle. - .settings-grid: minmax(340px, 1fr) -> minmax(min(340px, 100%), 1fr) -- ein nackter Pixel-Mindestwert erzwingt auf einem 375px-iPhone sonst waagerechten Overflow der ganzen Seite, sobald die Spalte breiter als der verfuegbare Platz ist. Neue Modifier-Klasse .settings-grid--wide (420px-Variante) ersetzt zwei betroffene inline style="grid-template-columns:minmax(420px,...)"-Overrides (LDAP-, NGINX-Seite), die denselben 100%-Fallback nicht hatten. - .permission-group-col: eigenes overflow-x:auto zusaetzlich zum bestehenden flex-wrap der Elternzeile -- falls eine einzelne Rechte-Spalte (z.B. Geraete mit R/W/E/D) auch nach dem Umbruch auf eine eigene Zeile noch zu breit ist. - Ab 900px (bestehender Sidebar-Breakpoint) zusaetzlich: .flex (generische Button-/Toolbar-Zeilen), .section-head, .term-toolbar/.log-toolbar und .detail-row brechen um statt seitlich ueberzulaufen; .modal-footer ebenso; .search-input in Tabellen-Toolbars schrumpft flexibel. - Ab 640px: nebeneinander angeordnete Formularfelder (z.B. HTTP-/HTTPS- Port bei NGINX) stapeln sich auf volle Breite statt zwei sehr schmale Eingaben zu erzwingen; Modal-Innenabstand etwas schmaler. - Ab 560px: Buttons in Kopfzeilen (section-head) nehmen die volle Breite ein statt als schmaler Block rechts zu kleben. Live auf Testsystem-Update-Restart verifiziert: alle Kernseiten (Dashboard, Clients, Switche, Gruppen, LDAP, NGINX, Live-Log, Wartung) laden weiterhin fehlerfrei (200) nach dem CSS-/Template-Austausch. WICHTIGER HINWEIS: In dieser Umgebung steht kein Browser-Screenshot- Werkzeug zur Verfuegung, die Aenderungen wurden also nicht pixelgenau auf echten iPhone-16-Pro-/iPad-Bildschirmgroessen gegengeprueft, sondern rein ueber CSS-Analyse und Seiten-Ladetests. Ein Blick auf einem echten Geraet (oder per Browser-Devtools-Emulation) wird empfohlen. VERSION 1.0.8 -> 1.0.9. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
5bca29f3da |
NGINX-Seite: Sichtbarkeit/Kontrolle der automatischen Let's-Encrypt-Verlaengerung
certbot richtet die automatische Verlaengerung (certbot.timer, zweimal taeglich) zwar schon beim Paket-Install selbst ein -- das war fuer den Admin in der Weboberflaeche bisher aber unsichtbar, reine Behauptung im Code-Kommentar. Jetzt sichtbar/nachpruefbar: - Neue Zeile "Automatische Verlaengerung" im Zertifikat-Status (nur bei Let's-Encrypt-Zertifikaten): Timer aktiv/inaktiv + naechster geplanter Lauf (_certbot_timer_status(), liest systemctl is-active/list-timers). - "Verlaengerung testen (Dry-Run)" -- certbot renew --dry-run, aendert nichts, zeigt nur ob die Verlaengerung im Ernstfall funktionieren wuerde. - "Jetzt manuell verlaengern" -- certbot renew --force-renewal, fuer Testzwecke oder eine sofortige Schluessel-Rotation (mit Warnhinweis auf Let's Encrypts Rate-Limits). - Hinweistext beim Anfordern zeigt den Timer-Status schon VOR der ersten Anforderung. VERSION 1.0.7 -> 1.0.8 (ab hier wieder normale fortlaufende Versionierung statt erneutem Release derselben Versionsnummer). Live auf Testsystem-Update-Restart verifiziert: Seite laedt weiterhin fehlerfrei (_certbot_timer_status crasht nicht ohne aktives Let's-Encrypt-Zertifikat), certbot.timer selbst bestaetigt aktiv (naechster Lauf laut systemctl list-timers korrekt angezeigt). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
4de5828573 |
NGINX-Seite ausgebaut: Domain/Ports, RAW-Config, Let's Encrypt mit Auto-Verlaengerung; Flask nur noch lokal erreichbar
Umbenennung: Einstellungen -> SSL/TLS heisst jetzt Einstellungen -> NGINX (Route /settings/ssl -> /settings/nginx, Recht settings_ssl.* -> settings_nginx.*, Icon "server" statt "shield") -- die Seite verwaltet den kompletten Reverse-Proxy, nicht mehr nur SSL. Neu: - Domain (server_name) und HTTP-/HTTPS-Port sind jetzt einstellbar, nicht mehr hart "_" bzw. 80/443. Aendert man den HTTP-Port weg von 80, wird automatisch zusaetzlich ein minimaler Nur-ACME-Block auf Port 80 mitgeschrieben, da Let's Encrypt (HTTP-01) protokollbedingt immer Port 80 braucht, unabhaengig vom konfigurierten HTTP-Port. - "RAW-Konfiguration"-Button zeigt die komplette aktuelle nginx-Config im selben Popup-Stil wie das Live-Log (Wiederverwendung von window.openRawLogModal, kein neuer Code dafuer noetig). - Let's Encrypt: Zertifikat per certbot anfordern (certonly --webroot, bewusst NICHT das certbot-nginx-Plugin -- das wuerde die App-eigene, komplett selbstgeschriebene Config direkt editieren und bei der naechsten Aenderung ueber diese Seite wieder verlieren). Automatische Verlaengerung laeuft ueber certbots eigenen systemd-Timer/Cron (Standard-Paketverhalten), ein Deploy-Hook (/etc/letsencrypt/renewal-hooks/deploy/tesm-reload-nginx.sh, wird wie die logrotate-Config bei jedem App-Start neu geschrieben) laedt nginx nach jeder Verlaengerung neu. Fehlgeschlagene Anforderung (kein oeffentlicher DNS-Eintrag o.ae.) wird sauber abgefangen und als Fehlermeldung angezeigt, ohne etwas zu veraendern -- live mit einer ungueltigen Test-Domain verifiziert. - Zertifikatsquelle (Upload vs. Let's Encrypt) wird als eigenes Setting (ssl_source) verfolgt, das Anwenden/Anzeigen/Entfernen beruecksichtigt beide Quellen jeweils passend (Entfernen einer LE-verwalteten Installation laeuft ueber "certbot delete", nicht ueber rohes Datei-Loeschen, damit certbots eigene Renewal-Konfiguration nicht verwaist). - certbot zu install.sh's Paketliste hinzugefuegt. Flask/gunicorn bindet jetzt an 127.0.0.1 statt 0.0.0.0 (tesm.service) -- direkter Zugriff auf Port 5000 aus dem Netz ist damit nicht mehr moeglich, nur noch ueber den nginx-Reverse-Proxy (der ohnehin nur ueber 127.0.0.1 zugreift). Beantwortet die Nachfrage, ob Flask unerreichbar gemacht werden kann. Live auf Testsystem-Update-Restart verifiziert: Domain/Port-Aenderung (HTTP-Port 8080 inkl. automatischem Zusatz-Block auf Port 80), RAW-Config-Anzeige, Zertifikat-Upload, Let's-Encrypt-Fehlschlag sauber abgefangen, Rueckbau auf Standardwerte (_, 80, 443, kein SSL) ueber denselben Anwenden/Bestaetigen-Sicherheitsnetz-Pfad wie zuvor. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
f27dde466b |
SSL/TLS-Verwaltung (Upload+Aktivieren), install.sh-Idempotenz, poe_web/rpi-check-Cleanup entfernt
SSL/TLS (neue Seite Einstellungen -> SSL/TLS, eigenes Recht settings_ssl.view/.edit):
- Zertifikat+privater Schluessel per Upload hinterlegbar (PEM), Validierung
VOR dem Speichern: gueltiges Zertifikat, noch nicht abgelaufen, Schluessel
passt zum Zertifikat (Abgleich ueber die oeffentlichen Schluessel als
DER-Bytes -- funktioniert algorithmus-unabhaengig fuer RSA/EC/...).
Speicherort /etc/tesm/ssl (bewusst ausserhalb von /srv/tesm, damit
rsync --delete beim Update das Zertifikat nicht mitloescht), Key mit
chmod 600.
- HTTPS aktivieren schreibt/validiert (nginx -t) eine neue nginx-Konfiguration
und laedt erst danach neu; Port 80 leitet dann automatisch auf https weiter.
HSTS optional zuschaltbar, setzt aktives SSL voraus.
- Gleiches Sicherheitsnetz wie bei den Netzwerkeinstellungen: nach dem
Anwenden 45s Zeitfenster zum Bestaetigen, sonst automatischer Rueckroll
der nginx-Konfiguration UND der ssl_enabled/hsts_enabled-Einstellungen.
Beide Pfade (Timeout-Rollback und Bestaetigen-verhindert-Rollback) live
auf Testsystem-Update-Restart verifiziert, inklusive eines ECHTEN,
ungeplant ausgeloesten Rollbacks waehrend des Testens.
- Noch KEINE Let's-Encrypt-Automatisierung (siehe Kommentar im Code) --
alle bekannten Instanzen laufen intern ohne oeffentliche Domain, HTTP-01
wuerde dort nicht funktionieren; kann bei Bedarf separat ergaenzt werden.
install.sh: Update-Idempotenz ("nur anfassen, was sich geaendert hat"):
- install_if_changed()-Helfer: Custom-Skript/systemd-Units/MOTD-Banner
werden nur noch bei tatsaechlich geaendertem Inhalt neu geschrieben,
daemon-reload nur wenn sich mindestens eine Unit geaendert hat.
- nginx-Konfiguration wird nur noch bei einer Frischinstallation aus dem
Repo-Template geschrieben -- eine bereits vorhandene (egal ob unveraendert
oder von der App selbst um SSL erweitert) wird nie wieder ueberschrieben.
Voraussetzung dafuer, dass die neue SSL-Funktion ein Update ueberlebt.
update.sh: komplette Sonderbehandlung fuer die zwei TESM vorausgehenden
Generationen (rpi-check.service, poe_web.service/poe_manager) entfernt --
auf allen aktuellen Installationen laengst migriert. Deaktivierte
Unit-Dateien dieser Generationen wurden auf Testsystem-Update-Restart
zusaetzlich manuell endgueltig geloescht (waren dort seit einer Migration
im September 2025 nur deaktiviert, nicht entfernt).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||
|
|
8253ab3f0e |
Verlauf: aktuelle live.log als Eintrag, Auswahl per Zeitraum statt Dateiname; RAW-Darstellung
- _live_log_history_files() liefert jetzt zusätzlich generation 0 = die
aktuelle, noch laufende live.log selbst (nicht nur die rotierten
Kopien .1/.2/...) -- _resolve_log_history_file akzeptiert 'live.log'
als gültige Auswahl.
- Jeder Eintrag bekommt ein range_label ('von' - 'bis'): 'bis' ist das
eigene mtime (Zeitpunkt der Rotation) bzw. 'laufend' bei der aktuellen
Datei, 'von' ist das mtime der naechstaelteren Generation -- rename()
bei einer Rotation aendert mtime nicht, jede Generation behaelt also
dauerhaft ihren eigenen Entstehungszeitpunkt. Live verifiziert: die
Zeitraeume reihen sich lueckenlos aneinander (Ende von Generation N ==
Anfang von Generation N-1).
- Dropdown und Toolbar zeigen jetzt den Zeitraum statt des rohen
Dateinamens.
- logs_history.html: Farbcodierung (colorizeLine) entfernt, log-box durch
ein <pre class="raw-log-content"> ersetzt -- reine RAW-Darstellung
ohne Client-seitiges Span-pro-Zeile-Aufbauen, auf Wunsch zur weiteren
Reduktion der Ladezeit im Verlauf (Live-Ansicht selbst bleibt farbig).
- VERSION 1.0.6 -> 1.0.7.
Live getestet auf Testsystem-Update-Restart: 4 Eintraege (aktuell + 3
rotierte Generationen), Zeitraeume lueckenlos verkettet, RAW-Endpoint für
'file=live.log' funktioniert, Path-Traversal-Versuch weiterhin mit 404
abgewiesen.
Zusaetzlich untersucht (kein Code-Fix, siehe Konversation): der Verdacht
eines install.sh-Bugs, der tesm-check.service nach einem Update gestoppt
liesse. journalctl zeigt auf beiden Boxen durchgehend 'active (running)'
ueber den fraglichen Zeitraum -- keine Bestaetigung eines Defekts in der
Dienstverwaltung gefunden. Wahrscheinlichste Erklaerung: ein manuell zu
Testzwecken erzwungener logrotate-Lauf (logrotate -f) direkt nach dem
Update hat live.log geleert, wodurch die Live-Log-Seite und die
Naechste-Pruefung-Anzeige fuer die Dauer eines Pruefintervalls leer
wirkten, bis der naechste Zyklus neuen Inhalt schrieb.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||
|
|
befc76819e |
Logrotate: TESM_LOG_DIR world-writable verhinderte jede Rotation
Root Cause (live auf POETEST reproduziert): /var/log/tesm war 0o777
(noch aus einer Zeit vor dem Umstieg auf root-laufende systemd-Units).
logrotate verweigert seit den aktuellen Haertungen jede Rotation in
einem group-/world-writable Verzeichnis ("insecure permissions") komplett
-- live.log wuchs auf POETEST seit der letzten Neuinstallation (9 Tage)
unbegrenzt, /var/lib/logrotate/status hatte fuer tesm ueberhaupt keinen
Eintrag. Direkte Folge: die neue Verlauf-Seite blieb leer, weil nie
live.log.1/.2/... entstanden.
Fix:
- os.chmod(TESM_LOG_DIR) und os.chmod(TESM_CLIENT_UPDATE_LOG_DIR) in
app.py: 0o777 -> 0o755 (tesm.service/tesm-check.service laufen beide
als root, kein anderer Dienstbenutzer schreibt hierher -- Keas eigenes
Log liegt separat unter /var/log/kea/). Laeuft bei jedem App-Start,
heilt bestehende Installationen also automatisch beim naechsten
Neustart/Update.
- install.sh: chmod 777 -> chmod 755 fuer /var/log/tesm bei einer
Frischinstallation, veraltete/falsche Kommentar-Begruendung (angeblich
auch fuer Kea noetig) korrigiert.
- _write_logrotate_config(): zusaetzlich 'su root root' in der
generierten Konfiguration als Absicherung, falls das Verzeichnis aus
irgendeinem Grund wieder zu weit geoeffnet wird.
- VERSION 1.0.5 -> 1.0.6.
Live verifiziert auf Testsystem-Update-Restart: logrotate -d zeigt nach
dem Fix keine 'insecure permissions'-Fehler mehr, ein erzwungener Lauf
(logrotate -f) erzeugt sauber live.log.1/live.log.2.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||
|
|
99d4d3cd01 |
Live-Log: 300-Zeilen-Tail, RAW-Popup und Log-Verlauf (History) mit eigenem Recht
- /logs und /get_log liefern nur noch die letzten 300 Zeilen (_tail_lines, effizientes Rueckwaerts-Lesen ohne komplette Datei einzulesen) statt der kompletten live.log -- behebt Ladezeit-Probleme auf groesseren Umgebungen. - Neuer Endpoint /logs/live/raw liefert das komplette Live-Log fuer ein Popup im Stil des bestehenden 'Ungespeicherte Aenderungen'-Modals (window.openRawLogModal in app.js, lazy nachgeladen erst bei Klick). - Neue Unterseite 'Verlauf' (/logs/history, /logs/history/raw) fuer aeltere, von logrotate rotierte Kopien des Live-Logs (live.log.1, .2, ...), auswaehlbar per Dropdown, ebenfalls mit 300-Zeilen-Tail + RAW-Popup. - Eigenes Recht logs_history.view (Kind von logs_group, direkt unter Live einsortiert) statt an logs_live.view gekoppelt -- Live-Status sehen und durch alte Logs blaettern sind bewusst getrennte Rechte. - Dateiname-Validierung gegen Path-Traversal in _resolve_log_history_file (striktes Regex + Verzeichnis-Check + Existenzpruefung). - VERSION 1.0.4 -> 1.0.5. Live getestet auf Testsystem-Update-Restart (192.168.82.51): Tail-Anzeige, RAW-Popup, Verlauf-Auswahl inkl. echter live.log.1, Permission-Matrix (Gruppen-Seite) sowie Rechtetrennung (Nutzer mit nur logs_live.view sieht /logs, aber weder Verlauf-Link noch Zugriff auf /logs/history bzw. /logs/history/raw) und Path-Traversal-Abwehr allesamt verifiziert. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
c30ad41101 |
install.sh: Sicherheitsnetz gilt jetzt fuer JEDES Update, nicht nur Schema-Aenderungen
Live gefunden: der 'Schema unveraendert -> kein Backup'-Kurzschluss aus v1.0.3 liess einen rein CODE-seitigen Fehler im neuen Paket (z.B. ein kaputtes app.py -- mit dem DB-Schema hat das nichts zu tun) komplett ungeschuetzt durch. Backup + Health-Check + ggf. Rollback laufen jetzt bei JEDEM Update einer bestehenden Installation, unabhaengig von SCHEMA_VERSION -- die bleibt nur noch fuer den Wortlaut der Log-Meldung relevant (In-Place-Update vs. Migration). Zusaetzlich beim Rollback selbst: 'mv' durch 'cp -a' beim Zurueckspielen ersetzt, plus die fehlgeschlagene Installation vorher separat beiseite gelegt (/srv/tesm-failed-update-<timestamp>) statt geloescht -- vorher wurde das Backup beim Zurueckspielen selbst zum neuen /srv/tesm und verschwand dadurch als eigenstaendige Sicherung (widersprach der eigenen Zusage 'Backup bleibt erhalten'). Live auf der Testbox in allen drei Pfaden verifiziert: - Erfolgreiches Update: Backup erstellt, Health-Check bestanden, Backup geloescht, Daten erhalten. - Migration (kuenstlich abweichende SCHEMA_VERSION, echte Struktur unveraendert): Backup erstellt, Health-Check bestanden, Backup geloescht. - Fehlgeschlagenes Update (absichtlich kaputte app.py): Backup erstellt, Health-Check schlaegt korrekt fehl, automatischer Rollback stellt den letzten funktionierenden Zustand wieder her (Dienst aktiv, Health-Check 200, Datenbank-Canary erhalten), Backup UND fehlgeschlagene Installation bleiben als zwei getrennte Verzeichnisse zur Fehlersuche erhalten. VERSION: 1.0.4 (v1.0.3 hatte die oben beschriebene Luecke und wurde bereits ausgerollt, daher hier ein neues Release statt eines erneuten Tags auf v1.0.3). |
||
|
|
e17d91743a |
install.sh wird update-faehig, update.sh auf Bootstrapper reduziert
install.sh erkennt jetzt selbststaendig eine bestehende Installation (an sqlite.db) und verhaelt sich entsprechend: - Keine vorhandene Installation -> normale Frischinstallation (wie bisher). - Vorhanden, SCHEMA_VERSION unveraendert -> einfaches In-Place-Update, kein Backup noetig (rsync schliesst sqlite.db ohnehin aus). - Vorhanden, SCHEMA_VERSION unbekannt/abweichend -> Update MIT Sicherheitsnetz: die KOMPLETTE bisherige Installation (Code, venv, Datenbank, Schluessel) wird vorher 1:1 nach /srv/tesm-backup-pre-migration-<timestamp> kopiert (bewusst nicht nur die Datenbank -- ein reines DB-Rollback wuerde beim naechsten Start mit dem NEUEN Code sofort denselben Migrationsfehler wiederholen). Die eigentliche Schema-Migration uebernimmt danach die App selbst beim Start (_ensure_schema() in app.py). Kommt der Dienst anschliessend per HTTP-Check auf /login nachweislich NICHT gesund hoch, wird automatisch die komplette Sicherung zurueckgespielt und bleibt zur manuellen Pruefung erhalten. Kommt er gesund hoch, gilt die Migration als erfolgreich und das Backup wird geloescht. update.sh delegiert diese gesamte /srv/tesm-spezifische Logik jetzt an install.sh und ist selbst nur noch fuer das Ablösen AELTERER, TESM vorausgehender Installationsstufen zustaendig (PoE Manager unter /srv/poe_manager, das urspruengliche reine Bash-Setup) -- bleibt aber als bequemer Ein-Skript-Bootstrapper (Download + Entpacken + Aufruf von install.sh) bestehen, wie gewuenscht. install.sh selbst ist dadurch jetzt auch OHNE update.sh direkt aus einem entpackten Release-Paket lauffaehig und erkennt ein Update trotzdem korrekt. VERSION: 1.0.3 (SCHEMA_VERSION unveraendert, 1 -- keine echte Schema- Aenderung in diesem Release, In-Place-Update-Pfad bleibt der Normalfall). |
||
|
|
e934c3ff0f |
Wartung: Update-Button orange + nach rechts, Bulk-Button 'Bulk-Update'
Reihenfolge in der Aktionen-Spalte getauscht (Neustart -> Update, statt Update -> Neustart) und der Einzel-Update-Button auf btn-primary (bereits vorhandene Akzentfarbe, orange) umgestellt, damit er sich vom neutralen Neustart-Button abhebt. Der Bulk-Button unten heisst jetzt 'Bulk-Update' statt 'Update starten'. Live end-to-end verifiziert: Testsystem-Update-Restart hat eine neue MAC/IP bekommen (52:54:00:AD:B1:73 / 192.168.82.51, Geraete-Eintrag in POETEST entsprechend aktualisiert) -- der neue Einzel-Update-Button hat darueber ein komplettes Update ausgeloest und erfolgreich durchlaufen (alle 6 Schritte einzeln bestaetigt: apt update, initramfs MODULES=most, apt upgrade, apt full-upgrade, apt clean, apt autoremove). |
||
|
|
7b17c952ba |
Papierkorb: Sortieren/Suchen; Wartung: Einzel-Update-Button + Bulk-Neustart
Papierkorb: jede der fuenf Kategorien (Clients/Switche/Zugangsdaten/ Benutzer/Gruppen) bekommt jetzt dasselbe Sortieren/Suchen wie die uebrigen Listenseiten (data-sortable + Such-Eingabe je Tabelle, nur wenn die Kategorie tatsaechlich Eintraege hat). Wartung: Update liess sich bisher nur per Bulk (Checkbox + 'Update starten') ausloesen. Jedes Geraet bekommt jetzt zusaetzlich einen eigenen 'Update'-Button (analog zum bereits vorhandenen 'Neustart'- Button, ueber ein eigenstaendiges <form> + form="update-<mac>"- Referenz -- kein verschachteltes <form>). Zusaetzlich ein 'Bulk- Neustart'-Button neben 'Update starten', der dieselben Checkboxen/ dasselbe #updateForm nutzt wie das Bulk-Update (Ziel-Route wird per JS nur fuer diesen einen Submit umgebogen), inkl. Bestaetigungsdialog wie beim Einzel-Neustart. Serverseitig war dafuer keine Aenderung noetig -- _maintenance_dispatch() verarbeitete beliebig viele MACs schon immer generisch, die bisherigen Formulare haben nur zufaellig immer genau eine bzw. alle uebermittelt. Live auf POETEST verifiziert: Papierkorb zeigt Sortieren/Suchen nur bei tatsaechlich befuellten Kategorien; der neue Einzel-Update-Button loest korrekt einen Job fuer genau dieses eine Geraet aus (SSH-Verbindungs- versuch zum richtigen Ziel bestaetigt, sauber behandelter Fehler, da das Testsystem gerade offline war). |
||
|
|
d90095885f |
Papierkorb: eigener globaler Bereich unter Einstellungen (view+edit)
Vorher: Sichtbarkeit einer Papierkorb-Sektion war an das jeweilige Ressourcen-Leserecht gekoppelt (devices.view fuer Clients-Sektion usw.), Wiederherstellen/endgueltig Loeschen an das jeweilige Ressourcen- Bearbeiten-Recht (switches.edit fuer Switche im Papierkorb usw.). Jetzt: papierkorb.view/papierkorb.edit als eigenstaendiges Rechtepaar unter Einstellungen (vorher unter Geraete einsortiert), komplett losgeloest von den einzelnen Ressourcen-Rechten: - papierkorb.view zeigt ALLE fuenf Kategorien auf einmal, unabhaengig davon, ob der Benutzer die jeweilige aktive Ressource sehen darf. - papierkorb.edit erlaubt Wiederherstellen/endgueltig Loeschen JEDER Kategorie, unabhaengig vom sonstigen Bearbeiten-Recht der jeweiligen Ressource (z.B. switches.edit gewaehrt jetzt KEINEN Zugriff mehr auf Switche im Papierkorb). Live auf POETEST mit zwei eigens angelegten Testgruppen verifiziert: ein Nutzer mit switches.edit (aber ohne papierkorb.edit) kann einen Switch im Papierkorb nachweislich NICHT wiederherstellen (deleted_at unveraendert vor/nach dem Versuch); ein Nutzer mit papierkorb.edit (aber ohne switches.edit) kann es. |
||
|
|
6ec9e5fd56 |
Gitea #2: Papierkorb bekommt eigenes Recht (papierkorb.view)
can_view_papierkorb leitete Sichtbarkeit bisher aus IRGENDEINEM der fuenf Ressourcen-Leserechte (devices/switches/credentials/users/groups .view) ab -- da die Standardgruppe 'Benutzer' bereits devices.view und switches.view hat, konnte praktisch jeder Benutzer den Papierkorb sehen (gemeldet: 'Papierkorb ist fuer alle sichtbar -> Rechtesystem'). Papierkorb hat jetzt ein eigenes, dediziertes Recht (papierkorb.view, neuer Eintrag unter PERMISSIONS/devices_group/children, automatisch im Gruppen-Editor sichtbar). Innerhalb der Seite bleibt jede Kachel zusaetzlich einzeln je Ressource gated wie bisher -- papierkorb.view schaltet nur die Seite selbst frei, keine automatischen Zusatzrechte. Die Standardgruppe bekommt das neue Recht NICHT automatisch (das ist der Fix) -- Admins behalten per is_admin-Bypass weiterhin vollen Zugriff. Live auf POETEST verifiziert: ein Testnutzer mit exakt den Standardgruppen-Rechten bekam vorher 200 auf /papierkorb, jetzt 302 (Zugriff verweigert), 'Papierkorb' verschwindet aus der Navigation, /devices bleibt unveraendert erreichbar. Gitea #1: LDAP-Login-Retry bei Domain-Controller in anderem Netzsegment Gemeldet: bei einem DC in einem anderen (langsameren) Netzsegment war ein doppelter Login noetig -- der erste Versuch scheiterte mit 'Benutzer/Passwort ungueltig', obwohl die Zugangsdaten korrekt waren. Ursache: sowohl der Service- als auch der User-Bind nutzten ein hartes 5s-Timeout (connect_timeout/receive_timeout) ohne jeden Retry -- eine erste, 'kalte' Verbindung zu einem entfernten/langsameren Netzsegment kann das leicht ueberschreiten (ARP-/Routing-Aufwaermen, TCP-Handshake, initiale Info-Abfrage), waehrend ein sofortiger zweiter Versuch dann klappt, weil der Pfad bereits 'warm' ist. Timeouts auf 10s angehoben und ein gezielter, genau EINMALIGER automatischer Retry ergaenzt (_ldap_connect_with_retry) -- greift NUR bei einem rein netzwerk-/timeout-bedingten Fehler (LDAPSocketOpenError/ LDAPSocketReceiveError/LDAPSocketSendError/LDAPResponseTimeoutError), NICHT bei einer tatsaechlich abgelehnten Anmeldung (z.B. falsches Passwort) -- ein Tippfehler soll keinen AD-Kontosperren-Zaehler unnoetig doppelt hochzaehlen. Isoliert unit-getestet (transienter Fehler -> 1 Retry -> Erfolg; echte Ablehnung -> kein Retry, sofortige Exception) und auf POETEST regressionsgetestet (LDAP dort weiterhin aktiviert und Dienst startet fehlerfrei). Zusaetzlich: /login aktiv gegen SQL-Injection/Login-Bypass/SSTI-Payloads getestet (13 Payload-Varianten inkl. UNION SELECT, DROP TABLE, Jinja- SSTI) -- keine Auffaelligkeiten, durchgehend parametrisierte Queries bestaetigt, users-Tabelle unveraendert. |
||
|
|
33753e4f41 |
Erklärende Kommentare aus dem Code entfernt, in KNOWLEDGE_BASE.md überführt
Betrifft app.py, create_db.py, poe.sh sowie alle HTML-Templates mit
Kommentaren (Jinja {# #}, HTML <!-- -->, eingebettetes JavaScript //).
Docstrings bleiben unangetastet -- nur reine Kommentarzeilen/-fragmente
wurden entfernt, damit der Code selbst schlank bleibt, ohne die
dahinterliegende Begründung zu verlieren.
Umsetzung:
- app.py/create_db.py: per Pythons eigenem tokenize-Modul entfernt (sicher
gegen '#' innerhalb von String-Literalen, Shebang-Zeile ausgenommen).
- poe.sh: manuell, da einzelne expect-Prompt-Strings ('expect "#"')
ein literales '#' als Teil des Strings enthalten, kein Kommentar.
- Templates: {# #}/<!-- --> per Regex (eindeutige Delimiter, anschließend
mit Jinja2s eigenem Parser validiert), echte // -JS-Kommentare manuell
identifiziert (zwei Fundstellen mit '//' waren tatsächlich Code --
als Protocol-relative-URL-Template-
String -- und wurden bewusst nicht angefasst).
Verifiziert: alle Python-/Bash-/Jinja-Dateien syntaktisch weiterhin
gültig, live auf der Testbox deployt und alle 14 Kernrouten mit 200
bestätigt, keine Fehler im App-Log.
|
||
|
|
bc360a0b7f |
Produktiver WSGI-Server (Gunicorn) statt Flasks Entwicklungsserver
tesm.service startete die App bisher direkt per 'python3 app.py', was Flasks eingebauten Werkzeug-Entwicklungsserver nutzt -- der meldet bei jedem Start 'WARNING: This is a development server. Do not use it in a production deployment.' und ist bewusst nicht für Dauerbetrieb gebaut. Umgestellt auf Gunicorn, mit zwei bewusst gesetzten Einschraenkungen: - '--workers 1': die App haelt laufende Wartungs-Jobs/Import-Vorschauen nur im Arbeitsspeicher DES EINEN Prozesses (_maintenance_jobs, _pending_imports, ...) -- mehrere Worker-Prozesse wuerden diesen Zustand auf mehrere, sich nicht kennende Prozesse verteilen. - '--worker-class gthread --threads 16' statt eines async-Workers: das Browser-SSH-Terminal (flask-sock) belegt pro offener WebSocket- Verbindung einen blockierenden Thread. - '--timeout 0': deaktiviert Gunicorns eigenen 30s-Worker-Timeout, der sonst auch eine offene, aber gerade inaktive SSH-Terminal-Verbindung killen wuerde. Live auf POETEST verifiziert: keine WARNING-Zeile mehr, genau 1 Master- + 1 Worker-Prozess, alle Kernseiten weiterhin 200, und als kritischer Regressionstest das WebSocket-SSH-Terminal Ende-zu-Ende gegen einen echten Host erfolgreich getestet (Host-Key/Passwort-Prompt/Login/Shell). Dabei einen unabhaengigen Altlast-Fehler in POETESTs venv gefunden und allgemein in install.sh gefixt: './venv/bin/pip' hat eine zur Venv-Erstellung fest einprogrammierte Shebang-Zeile -- nach der historischen Umbenennung 'poe_manager' -> 'tesm' zeigte sie auf einen nicht mehr existierenden Pfad und schlug mit 'cannot execute: required file not found' fehl. 'python3 -m pip' umgeht das, da nur der (korrekt referenzierte) venv-Python-Interpreter selbst gebraucht wird -- betrifft sonst auch jedes kuenftige In-Place-Update auf einem aehnlich gewachsenen Host. |
||
|
|
9b3051c984 |
VERSION: 1.0.1
Fortlaufendes Release nach v1.0.0 -- Schema unveraendert (SCHEMA_VERSION weiterhin 1), bereits installierte v1.0.0-Hosts aktualisieren sich beim naechsten update.sh-Lauf per In-Place-Update (Datenbank bleibt erhalten). |
||
|
|
6dd412c6e1 |
Import: 'database is locked' bei LDAP-/DHCP-Import behoben; Wartungs-Update mit Einzelschritten + Fehler-Log
_import_ldap()/_import_dhcp() riefen bislang set_setting() auf, das eine EIGENE sqlite3-Verbindung oeffnet. import_apply() ruft mehrere IMPORT_APPLIERS nacheinander auf DERSELBEN, noch offenen Verbindung auf und committet erst am Ende -- war zu diesem Zeitpunkt (z.B. durch eine vorher im selben Import verarbeitete Kategorie) bereits eine Schreib- Transaktion offen, blockierte set_settings()' eigene Verbindung darauf, bis 'sqlite3.OperationalError: database is locked' auftrat (live in Produktion reproduziert). Beide Funktionen schreiben jetzt direkt auf die uebergebene Verbindung. Isoliert reproduziert und verifiziert (alter Code: Lock-Fehler: neuer Code: fehlerfrei). Wartungs-Update fuer Linux-Clients (_apt_upgrade_command) außerdem komplett neu aufgebaut: statt einer einzigen, mit '&&' verketteten Befehlskette laufen jetzt einzeln benannte Schritte (apt update -> initramfs MODULES=most dauerhaft in initramfs.conf setzen -> apt upgrade -> apt full-upgrade -> apt clean -> apt autoremove), jeder mit eigenem 'Schritt N (...) -> erfolgreich/fehlgeschlagen (Exit-Code ...)'-Marker im Live-Fenster (mit Zeitstempel je Zeile, wie gehabt). Bricht beim ersten fehlgeschlagenen Schritt sofort ab. Bei einem mit Exit-Code fehlgeschlagenen Update wird zusaetzlich eine Fehler-Logdatei unter /var/log/tesm/clientupdate/<clientname>.error geschrieben (ueberschreibt eine vorherige aus einem frueheren Fehlschlag desselben Clients). |
||
|
|
3c3fbd8f23 |
update.sh: In-Place-Update statt Fresh-Install, wenn sich das DB-Schema nicht geändert hat
Neue Datei SCHEMA_VERSION (neben VERSION) markiert den DB-Schema-Stand. update.sh laedt/entpackt das Release-Paket jetzt VOR der Bestaetigungs- abfrage und vergleicht die SCHEMA_VERSION der bestehenden Installation mit der des neuen Pakets: - Stimmen beide ueberein (und existiert bereits eine sqlite.db) -- In-Place-Update: /srv/tesm bleibt bestehen, install.sh rsynct nur den neuen Code darueber (sqlite.db/fernet.key/secret.key waren dort schon immer von rsync ausgeschlossen). Geraete/Switche/Zugangsdaten/Benutzer/ Einstellungen bleiben vollstaendig erhalten, kein manuelles Neuanlegen oder Re-Import noetig. - Unterscheiden sie sich (oder ist eine der beiden Versionen unbekannt, z.B. eine Installation von vor dieser Aenderung) -- weiterhin der bisherige sichere Weg: /srv/tesm wird beiseite verschoben (nicht geloescht) und komplett frisch installiert. /srv/poe_manager (voellig andere App/Schema) wird davon unabhaengig immer beiseite verschoben, nie in-place uebernommen. create_db.py bekommt einen Hinweis-Kommentar, SCHEMA_VERSION bei jeder Schema-Aenderung mit hochzuzaehlen. |
||
|
|
d77327cee1 |
skripts/: Admin-Hilfsskripte für Bulk-Aktionen ins Repo aufgenommen
- assign_credential_to_all_devices.py: weist allen aktiven Geraeten auf einen Schlag dieselben Zugangsdaten zu (Name als optionales Argument, Standard 'HAPS'), inkl. Eintrag im Aenderungsverlauf. - accept_all_hostkeys.py: akzeptiert die SSH-Host-Keys aller aktiven Geraete in einem Rutsch (paralleler Verbindungsaufbau + Schluesseltausch, kein Login) -- nutzt dieselbe known_hosts-Datei wie die App, ein geaenderter (statt unbekannter) Key wird bewusst NICHT automatisch uebernommen, sondern separat als Warnung ausgegeben. Beide live gegen POETEST verifiziert. |
||
|
|
d72db26a86 |
Versionsnummer in der UI anzeigen (Login-Seite + Sidebar-Footer)
VERSION-Datei (neben app.py, wird von install.sh 1:1 nach /srv/tesm mitkopiert) plus Context-Processor inject_tesm_version() -- die zugehoerige app.py-Aenderung war bereits im vorherigen Commit enthalten. Damit laesst sich der installierte Stand direkt in der Oberflaeche ablesen, ohne Datei-Zeitstempel oder Commit-Hashes vergleichen zu muessen. |
||
|
|
0b9b92a228 |
Wartung/Update: MODULES=most beim apt upgrade + autoremove/clean danach
_apt_upgrade_command() setzt jetzt zusaetzlich MODULES=most (initramfs-
tools) fuer den Update-Aufruf -- falls dabei ein neuer Kernel installiert
und ein initramfs neu gebaut wird, landen vorsorglich ALLE verfuegbaren
Treiber darin statt nur die per Abhaengigkeitserkennung ('dep') erkannten,
damit der Client nach einem Kernel-Wechsel zuverlaessig bootet. Am Ende
zusaetzlich 'apt-get autoremove'/'apt-get clean', damit nicht mehr
benoetigte Kernel/Pakete sowie der .deb-Cache nicht auf jedem Client
unbegrenzt Platz belegen. Live gegen einen echten Ubuntu-Client
verifiziert (Exit-Code 0, komplette Kette lief durch).
|
||
|
|
2a092af508 | Merge: poe.sh-Revert von origin/main mit lokaler Kommentar-Aktualisierung zusammenführen | ||
|
|
c55f9d992d |
poe.sh: Regression bei ArubaOS-Manager-Accounts zurückgesetzt
disable_poe()/enable_poe() waren waehrend dieser Sitzung um explizite
'timeout { exit 1 }'/'eof { exit 1 }'-Abbrueche 'gehaertet' worden, um
echte SSH-/Kommando-Fehlschlaege vom Aufrufer unterscheidbar zu machen.
Live an Switch 'ST-2000-Sued' (Aruba JL322A 2930M) reproduziert: das
dafuer verwendete Login-Prompt-Pattern -re ".*> $" matcht nur einen
Operator-Prompt ("switchname>"). Landet der SSH-Account (hier: admin)
nach dem Login mit Manager-Rechten direkt im privilegierten Modus
("switchname#"), matcht das Pattern nie -> 5s-Timeout -> exit 1,
obwohl Passwort und Verbindung tatsaechlich in Ordnung waren. Jeder
manuelle wie automatische PoE-Neustart ueber einen Manager-Account
schlug dadurch fehl.
disable_poe()/enable_poe() sind jetzt wieder exakt auf den Stand der
urspruenglichen, in Produktion ueber Jahre bewaehrten Fassung
zurueckgesetzt (keine expliziten timeout/eof-Abbrueche, kein
abschliessendes exit 0) -- nur der Kommentar wurde aktualisiert, um
diesen Vorfall zu dokumentieren.
|
||
|
|
9f4195b800 | usr/local/bin/custom/poe.sh aktualisiert | ||
|
|
d066dbf03d |
SSH-Verbindungstest: 'password' vor 'keyboard-interactive' bevorzugen
Bei einigen Switches/APs (u.a. ArubaOS-Varianten), die Server-seitig sowohl 'password' als auch 'keyboard-interactive' anbieten, fuehrte keyboard-interactive zu einem stillen Fehlschlag ganz ohne sichtbaren Prompt im Browser-Terminal (RFC 4256 erlaubt eine erste Runde ganz ohne Prompts -- fuer den Nutzer unsichtbar -- gefolgt von sofortigem Scheitern statt einer zweiten Runde mit dem eigentlichen Passwort- Prompt). Das erklaerte exakt das gemeldete Symptom: kein Prompt, direkt 'Zugangsdaten falsch'. 'password' wird jetzt bevorzugt, wenn der Server es ueberhaupt anbietet (Ende-zu-Ende gegen einen echten Host verifiziert: Host-Key-Bestaetigung -> Passwort-Prompt -> erfolgreicher Login, keine Regression). Die keyboard-interactive-Handler-Funktion wurde zusaetzlich gehaertet: title/instructions werden jetzt mit angezeigt (manche Geraete legen den Hinweistext dort statt in prompt_list ab) und leere prompt_text-Strings bekommen einen Passwort-Fallback-Text, falls keyboard-interactive doch der einzig angebotene Weg ist. |
||
|
|
e6f9a7aee0 |
KRITISCH: install.sh brach ohne TTY sofort und lautlos ab (clear + set -e)
"clear" schlägt ohne TERM/Pseudo-Terminal mit Exit-Code 1 fehl ("TERM
environment variable not set") -- z.B. genau dann, wenn install.sh (wie von
update.sh vorgesehen) per SSH exec_command ohne pty aufgerufen wird. Mit
"set -e" hat das die GESAMTE Installation sofort an Zeile 2 abgebrochen,
noch bevor auch nur ein apt-Paket installiert oder eine einzige Zeile ins
Install-Log geschrieben wurde -- kein Fehlertext, kein Hinweis, einfach
Stille. Live reproduziert: update.sh lud das Release-Paket korrekt herunter
und entpackte es, install.sh selbst tat aber schlicht nichts.
Behoben: "clear" ist jetzt bewusst nie fatal (2>/dev/null || true) -- rein
kosmetisch, hat keinen Einfluss auf die eigentliche Installation.
|
||
|
|
8fc9ce7b27 |
create_db.py/create_admin.py: frisches Setup auf den vollständig migrierten Schema-Stand bringen
Ein wirklich frisches Setup (create_db.py auf einer neuen Installation) erzeugte bislang nur den ALLERERSTEN Schema-Stand aus der Projektgeschichte -- alle seither per _ensure_schema()-Migration nachgerüsteten Spalten und Tabellen fehlten komplett, bis diese Migration beim ersten App-Start erfolgreich durchläuft. Live auf einem frisch installierten Produktivsystem ist genau das nicht passiert: "sqlite3.OperationalError: no such column: deleted_at" beim Dashboard-Aufruf, weil devices/switches/credentials/ users/groups ohne die Papierkorb-Spalte angelegt wurden. Fehlten außerdem komplett: dhcp_subnets, dhcp_manual_reservations, service_accounts, ldap_group_mappings (LDAP/DHCP-Funktionen wären auf einer solchen frischen Installation gar nicht nutzbar gewesen) sowie dhcp_option_defs.is_standard. - create_db.py jetzt deckungsgleich mit dem tatsächlichen, vollständig migrierten Schema (per .schema-Dump von einer produktiv laufenden Instanz verifiziert) -- category/deleted_at auf credentials, deleted_at auf switches, credential_id/ssh_port/deleted_at auf devices, deleted_at auf users/groups, is_standard auf dhcp_option_defs, plus die vier fehlenden Tabellen. - POE_DB_PATH → TESM_DB_PATH in create_db.py UND create_admin.py (weiteres übersehenes Namensrelikt aus der TESM-Umbenennung), Docstring von create_admin.py aktualisiert. - Live verifiziert: komplett frische DB erzeugt, App auf separatem Testport dagegen gestartet, alle wesentlichen Routen (Dashboard, Geräte, Switche, Zugangsdaten, Wartung, Papierkorb, Benutzer, Gruppen, alle Einstellungsseiten, Logs, Konto) liefern 200 ohne einen einzigen Fehler im App-Log -- eine frische Installation hängt jetzt an keiner Stelle mehr von der Laufzeit-Migration ab, um überhaupt benutzbar zu sein. |
||
|
|
89682618f7 |
update.sh: auch /srv/poe_manager + poe_web.service (Vorgänger-Flask-App) abschalten
Ergänzt die bereits behandelte ursprüngliche Bash-only-Generation (WiS/Aruba-PoE: rpi-check.service + ips.list) um die dazwischenliegende Generation -- die Flask-App "PoE Manager" unter /srv/poe_manager mit poe_web.service, der Vorgänger von TESM in diesem Repo vor der Umbenennung. update.sh erkennt und sichert jetzt beide möglichen alten Installationsorte (/srv/tesm, /srv/poe_manager) unabhängig voneinander nach <Verzeichnis>-backup-<Zeitstempel> und stoppt/deaktiviert alle drei möglichen Dienst-Generationen. README entsprechend erweitert. |
||
|
|
31404266f3 |
update.sh: Release-Asset-Download statt git clone, ursprüngliches Bash-Setup mit abschalten
- update.sh lädt jetzt ein fertig gepacktes Gitea-Release-Asset (tesm-<tag>.tar.gz, Default "latest") direkt per curl herunter und entpackt es -- kein "git clone" bzw. Git auf dem Zielsystem mehr nötig. - Stoppt/deaktiviert zusätzlich zu den aktuellen TESM-Diensten auch das ursprüngliche, noch ältere reine Bash-Setup (rpi-check.service, rpi-check-restart.service/.timer -- der Vorgänger von TESM ganz ohne Datenbank/Web-UI, statische ips.list statt generate_ips.py), falls auf dem Zielsystem noch vorhanden. - README entsprechend aktualisiert, inkl. kurzer Anleitung zum Paketieren eines neuen Release (versionierter Tag + bewegliches "latest"-Tag). |
||
|
|
bfb58b62e9 |
Update/Reinstall-Skript für frische Release-Installationen (update.sh)
Ergänzt install.sh (das ein bestehendes Checkout voraussetzt) um einen Weg, eine bisher ad-hoc gepflegte Installation (z.B. per manuellem Datei-Upload während der Entwicklung) durch eine frische, aus einem Git-Tag geklonte Installation zu ersetzen: - Stoppt/deaktiviert tesm.service, tesm-check.service, tesm-check-restart.timer. - Verschiebt das bestehende /srv/tesm NICHT gelöscht, sondern nach /srv/tesm-backup-<Zeitstempel> -- bleibt als Rückfalloption und Datenquelle für einen späteren Export/Import vollständig erhalten. - Klont den angegebenen Release-Tag frisch und führt darin install.sh aus. - Weist sowohl vor dem Start als auch am Ende deutlich darauf hin, dass das Ergebnis eine FRISCHE Installation ohne die Daten der alten Instanz ist -- vorher exportieren (Einstellungen -> Im-/Export) oder danach alles neu einrichten. README um den entsprechenden Abschnitt ergänzt. |
||
|
|
93bcf4f6b6 |
DHCP: Auto-Write+Neustart nach Änderungen, Lease löschen, Dedup, Autostart-Slider
- Automatisches Schreiben+Neustart: jede relevante Änderung (Reservierung, Option, Subnetz, globale Einstellung) ruft jetzt _dhcp_write_and_restart() direkt nach dem Speichern auf -- schreibt die Kea-Config neu und startet den Dienst neu, FALLS er gerade aktiv ist (kein ungefragtes Hochfahren eines bewusst gestoppten Dienstes). Ein manuelles "Datei schreiben" + "Neu starten" kann dadurch nicht mehr vergessen werden. Der bisherige "Speichern"-Button (write_dhcp_file) restartet jetzt ebenfalls automatisch statt nur zu schreiben. - Leases-Übersicht dedupliziert: Keas memfile-Backend hängt bei jeder Verlängerung eine neue Zeile an, statt die alte zu ersetzen -- dieselbe Adresse stand oft dutzendfach in der CSV. _dhcp_read_leases() zeigt jetzt nur noch die jeweils neueste (per "expire") Zeile pro IP. - Einzelne Leases löschen (neuer Button + Route): entfernt alle Zeilen einer Adresse aus der Lease-CSV und startet den Dienst neu, damit Kea sie beim Neuladen nicht aus dem Speicher zurückschreibt. - Eigenständiger Autostart-Slider (wie beim Aktivieren/Deaktivieren von Clients) -- steuert nur systemctl enable/disable, entkoppelt vom aktuellen Start/Stopp-Zustand. - KRITISCH, live gefunden und behoben: die erste Version von _dhcp_delete_lease() hat Kea beim Neustart zum Absturz gebracht -- Pythons csv-Modul schreibt standardmäßig "\r\n"-Zeilenenden, Kea erwartet aber reines "\n" und lehnte die Datei mit "invalid column: 'pool_id^M'" komplett ab. Zusätzlich hätte das Neuanlegen der Datei als root (der App-Prozess) sie root:root statt dem Kea-Dienstnutzer gehören lassen, wodurch Kea keine neuen Leases mehr hätte schreiben können. Beides gefixt (lineterminator="\n", Eigentümer/Rechte der Originaldatei nach dem Schreiben wiederhergestellt) und auf POETEST bestätigt: Dienst übersteht den Neustart jetzt, Datei bleibt _kea:_kea, keine ^M mehr. |
||
|
|
4c13127679 |
Wartung: Update-Ausgabe live streamen, Zeitstempel pro Zeile, Auto-Scroll
- Neue _run_ssh_command_streaming(): liest stdout/stderr zeilenweise WÄHREND
das SSH-Update noch läuft, statt alles erst nach Abschluss auf einmal zu
liefern. Jede Zeile bekommt sofort einen eigenen Zeitstempel und wird per
_maintenance_job_set() live in den Job-Status geschrieben (bereits alle
2s gepollt) -- Puffer auf die letzten 400 Zeilen begrenzt statt eines
harten Zeichen-Limits.
- Eindeutiger Abschluss-Marker als letzte Ausgabezeile: "✔ All Updates
Done" bei Erfolg, "✖ Update abgebrochen/fehlgeschlagen …" bei Fehler --
vorher endete die Ausgabe einfach mitten im rohen apt-Output.
- Frontend: Ausgabefenster scrollt jetzt automatisch mit, sobald neue
Zeilen reinkommen ("stick to bottom", solange man nicht selbst
hochgescrollt hat) und beim ersten Öffnen direkt ans Ende.
- Live auf POETEST verifiziert: Zeilen tragen korrekte Zeitstempel, Update
endet sichtbar mit "✔ All Updates Done".
|
||
|
|
51d105399b |
Kritischen poe.sh-Pfadbug beheben + verschachteltes <form> in Wartung fixen
- KRITISCH: usr/local/bin/custom/poe.sh wurde bei der TESM-Umbenennung
übersehen und zeigte weiterhin komplett auf die alten Pfade
(/srv/poe_manager/sqlite.db, /srv/poe_manager/generate_ips.py,
/var/log/poe-manager) -- der Ping-Check/PoE-Neustart-Loop
(tesm-check.service) lief dadurch seit dem Umzug nur noch auf Fehler
("No such file or directory"), das Live-Log stand seit gestern 19 Uhr
still und automatische PoE-Neustarts bei Geräteausfall liefen die
ganze Zeit ins Leere. Auf POETEST live behoben und verifiziert: Log
schreibt wieder frische Einträge, Checks laufen wieder durch.
- Wartung: "Update starten" nahm live IMMER zusätzlich das erste Gerät
mit, auch ganz ohne angehakte Checkbox. Ursache: der "Neustart"-Button
jeder Zeile saß in einem eigenen <form>, das INNERHALB des großen
#updateForm verschachtelt war -- ungültiges HTML, wodurch Browser das
äußere Formular vorzeitig schließen/aufspalten. Behoben, indem jedes
Neustart-<form> jetzt außerhalb von #updateForm steht und der Button
es nur per form="reboot-<mac>" referenziert. Live verifiziert: ein
gezielt einzeln ausgewähltes Gerät startet jetzt ausschließlich sich
selbst, keine leere Auswahl mehr möglich.
- Zusätzlich defensiv: Checkboxen werden beim Laden der Wartungsseite
jetzt explizit zurückgesetzt (manche Browser stellen den
Checked-Zustand nach einem Redirect/Reload sonst aus dem Verlauf
wieder her).
- Restliche "poe"-Relikte aus der TESM-Umbenennung bereinigt: Export-
Dateiname (poe_manager_export_… → tesm_export_…), Kommentare
(poe_web.service → tesm.service) sowie ein für den Nutzer sichtbarer
Hinweistext in Systemeinstellungen (rpi-check.service → tesm-check.service).
|
||
|
|
34d9cdebd6 |
Netzwerkeinstellungen: Mehrere Interfaces korrekt unterstützen
- Kritischer Bug behoben: _apply_network_config() baute die netplan- Override-Datei bei JEDEM Speichern komplett neu auf (nur mit dem gerade bearbeiteten Interface) statt bestehende Einträge anderer Interfaces zu erhalten -- ein zweites Interface einzurichten hat dadurch die statische Konfiguration des ersten aus der Datei geworfen (fiel auf DHCP zurück). Jetzt wird die vorhandene Datei eingelesen und nur der Block des gespeicherten Interfaces ersetzt/ergänzt. - Live auf POETEST reproduziert und nach dem Fix verifiziert: enp1s0 verlor durch den Bug seine statische 192.168.80.202/24 (fiel auf DHCP zurück), während enp3s0 (zweites, neu eingerichtetes Interface) 192.168.82.202/24 behielt. Nach dem Fix enp1s0 über die echte GUI-Route wiederhergestellt (Werte aus dem aktiven DHCP-Lease bestätigt) -- beide Interfaces stehen jetzt nebeneinander in derselben netplan-Datei. - Neuer JSON-Endpunkt /settings/network_state + JS: die Eingabemaske (Modus/IP/Prefix/Gateway/DNS) lädt beim Umschalten des Interface-Dropdowns jetzt dessen tatsächlichen Ist-Zustand nach, statt weiter die Werte des zuvor angezeigten Interfaces zu zeigen. - Die Übersicht ganz oben auf der Karte zeigt jetzt JEDES aktive Interface gleichzeitig (eigene Zeile mit IP/Gateway/Modus/DNS) statt nur das gerade im Formular ausgewählte. - net_interface-Setting wird jetzt tatsächlich persistiert (war vorbereitet, aber nie geschrieben), damit die Seite nach einem Reload das zuletzt bearbeitete statt immer nur das alphabetisch erste Interface vorbelegt. |
||
|
|
3ab3407a18 |
Login-Seite überarbeitet, TESM-Logo als echter Hintergrund; SSH-MOTD-Banner mit Live-Daten
- Login-Seite: WiS-/TESM-Logo und "Melde dich an, um fortzufahren" aus der Box entfernt; die TESM-Wortmarke mit Funktionszeile ist jetzt der tatsächliche Seitenhintergrund (absolut positioniert, hinter allem), die Box liegt zentriert davor. Nebenbei zwei echte Bugs gefunden und behoben: fehlendes Cache-Busting auf login.html (asset_url() wie in base.html, sonst hält nginx' 7-Tage-Cache eine alte style.css/app.js fest) und ein durch den fehlenden align-items-Wert auf die volle Höhe gestrecktes Login-Formular. - Neuer SSH-Login-Banner (dynamisches MOTD) mit Live-Flottenstatus statt der Standard-Ubuntu-Willkommensmeldung: srv/tesm/motd_banner.py liest Online/Offline- und DHCP-Status aus denselben Quellen wie das Web-Dashboard (get_device_status()/_dhcp_read_leases()), eingebunden über etc/update-motd.d/50-tesm. install.sh deaktiviert dabei die Standard- Ubuntu-MOTD-Skripte (chmod -x, reversibel) und installiert den neuen Hook. |
||
|
|
d1b10bd970 |
Rebrand to TESM: infra rename, LDAP/AD login, selective import/export, Papierkorb
- Vollständiges Rebranding "PoE Manager" -> TESM (TimEShepManager): neue Logos/Favicon (theme-aware), Sidebar/Login-Branding, Copyright-Zeile. - Infrastruktur-Umbenennung: srv/poe_manager -> srv/tesm, alle POE_*-Env-Vars -> TESM_* (POE_SCRIPT bewusst unverändert, echtes PoE-Skript), systemd Units poe_web/rpi-check* -> tesm/tesm-check*, nginx-Site, netplan/logrotate Configs, Gitea-Repo alientim/Aruba-PoE-Modern -> alientim/tesm. - LDAP/Active-Directory Same-Sign-On: Search+Bind, AD-Gruppen->App-Gruppen- Zuordnung (additiv), Konto-Sperren, AD-Vorab-Suche/-Anlage, eigene Einstellungsseite. - Selektives Import/Export (8 Kategorien, Zwei-Schritt-Vorschau) mit eigenem R/E/X-Rechtemodell (X = Export, getrennt von E = Import). - Papierkorb: Soft-Delete statt Hard-Delete für Geräte/Switche/Zugangsdaten/ Benutzer/Gruppen (AD-Nutzer ausgenommen), eigene Seite unterhalb Wartung, konfigurierbare Aufbewahrungsfrist. - rpi_ip -> ip Spalten-/Code-Umbenennung (Geräte sind längst nicht mehr auf Raspberry Pis beschränkt). - README auf aktuellen Stand gebracht. |
||
|
|
49634da0d9 |
Aufräumen: totes/unbenutztes entfernt, Beispiele verallgemeinert
- create_user.py entfernt: legte nur Nicht-Admin-Benutzer per CLI an, vollständig durch die Web-UI (Einstellungen -> Benutzer, unterstützt zusätzlich Vor-/Nachname und Gruppenzuordnung) abgedeckt und von nirgends (install.sh, README, systemd-Units) referenziert - Ungenutzten Flask-Import send_from_directory entfernt (nie aufgerufen) - Drei ungenutzte CSS-Regeln entfernt (in keinem Template/JS mehr referenziert): .btn-ghost, .dc-status (Karten-Layout hat kein .dc-status-Element mehr), .nav-divider - Platzhalter für die manuelle DHCP-Reservierung nutzte als einziger im ganzen Projekt "192.168.80.x" statt der überall sonst verwendeten generischen Beispiel-Range "192.168.1.x" -- war ein Rest aus dem Testen gegen die konkrete Test-VM, jetzt vereinheitlicht - Zwei nicht versionierte, lokale Log-Dateien (mini.log, server.log) aus dem Projektverzeichnis entfernt Systematisch geprüft und dabei nichts Weiteres gefunden: alle 16 Templates werden tatsächlich gerendert, alle Imports (bis auf den einen oben) werden verwendet, keine toten Hilfsfunktionen in app.py (die drei Kandidaten aus einer AST-Analyse -- __getattr__, avatar_url, display_name -- sind über Jinja-Attributzugriff bzw. als Python-Dunder-Methode tatsächlich in Gebrauch), requirements.txt enthält nur echte direkte + transitive Abhängigkeiten. Live verifiziert: alle 14 Hauptseiten liefern nach dem Deploy weiterhin HTTP 200, keine Fehler im journalctl-Log. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
7b30ebce7b |
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> |
||
|
|
d525ef2e0a |
poe.sh: parallele Geräte-Prüfung + echte PoE-Erfolgskontrolle
Nach einem echten Import (46 Geräte, 8 Switche aus dem Altsystem) zeigten sich zwei Probleme in der Hintergrund-Prüfschleife: - Geräte wurden strikt sequentiell geprüft (eine einzige "while read"- Schleife) -- ein einzelnes ausgefallenes Gerät mit ebenfalls nicht erreichbarem Switch (bis zu 5s expect-Timeout je Verbindungsschritt) verzögerte die Prüfung aller nachfolgenden Geräte. Jede Geräte-Prüfung läuft jetzt als eigener Hintergrund-Job (& + wait), über Process Substitution statt einer Pipe, damit wait die Jobs auch tatsächlich erfasst (bei "cmd | while ...; done" liefe die Schleife sonst in einer eigenen Subshell). - disable_poe()/enable_poe() haben unabhängig vom tatsächlichen Ergebnis der SSH/expect-Sitzung immer wie ein Erfolg gewirkt -- lief die Verbindung in einen Timeout (Switch nicht erreichbar, falsche Zugangsdaten, falscher Port), endete das expect-Skript trotzdem mit Exit-Code 0, poe.sh loggte fälschlich "PoE deaktiviert"/"PoE aktiviert", obwohl am Switch nichts passiert war. Jeder kritische expect-Schritt hat jetzt einen eigenen timeout-Zweig mit explizitem "exit 1"; poe.sh wertet den Rückgabewert aus und loggt bei einem nicht erreichbaren/ablehnenden Switch stattdessen klar, dass die PoE-Aktion fehlgeschlagen ist. Gilt für die Prüfschleife, den manuellen Neustart (poe.sh restart <mac>) und damit auch den Dashboard-Neustart-Button. Live verifiziert: alle 46 Geräte innerhalb von ~2s geprüft (statt sequentiell), alle 43 offline erkannten Geräte mit Switch-Zuordnung zeigten korrekt die neue Fehlermeldung, da die realen Switch-IPs vom Test-Host aus nicht erreichbar sind -- vorher hätte das fälschlich als 43x "PoE deaktiviert/aktiviert" geloggt. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
3b2a5fc76c |
Wartung: Online-Watchdog nach Neustart, Status-Reset, Zugangsdaten-Spalte
- Nach einem ausgelösten SSH-Neustart blieb der Job-Status bisher für immer auf "Neustart ausgelöst" stehen -- ergänzt um einen Ping- Watchdog (gleiche Invocation wie das bestehende Monitoring in poe.sh: "ping -c 1 -W 2"), der nach einer kurzen Schonfrist (15s) regelmäßig prüft, bis das Gerät wieder antwortet (Timeout 5min). Status wechselt dann sichtbar auf "wieder online" bzw. auf einen Fehler bei Zeitüberschreitung. - Ein abgeschlossener ERFOLGREICHER Job wird bei jedem (erneuten) Laden der Wartungsseite zurückgesetzt (wieder "Noch keine Aktion") -- ein Erfolg ist nur relevant, solange man ihn live mitverfolgt. Fehler bleiben bewusst bis zur nächsten Aktion sichtbar, um sie nicht durch einen einfachen Reload zu übersehen. - Clients-Tabelle zeigt jetzt wie die Switche-Tabelle eine "Zugangsdaten"-Spalte (Name + Username oder "— keine —"). Live gegen das dedizierte Testsystem verifiziert: zwei echte Neustarts, beide per uptime -s bestätigt (kein False-Positive-Ping gegen die noch nicht heruntergefahrene alte Instanz), Status korrekt von "wartet auf Online" auf "wieder online" gewechselt; Status-Reset beim Seitenaufruf ebenfalls live bestätigt (sichtbar vor, leer nach einem GET /maintenance). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
8772f7777b |
Wartung: SSH-Bulk-Update & -Neustart für Linux-Clients
Neue Seite "Wartung" (Geräte-Bereich, eigenes Rechtepaar maintenance.view/maintenance.edit), komplett getrennt vom PoE-Neustart auf dem Dashboard: - Dashboard-Neustart bleibt ausschließlich PoE-Reset über den Switch - Wartung läuft immer per SSH direkt auf dem Gerät, unabhängig von jeder Switch-Zuordnung -- nur Geräte mit SSH-Zugangsdaten der Kategorie "Linux-Client" erscheinen dort (Windows/PowerShell folgt als eigener, separater Schritt -- kein Testsystem dafür verfügbar) Backend: - _run_ssh_command()/_run_ssh_reboot(): nicht-interaktive SSH-Ausführung mit paramiko.RejectPolicy() gegen dieselbe known_hosts-Datei wie das interaktive Browser-Terminal -- ein Host muss vorher einmal per "Verbindung testen" bestätigt worden sein, da hier (anders als im Terminal) niemand eine "yes/no"-Rückfrage beantworten kann - _apt_upgrade_command(): "apt update && apt upgrade -y" nicht- interaktiv mit DEBIAN_FRONTEND=noninteractive und --force-confdef/--force-confold, damit ein Paket-Postinst-Skript nicht auf eine nie kommende Config-Rückfrage wartet - Reboot behandelt einen abrupten Verbindungsabbruch NACH dem Kommando als Erfolg (die Maschine fährt herunter, bevor sie antworten kann); nur ein Fehler VOR dem Reboot-Kommando zählt als echter Fehler - Job-Status lebt im Prozessspeicher (wie das SSH-Terminal), Live- Anzeige per Polling (/maintenance/status) Live gegen ein dediziertes Testsystem (getrennt vom gemeinsam genutzten App-Host, um dort kein echtes apt upgrade auszulösen) verifiziert: echtes "apt update && apt upgrade" inkl. Kernel-/systemd-/ netplan-Paketen lief vollständig durch, anschließender SSH-Neustart hat das System nachweislich neu gestartet (uptime nach dem Job bestätigt). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
63ca7782ba |
Geräte: SSH-Zugangsdaten mit Kategorien, PoE-Neustart nur wenn möglich
- credentials: neue Spalte 'category' (Switch/Linux-Client/ Windows-Client/Router), Grundlage für die geplante SSH-basierte Wartungsfunktion (Bulk-Updates); Löschschutz prüft jetzt zusätzlich devices.credential_id, nicht mehr nur switches.credential_id - devices: optionale SSH-Zugangsdaten + SSH-Port je Gerät, inkl. "Verbindung testen"-Terminal wie bei Switches - Dashboard: PoE-Neustart-Button wird für Geräte ohne zugewiesenen Switch/Port komplett ausgeblendet (nicht nur ausgegraut), da für diese kein PoE-Reset über poe.sh möglich ist; Button bleibt ausschließlich für den PoE-Neustart über den Switch zuständig - Schließen-Button im Geräte-Modal immer rechts, unabhängig davon welche Aktions-Buttons gerade sichtbar sind - "Port" im Geräte-Formular zu "Switchport" umbenannt und von "SSH-Port" klar abgegrenzt (unterschiedliche Konzepte: physischer Switch-Port fürs PoE vs. TCP-Port für SSH) - .btn:disabled greift jetzt echtes Grau statt nur reduzierter Deckkraft der jeweiligen Button-Farbe Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
d3cf78592f |
Fix: 'host-name'-Option kollidierte mit dem nativen Reservierungs-Hostname
Der Nutzer fragte zu Recht nach, ob eine Reservierung mit gleichzeitig gesetztem hostname-Feld (aus dem Namen abgeleitet, z.B. 'unraid') UND einem option-data-Override fuer 'host-name' (Code 12, z.B. 'test') so beabsichtigt ist. Laut Kea-Dokumentation beantwortet Kea die Host-Name- Anfrage eines Clients bei einer Reservierung automatisch mit deren eigenem hostname-Feld -- der zusaetzliche Options-Override fuer dieselbe Option war eine unbeabsichtigte Dopplung/Quelle fuer Widersprueche, keine zusaetzliche Funktion. 'host-name' aus den 56 verbleibenden Standard-Optionen entfernt (war eine von 57), neue Migration v3 loescht eine bereits geseedete/gesetzte host-name-Zeile samt Wert aus bestehenden Installationen nach. Live auf der Test-VM verifiziert: Option ist verschwunden, die 'unraid'-Reservierung enthaelt jetzt nur noch hw-address/ip-address/hostname ohne option-data, kea-dhcp4 -t weiterhin erfolgreich. |
||
|
|
255308f81b |
App-weite Warnung bei ungespeicherten Aenderungen + Options-UI-Fix + Cache-Busting
1. Generischer Ja/Nein-Dialog bei ungespeicherten Aenderungen (kein Opt-in pro Seite noetig): initUnsavedChangesGuard() erkennt automatisch jedes Formular mit echten Eingabefeldern, das seit dem Laden der Seite veraendert wurde, und fragt beim Klick auf einen Link (z.B. Sidebar- Navigation), ob zuerst gespeichert werden soll -- eigener Dialog (confirmSaveDiscard) statt des bestehenden confirmAction (dort ist "Abbrechen" = Bleiben, hier gibt es bewusst nur die zwei angefragten Optionen: speichern oder verwerfen, beide verlassen die Seite). "Ja, speichern" sendet das Formular ganz normal ab (echter POST) und merkt sich das eigentliche Ziel in sessionStorage, um danach automatisch dorthin weiterzuleiten. Nachtrag noch in dieser Session gefixt: ein normales Speichern ueber den Seiten-eigenen Button loeste zusaetzlich die native Browser-Warnung (beforeunload) aus, obwohl die Aenderung bereits gespeichert wurde -- navigatingAway-Flag wird jetzt bei jedem Submit eines beobachteten Formulars (eigener Button ODER eigener Ja/Nein-Dialog) gesetzt, die generische Browser-Warnung erscheint dadurch nur noch bei tatsaechlich ungefragter Navigation (Tab schliessen, Reload, Adresszeile). Live per Playwright in drei Szenarien verifiziert (direktes Speichern, Ja via eigenem Dialog, Nein via eigenem Dialog) -- kein Browser-Popup in den ersten beiden Faellen, korrektes Redirect zum urspruenglichen Ziel nach "Ja". 2. DHCP-Options-Dropdown (Reservierungs-Dialog) verschiebt ein neu ausgewaehltes Options-Feld jetzt direkt unter das Dropdown (in einen .poe-added-options-Container) statt es nur an seiner urspruenglichen Stelle in der bis zu 57 Eintraege langen Liste einzublenden -- lag dort oft ausserhalb des sichtbaren Modal-Ausschnitts und wirkte, als waere nichts passiert. Bereits gesetzte Overrides stehen von Anfang an im selben Container, weitere haengen sich beim Auswaehlen unten an. 3. Cache-Busting fuer app.js/style.css (asset_url()-Helper, haengt die Aenderungszeit der Datei als ?v=... an) -- ein im Browser gecachtes, veraltetes app.js konnte nach einem Update sonst den Eindruck erwecken, eine neue Funktion wuerde "nicht funktionieren", obwohl der Code auf dem Server laengst aktuell war. Alle drei live auf der Test-VM deployt und per Playwright verifiziert, keine Fehler im journalctl-Log. (Testartefakt aufgeraeumt: der echte Host-Hostname wurde durch die Playwright-Tests versehentlich auf "directsave-test"/"navsave-test" geaendert und wieder auf POETEST zurueckgesetzt.) |
||
|
|
1a0b55ed44 |
DHCP: manuelle Reservierungen, Options-Schutz+Dropdown-UI, Lease-Countdown-Fix
1. Manuelle Reservierungen (dhcp_manual_reservations) fuer Geraete, die nicht als Client in dieser App gepflegt werden -- bewusst eine eigene, explizite Aktion (MAC/IP/Name), damit kein externes Geraet unueberwacht automatisch eine feste IP bekommt. _dhcp_reservation_candidates fuehrt automatische (aus devices) und manuelle Reservierungen jetzt zusammen, case-insensitiv nach MAC dedupliziert (automatisch gewinnt bei Konflikt). Automatische Reservierungen sind in der Tabelle nicht loeschbar (Schloss-Symbol statt Button, sie entstehen/verschwinden mit den Client-Stammdaten), manuelle schon. Live verifiziert: Hinzufuegen, doppelte MAC ablehnen, ungueltige MAC ablehnen, generierte Kea-Config mit gemischten Auto-/manuellen Reservierungen besteht kea-dhcp4 -t. 2. Standard-DHCP-Optionen um 10 weitere verifizierte Optionen erweitert (u.a. host-name/12 -- Kea-Name mit Bindestrich, NICHT "hostname", live gegen echtes Kea geprueft) -- jetzt 57 insgesamt. Standard-Optionen sind nicht mehr loeschbar (Backend lehnt es ab, UI zeigt ein Schloss-Symbol statt Loeschen-Button) -- vorher liess sich eine vorbefuellte Options- Definition versehentlich entfernen. 3. Options-Tabellen (global UND pro Reservierung) zeigen jetzt nur noch tatsaechlich genutzte Options -- eigene immer, Standard-Optionen nur mit gesetztem Wert. Weitere Standard-Optionen kommen ueber ein Dropdown "+ Option hinzufuegen" dazu statt permanent alle 57 als leere Felder zu zeigen. Das neu eingeblendete Feld wird automatisch in den sichtbaren Bereich gescrollt und fokussiert (bei bis zu 57 moeglichen Optionen kann das Feld sonst weit unterhalb des sichtbaren Modal-Ausschnitts liegen und es wirkt so, als waere nichts passiert). 4. Lease-Countdown-Fix: die Ablaufzeit wird jetzt explizit als UTC ausgewiesen (Kea arbeitet intern in UTC-Epoch) und zusaetzlich als live tickender Countdown angezeigt (gleiches Prinzip wie der bestehende "Naechste Pruefung"-Timer). Vorher wirkte eine reine Absolutzeit auf der nicht selbst aktualisierenden Seite bei den kurzen Standard-Lease-Zeiten (600s) schnell "schon abgelaufen", sobald etwas Zeit vergangen war, bis man tatsaechlich hinschaut -- live im echten Betrieb genau als dieses Missverstaendnis beobachtet und anhand der rohen Kea-CSV verifiziert, dass die richtige Spalte (expire) korrekt gelesen wird. 5. UI-Feinschliff: "In Datei schreiben" bei den Reservierungen heisst jetzt "Speichern" und ist der primaere (orange) Button, "Reservierung hinzufuegen" sekundaer -- das Hinzufuegen legt nur einen Datensatz an, erst "Speichern" schreibt die tatsaechlich wirksame Kea-Config. Alles live auf der Test-VM deployt und verifiziert (Playwright fuer die Dropdown-Sichtbarkeit/Fokus-Interaktion, curl fuer die Backend-Validierung, kea-dhcp4 -t fuer die generierte Konfiguration), keine Fehler im journalctl-Log. |
||
|
|
9885a579ea |
DHCP: Subnetze deaktivierbar + 47 verifizierte Standard-Optionen
1. Subnetze koennen einzeln deaktiviert werden (enabled-Spalte), ohne sie loeschen zu muessen -- werden dann bei "In Datei schreiben" und "Aktivieren & (neu) starten" komplett uebersprungen. Bewusst nicht als "ungueltig" gewertet (kein Fehler/keine Warnung), da eine deaktivierte Zeile absichtlich kein echtes Netz braucht. Toggle-Button in der Subnetz-Tabelle, deaktivierte Zeilen abgeblendet mit "Deaktiviert"-Pill. 2. 47 kuratierte Standard-DHCP-Optionen vorbefuellt (analog den "Predefined Options" eines Windows-DHCP-Servers) -- Router/DNS-Server/ Domain-Name/Lease-Zeiten ausgenommen, dafuer gibt es bereits eigene Felder. Alle Namen live gegen eine echte Kea-2.4.1-Instanz verifiziert: jede der 49 Kandidaten-Optionen einzeln per kea-dhcp4 -t getestet, 47 akzeptiert (2 falsch geratene Namen verworfen: nis-domain-name statt nis-domain, nisplus-domain statt nisplus-domain-name). Wichtig fuer Kea-Kompatibilitaet: Standard-Optionen bekommen KEIN eigenes option-def in der generierten Konfiguration (neue is_standard- Spalte in dhcp_option_defs) -- Kea kennt sie bereits nativ, eine Neudefinition haette die eingebaute dupliziert. Nur echte eigene/ herstellerspezifische Options bekommen weiterhin ein option-def. Live verifiziert: option-def bleibt bei gesetztem Standard-Options-Wert leer, option-data enthaelt den Wert korrekt, kea-dhcp4 -t besteht, echter Dienstneustart mit der Config erfolgreich. Einmalige Migration (Guard wie bei den anderen Schema-Migrationen) -- ein vom Admin bewusst geloeschter Standard-Eintrag wird nach einem Neustart nicht stillschweigend wieder angelegt. Ausserdem in dieser Session zuvor gefixt (siehe vorherige Commits): der falsche Kea-Schluessel lease-file->name, der jeden Dienststart blockierte, sowie der 'Jetzt pruefen'-Button, der den Topbar-Countdown nur auf dem Dashboard aktualisierte. |
||
|
|
182457629d |
Fix: 'Jetzt pruefen' aktualisierte Topbar-Countdown nur auf dem Dashboard
Der Klick loeste den Neustart von rpi-check.service und damit einen echten, sofortigen Pruef-Durchlauf immer korrekt aus -- nur die Topbar-Anzeige 'Naechste Pruefung in Xs' wurde ausserhalb des Dashboards nie aktualisiert, da nur index.html einen Listener fuer das poe:check-triggered-Event registrierte. Auf allen anderen Seiten (DHCP, Switches, Settings, ...) lief der Countdown deshalb sichtbar mit dem alten Zeitpunkt weiter, obwohl im Hintergrund laengst neu geprueft wurde. Fix (klein gehalten, kein neuer Endpunkt): run_check_now() wartet kurz auf den ersten Log-Eintrag des frisch gestarteten Dienstes und liefert den neuen Zeitpunkt direkt in seiner bestehenden JSON-Antwort mit. app.js schreibt diesen Wert danach unabhaengig von der aktuellen Seite ins global-timer-pill-Element -- der laufende Countdown liest seine Werte ohnehin bei jedem Tick frisch aus dem DOM. Live auf der Test-VM verifiziert (mehrere echte Durchlaeufe ueber /check/run-now, frischer last_run_epoch_ms in der Antwort, keine Fehler im journalctl-Log). |
||
|
|
bf9b816fa2 |
DHCP: Mehrfach-Subnetze, aktive Leases-Anzeige, Reboot-Persistenz
1. Mehrere gleichzeitige DHCP-Subnetze statt einer einzelnen globalen Range
-- ein Host kann mehrere IPs/Interfaces mit jeweils eigenem Netz haben,
fuer die alle DHCP angeboten werden soll:
- Neue Tabelle dhcp_subnets (interface, range_start/end, optionales
gateway/dns je Zeile) statt der bisherigen dhcp_interface/_range_*/
_gateway/_dns-Settings-Keys. Domain, Lease-Zeiten und Ausgabepfad
bleiben global (Kea-weit gueltig).
- Einmalige Migration: eine bestehende globale Einzel-Range wird beim
ersten Start automatisch in eine erste Subnetz-Zeile ueberfuehrt,
statt eine funktionierende Konfiguration beim Upgrade zu verlieren
(live verifiziert: bestehende 192.168.80.220-230-Range korrekt
uebernommen).
- Neue Routen add/edit/delete_dhcp_subnet, jede mit derselben
Sicherheitsregel wie zuvor: es muss eine physische IP im gewuenschten
Bereich vorhanden sein (_dhcp_matching_network je Subnetz-Zeile),
sonst wird die Aktion abgelehnt. _detect_interface_network wurde dafuer
in eine neue _detect_interface_networks (alle IPv4-Adressen eines
Interfaces, nicht nur die erste -- ein Interface kann mehrere IPs
tragen) plus einen duennen Wrapper fuer die primaere Adresse aufgeteilt.
- _render_kea_config generiert jetzt einen eigenen subnet4-Block je
Subnetz (eigenes Gateway/DNS-option-data), _dhcp_reservation_candidates
ordnet jedes Geraet anhand seiner IP dem richtigen Subnetz zu.
- "In Datei schreiben" ueberspringt inzwischen ungueltig gewordene
Subnetze einzeln (mit Warnung, welche) statt die ganze Aktion
abzubrechen; "Aktivieren & (neu) starten" verweigert den Start, falls
kein einziges gueltiges Subnetz mehr existiert.
- Live auf der Test-VM verifiziert: Anlegen/Bearbeiten/Loeschen inkl.
Ablehnung ungueltiger Ranges, generierte Kea-Config mit zwei
subnet4-Bloecken korrekt (Interface, Subnet-CIDR, Pools je Zeile).
2. Aktive Leases direkt aus Kea sichtbar (settings_dhcp.html, neue Karte)
-- macht Clients OHNE eigene Reservierung sichtbar, die sich einfach
eine freie IP aus dem Pool genommen haben, statt nur die (unvollstaendige)
Reservierungsliste zu zeigen. Liest die memfile-Lease-CSV direkt
(kein Kea-Control-Agent noetig), reserved-Flag durch Abgleich mit den
bekannten Reservierungs-MACs. Live gegen eine echte (leere)
kea-leases4.csv verifiziert.
3. DHCP-Dienst uebersteht einen Host-Neustart jetzt korrekt im zuletzt
bewusst gewaehlten Zustand: "Stoppen" deaktiviert den Dienst zusaetzlich
(nicht nur systemctl stop), sonst wuerde systemd ihn nach einem Neustart
automatisch wieder hochfahren, obwohl der Admin ihn bewusst abgeschaltet
hat. Dieselbe Ergaenzung im automatischen Netzwerkaenderungs-Stop (siehe
vorheriger Commit) -- sonst koennte ein Host-Neustart vor der
Range-Pruefung den Dienst trotzdem mit einer ggf. falschen Konfiguration
wieder starten.
|
||
|
|
efda77f813 |
Fix: falscher Kea-Schluessel lease-file statt name blockierte jeden Dienststart
Live reproduziert: 'kea-dhcp4 -t' lehnte JEDE generierte Konfiguration mit 'got unexpected keyword "lease-file" in lease-database map' ab, sobald mindestens ein Subnetz konfiguriert war -- der Dienst liess sich dadurch nie starten, unabhaengig von der Anzahl/Gueltigkeit der Subnetze selbst. Kea nennt den Datei-Pfad des memfile-Lease-Backends 'name', nicht 'lease-file'. Nach dem Fix: 'kea-dhcp4 -t' besteht, Dienststart auf der Test-VM mit einem echten Subnetz verifiziert (DHCP4_STARTED, Dienst aktiv). |
||
|
|
9660258940 |
Fix: DHCP-Range wurde nie gegen das tatsaechliche Netz geprueft
Auf der Test-VM liess sich der Dienst mit einer gespeicherten Range (192.168.81.203-212) starten, obwohl das erkannte Netz des Interfaces 192.168.80.0/24 war -- die Range gehoerte also zu einem komplett anderen Subnetz. _dhcp_config_is_complete() prophfte bisher nur, ob Range Start/ Ende ueberhaupt ausgefuellt waren, nie ob sie zum echten Netz passen. Neue Funktion _dhcp_matching_network(interface, start, end) validiert die Range jetzt an drei Stellen: beim Speichern (save_dhcp_config), beim Schreiben der Kea-Datei (write_dhcp_file) und beim Starten des Dienstes (dhcp_service_action=enable_restart) -- jede der drei Aktionen wird mit einer genauen Fehlermeldung abgelehnt, statt eine zum Netz nicht passende Range durchzulassen. Die Pruefung beruecksichtigt dabei ALLE IPv4-Adressen des Interfaces, nicht nur die erste -- ein Interface kann mehrere IPs/Subnetze gleichzeitig tragen (Alias-IPs). Dafuer wurde _detect_interface_network() in eine neue _detect_interface_networks() (Mehrzahl, alle Adressen) plus einen duennen Wrapper fuer die primaere/erste Adresse aufgeteilt -- alle bisherigen Aufrufer (Anzeige auf der DHCP-Seite, Host-Netzwerkeinstellungen) bleiben unveraendert auf der primaeren Adresse. Echte Mehrfach-Ranges gleichzeitig (mehrere Subnetze parallel, z.B. auf verschiedenen Interfaces) sind bewusst nicht Teil dieses Fixes -- laut Ruecksprache ein separater, groesserer Umbau (DB-Tabelle statt globaler Range, mehrere subnet4-Bloecke in der Kea-Config, Listen-UI) und als naechster Schritt vorgesehen. Live auf der Test-VM verifiziert: falsche Range wird beim Speichern, Schreiben und Starten korrekt abgelehnt, eine zum erkannten Netz passende Range weiterhin akzeptiert -- keine Fehler im journalctl-Log. |
||
|
|
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. |
||
|
|
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). |
||
|
|
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. |
||
|
|
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. |
||
|
|
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>
|
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
bb3a6ab3a5 |
Gruppen-Rechtetabelle: Einrückung der Unterpunkte auf 48px erhöht
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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>
|
||
|
|
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> |
||
|
|
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> |
||
|
|
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>
|
||
|
|
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> |
||
|
|
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>
|
||
|
|
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> |
||
|
|
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> |
||
|
|
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>
|
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |