Commit Graph
73 Commits
Author SHA1 Message Date
adminandClaude Opus 5.5 6e4caf7e95 Struktur & Workspaces für root, Ziel-OE am Installationsmedium (Schritt F)
Admin-Reiter je Organisation: OE-Baum als Karten mit Geräten/Distros,
eigenem bzw. geerbtem Workspace, Workspace zuweisen (automatisch oder
Version pinnen) und Unter-OEs anlegen; Ablehnungen der Distro-Regel
werden mit Begründung angezeigt. Reiterleiste bricht jetzt um.
Kundenseite: Struktur zeigt den Workspace je OE, Gerät verschieben zeigt
die Ablehnung statt sie zu verschlucken, Installationsmedium mit
Ziel-OE (gespeichert, Konflikt mit dem Profil verständlich gemeldet).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:12:35 +02:00
adminandClaude Opus 5.5 9dccf7dd79 Migration 0008: Ziel-OE in der Installationsmedium-Konfiguration
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:12:21 +02:00
adminandClaude Opus 5.5 e390f58bb7 Workspace-Detailseite mit Versionen, Entwurf und Verwaltung (Schritt E)
Veröffentlichte Version nur lesend, Bearbeiten legt einen Entwurf an.
Im Entwurf: Inhalt wie im Assistenten (auf der Distro nicht umgesetzte
Einträge gesperrt), Webapps aus Vorlage oder frei, Wirkungsvorschau
(+/− je Bereich, wie viele Geräte automatisch umsteigen, gepinnte,
Profile), Veröffentlichen mit Notiz, Verwerfen. Dazu Nutzung,
Versionsliste, Name/Zweck, Vorlage und Sichtbarkeit (alle, Kundengruppen,
Organisationen), Kopieren mit Vorab-Prüfung für andere Distro, Löschen
nur wenn ungenutzt mit Bestätigung.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:34:40 +02:00
adminandClaude Opus 5.5 b4c9ea3b00 Assistent Workspace erstellen (Schritt D), Theme-Init CSP-konform
Sechs Schritte: Name/Zweck/Eigentümer, Apps, Webapps, Richtlinien
(aufklappbar wie im Katalog), Distro, Prüfen. Mitlaufende Distro-Leiste
zeigt, welche Distro noch möglich ist; bei Konflikten nennt Schritt 5 die
fehlenden Einträge mit Alternativen aus derselben Kategorie und
Entfernen-Knopf. Ohne JavaScript ein langes Formular, der Server prüft
ohnehin. Workspace-Liste verweist auf den Assistenten statt auf das alte
Schnellformular.

Nebenbei gefunden: das Inline-Skript in base.html, das ein manuell
gewähltes Hell-/Dunkel-Design vor dem CSS setzt, wurde von der eigenen
CSP blockiert - jetzt static/theme-init.js.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 12:12:14 +02:00
adminandClaude Opus 5.5 5e80cc40c6 Gruppen ohne eigenen Workspace, Admin-Liste mit Distro/Version/Vorlage
Workspace-Zuweisung an Gerätegruppen entfällt (Kunden- und Admin-Seite),
Gruppen ergänzen nur noch. Workspace-Liste zeigt Distro, Version,
Eigentümer und Vorlagen-Status, beim Anlegen wählbare Distro.
Workspace-Creator neu, Schritt C.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 11:54:37 +02:00
adminandClaude Opus 5.5 3c7a7e49f3 Bereich Datenbank geht in Workspaces auf (Reiter Workspaces und Vorlagen)
Workspace-Liste unter /admin/workspaces/liste, Merkmale, Kategorien,
Webapp-Vorlagen und Distro-Abdeckung (frühere Flottenmatrix) unter
/admin/workspaces/vorlagen. Gemeinsame Unternavigation, Menüpunkt
Datenbank entfällt, alte /admin/technik-Adressen leiten weiter
(POST mit 307). Workspace-Creator neu, Schritt B.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 11:45:37 +02:00
adminandClaude Opus 5.5 bf1470a3ff Neuer Bereich Workspaces für root, Reiter Katalog
Katalog ersetzt die Flottenmatrix als Übersicht: Apps, Webapps und
Konfigurationsrichtlinien aufklappbar nach Kategorien, je Eintrag Icon
(Fallback Zahnrad) und Distro-Verfügbarkeit aus den Blueprints.
Kategorien bekommen eine Art (App/Richtlinie), Webapp-Vorlagen eine
Kategorie. Workspace-Creator neu, Schritt A.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 11:28:51 +02:00
adminandClaude Opus 5.5 57221bb180 Workspace-Creator kundenseitig sperren, Einrichtung vorerst nur durch root-admin
Menüpunkt ausgegraut, /workspace zeigt nur einen Hinweis plus die
bereitstehenden Workspaces (lesend), alle schreibenden Endpunkte liefern
403. Zuweisung an Gerätegruppen bleibt offen. Freischalten über
WORKSPACE_CREATOR_FREIGEGEBEN in routers/workspace.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 08:11:25 +02:00
adminandClaude Opus 5.5 19460174c1 Installationsmedium: Optionen Offline-Medium und Neuinstallation ohne Rueckfrage
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 12:17:31 +02:00
adminandClaude Opus 5.5 ee9fefce67 Vorlagen-Formular: Partitionierungsschema aus den Haken ableiten, Serverfehler anzeigen
Angehakte /home-/var-Partitionen wurden ohne Hinweis verworfen, wenn das
separate Auswahlfeld auf 'Einfach' stand (Kunden-ISO-Test 02.10.2026).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 14:51:44 +02:00
adminandClaude Opus 5.5 ee3f6596c5 Admin-Reiter Diagnosezugang pro Organisation, Hash an ISO-Build (ADR-0035)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 14:18:26 +02:00
adminandClaude Sonnet 5 a196fd2ea4 Admin-GUI: custom-Partitionierungsschema in Bereitstellungsvorlagen-Formular
Neues Schema-Dropdown (single/custom) + feste Auswahl fuer /home, /var,
/usr (Aktivieren-Checkbox + Prozent + Dateisystem je Mountpoint) - bewusst
keine freie Mountpoint-Liste, einfacher serverseitig zu validieren und
deckt die konkret gewuenschten Faelle ab. Admin-Tabelle zeigt das
gewaehlte Schema jetzt mit an.

Teil der E2E-Testrunde, siehe platform-docs/roadmap/
e2e-testrunde-lokale-adressierung.md Phase 3. Gegenstueck in
provisioning-server (Validierung, DB-Persistenz) ist dort bereits
committet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DWKevK2QUK5tsGmqfSD6Yy
2026-09-19 14:52:23 +02:00
adminandClaude Sonnet 5 de4a6ea819 Phase 6: App-Icons + Beschreibungen in Merkmale-Verwaltung und Flottenmatrix
Neuer Proxy /admin/technik/merkmal-icons/{filename} (gleiches Muster wie
der bestehende Webapp-Icon-Proxy, anode kennt seine öffentliche Adresse
nicht) - anders als dort aber mit tatsächlich durchgereichtem Content-
Type statt fest angenommenem "image/x-icon", weil der Bildtyp hier
wirklich variiert (Simple-Icons-SVG vs. Favicon-ICO/PNG/JPG). Icons sind
klein genug (Obergrenze 2 MB) für einmaliges Einlesen statt echtem
Streaming.

Merkmale-Verwaltung zeigt jetzt Icon + Beschreibung sichtbar (vorher nur
im Bearbeiten-Formular versteckt), Flottenmatrix zeigt das Icon neben
Key/Name der Zeile.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-11 11:44:20 +02:00
adminandClaude Sonnet 5 830c96eaf1 Phase 5: Dashboard-Inhalt (Kunde + Admin)
Beide Platzhalter-Dashboards aus Phase 1 bekommen jetzt echten Inhalt -
Nächste-Schritte-Checkliste, Flotten-Gesundheit (Balken für Platten-/
RAM-Auslastung, Liste überfälliger Check-ins), letzte Aktionen. Holt die
Auswertung von den neuen anode-Endpunkten (provisioning-server), ergänzt
kundenseitig nur die Installationsmedium-Prüfung (iso_build_id lebt in
installationsmedium_konfiguration, kundenplattforms eigener DB).

Grafiken bewusst als einfache CSS-Balken (.balken/.balken-fuellung in
base.html) - kein Chart-Framework/CDN-Import, passt zum bestehenden
Vanilla-JS/CSS-Ansatz (table-tools.js, theme-toggle.js).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-11 11:21:45 +02:00
adminandClaude Sonnet 5 7efd9759a7 Phase 4: Kategorie-Auswahl + Kiosk-Checkbox beim Webapp-Anlegen
Formular-Ergänzung zum serverseitigen Gegenstück (provisioning-server):
optionale Kategorie-Auswahl (dieselben App-Kategorien wie bei Merkmalen)
und ein Kiosk-Modus-Kontrollkästchen. Das Kontrollkästchen ist deaktiviert
mit Hinweistext, solange der Workspace weder Chrome noch Chromium als
Merkmal hat (hat_kiosk_browser in workspace.py) - vermeidet einen
Schalter, der sichtbar existiert, aber wirkungslos wäre. Webapp-Liste
zeigt Kategorie/Kiosk-Status als kleine Badges an.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-11 11:10:45 +02:00
adminandClaude Sonnet 5 44659ae305 Autofilter auf alle sortierbaren Spalten ausgeweitet + aktiver-Filter-Farbmarkierung
Nutzer-Feedback (11.09.2026): der Excel-artige Spalten-Filter (Runde von
heute Vormittag) fehlte weiterhin auf einigen Spalten - Hostname/Kennung/
Gruppen/Letzter Check-in in der Flotte, Key/Name in den Merkmalen, sowie
allen Spalten der Flottenmatrix. Jetzt bekommt konsequent jede
data-sort-Spalte auch data-filter, wie beim Vorbild Excel (auch dort hat
jede Spalte einen Autofilter, unabhängig von der Sinnhaftigkeit im
Einzelfall).

Zweiter Wunsch: solange ein Filter tatsächlich einschränkt (nicht mehr
alle Werte angehakt), färbt sich die Titelzeile sanft in der Akzentfarbe
(neue CSS-Klasse .spalten-gefiltert, von wendeSpaltenFilterAn() gesetzt) -
das Trichter-Icon nutzt fill="currentColor" und färbt sich automatisch
mit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-11 10:58:53 +02:00
adminandClaude Sonnet 5 fb10419d88 "Diesen Standard übernehmen"-Knopf bei globalen Workspace-Standards
Schliesst die Lücke aus dem heutigen Nutzer-Feedback: Organisationen ohne
einen einzigen eigenen Workspace hatten keinen Weg mehr zu Webapps, weil
Webapps strikt an Besitz gebunden sind (fetch_sichtbaren_workspace()).
Neue Route POST /workspace/{id}/uebernehmen prüft Sichtbarkeit (global
oder eigene Organisation), ruft den neuen anode-Klon-Endpunkt auf und
leitet auf die Detailseite des neuen, voll editierbaren Workspace um.
Button dazu in workspace_detail.html, nur bei schreibgeschützten
(globalen) Standards sichtbar.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-11 10:36:13 +02:00
adminandClaude Sonnet 5 8b3943e953 Filter-Icon: Trichter-Symbol statt Dreieck (Verwechslung mit Sortierung)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-11 10:29:56 +02:00
adminandClaude Sonnet 5 383f2a1949 Fix: Filter-Icon zeigt sich jetzt immer, nicht erst ab 2 Werten
Gleicher Rueckfall wie der Bugfix vom Vortag (workspace_namen|length),
diesmal in der neuen clientseitigen baueSpaltenFilter() - Nutzer wollte
das Icon konsistent immer sehen, unabhaengig von der aktuellen
Werteanzahl in der Spalte.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-11 10:15:15 +02:00
adminandClaude Sonnet 5 70d70dbf99 Fix: Cache-Busting für /static/*-Dateien ergänzt
Live vom Nutzer gefunden: eine Aktualisierung von table-tools.js kam
nicht an, weil der Browser die alte Version unter derselben URL
gecacht hatte - kein Grund fuer den Browser, neu zu laden. Neuer
static_version-Jinja-Global (Hash ueber den tatsaechlichen Inhalt aller
static/-Dateien, bei Prozessstart berechnet), als ?v=... an pico.min.css/
table-tools.js/theme-toggle.js angehaengt - aendert sich automatisch bei
jedem Deploy, ohne manuelles Hochzaehlen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-11 10:05:34 +02:00
adminandClaude Sonnet 5 3aa653e002 Tabellen-Filter als Excel-artiger Spalten-Autofilter statt separatem Block
Nutzer-Wunsch: Filter-Icon direkt in der Titelzeile statt eines eigenen
Blocks unter der Suche. table-tools.js komplett umgebaut - ein
th[data-filter] bekommt automatisch ein ▾-Symbol, Klick oeffnet ein
Dropdown mit Checkboxen fuer jeden in dieser Spalte tatsaechlich
vorkommenden Wert (rein clientseitig aus den gerenderten Zellen
ermittelt, kein Server-Code mehr noetig). Ersetzt das aeltere,
serverseitig befuellte data-table-category-filter-Muster vollstaendig -
Merkmale/Kategorie und beide Geraete-Listen/Workspace nutzen jetzt nur
noch das Attribut, kein <details>-Block + kein workspace_namen/
Wertelisten-Handling im Router mehr noetig. Bei Gelegenheit gleich auf
OE, Status (Kunde) und Organisation (Admin) ausgeweitet, da jetzt keine
Server-Aenderung mehr pro Spalte noetig ist.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-11 09:51:45 +02:00
adminandClaude Sonnet 5 2f22b34076 Fix: Workspace-Filter fälschlich erst ab 2 verschiedenen Werten gezeigt
Nutzer-Feedback: als Org-Admin (Liebherr, aktuell alle 6 Geräte auf
'Entwickler') war der Filter unsichtbar, weil er absichtlich erst ab 2
unterschiedlichen Workspace-Namen erschien - schlechte Entscheidung,
Filter jetzt immer sichtbar sobald mindestens ein Workspace vorkommt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-11 09:43:11 +02:00
adminandClaude Sonnet 5 6d9b2f5a0d GUI-Umbau Phase 3: Kunden-Workspace-Creator
Organisations-Admins bekommen erstmals Zugriff auf die Workspace-
Komposition (bisher exklusiv /admin/technik/workspaces vorbehalten,
ADR-0028 hatte das als "zunaechst admin-only" offen gelassen).

- routers/workspace.py (Neubau, ersetzt den Phase-1-Platzhalter):
  /workspace (eigene Workspaces + bereitgestellte globale Standards,
  ueber den bestehenden ADR-0028-Endpunkt), /workspace (POST, anlegen -
  organization_id kommt nie aus dem Formular), /workspace/{id} (Detail:
  eigener Workspace -> volle Kategorie-Checkbox-Komposition, Wiederver-
  wendung von admin_technik.gruppiere_merkmale_nach_kategorie(); globaler
  Standard -> reine Leseansicht, kein Formular).
- Webapps-Verwaltung zieht von der bisherigen eigenen Seite (/webapps)
  hierher um (workspace_detail.html, nur fuer eigene Workspaces sichtbar)
  - alte Route bleibt nur noch als 301-Redirect auf /workspace bestehen.
- Neue Besitz-/Sichtbarkeitspruefung fetch_sichtbaren_workspace() (eigene
  Organisation ODER global sichtbar, sonst 404) analog zum bestehenden
  find_oe_in_organization()-Muster.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-11 09:03:04 +02:00
adminandClaude Sonnet 5 ae6a1c38c6 GUI-Umbau Phase 2: Workspace-Checkbox-Filter für Flotte-Tabellen
Nach dem in Runde 2 gebauten data-table-category-filter-Muster (gleicher
JS-Mechanismus, kein neuer Code) - Kunden- und Admin-Geräteliste bekommen
einen Checkbox-Filter nach Workspace-Name. Admin-Geräteliste hatte bisher
gar keine Workspace-Spalte, jetzt ergänzt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-11 08:59:04 +02:00
adminandClaude Sonnet 5 5b65d08e15 GUI-Neuordnung Phase 1: Navigation, Dashboard/Workspace-Platzhalter, Flows->Reiter
- Neue Primaer-Nav-Reihenfolge Kunde: Dashboard -> Organisation ->
  Workspace (neu) -> Installationsmedium -> Flotte (ans Ende verschoben).
- Neue Primaer-Nav-Reihenfolge Admin: Dashboard -> Organisationen ->
  Datenbank -> Flotte -> Admin-Accounts (Flows als eigener Punkt entfernt).
- Dashboard (Kunde+Admin) und Workspace (Kunde) als Platzhalter-Routen
  angelegt - Navigationspunkte von Anfang an klickbar, Inhalt folgt in
  spaeteren Phasen (Dashboard: Phase 5: Workspace: Phase 3).
- "Webapps" aus der Organisation-Unternavigation entfernt (zieht in
  Phase 3 in den neuen Workspace-Bereich um, vorlaeufig weiter unter
  /webapps erreichbar ueber einen Link auf der Workspace-Platzhalterseite).
- Flows: frueherer eigenstaendiger Einstiegspunkt (/admin/flows,
  Organisationswahl) entfernt (admin/flows_index.html geloescht) - neuer
  7. Reiter "Flows" auf der Organisations-Detailseite
  (admin/kunden_detail.html + admin/flows_organisation.html mit
  derselben Reiter-Nav), routers/admin_flows.py-Logik unveraendert.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-11 08:55:00 +02:00
adminandClaude Sonnet 5 e8baa61137 Fix: Navigationsspalte fälschlich zentriert statt linksbündig
Pico setzt "nav ul { align-items: center }" fuer den ueblichen
horizontalen Navbar-Fall - durch die erzwungene flex-direction:column
in .primary-nav wurde daraus horizontale Zentrierung jedes Eintrags
statt linksbuendig ueber die volle Spaltenbreite. align-items:stretch
behoben, macht nebenbei die ganze Zeile klickbar statt nur den Text.
Mobile Zeilen-Variante behaelt bewusst align-items:center (dort richtig).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-10 16:44:10 +02:00
adminandClaude Sonnet 5 5b134342dc Fix: Admin-Konten konnten eigenes Passwort nicht mehr aendern
Der GUI-Umbau vom Vortag entfernte den "Organisation"-Nav-Punkt fuer
is_superuser-Konten - "Mein Konto" lag aber ausschliesslich auf
/organisation, wodurch root sein eigenes Passwort nicht mehr erreichen
konnte (Route funktionierte weiter, war nur aus der Navigation
verschwunden - live vom Nutzer bemerkt). passwort_aendern() antwortet
jetzt rollenabhaengig (admin/accounts.html statt organisation.html fuer
is_superuser-Konten), neue "Mein Konto"-Sektion auf der Admin-Accounts-
Seite, beide Formulare posten an denselben bestehenden Endpunkt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-10 16:39:18 +02:00
adminandClaude Sonnet 5 6fe10b48db GUI-Feedback Runde 2: Bug-Fixes, Reiter-Splits, Icons, Darkmode
- Status-Spalte (Kunden-Geraeteliste) jetzt auch sortierbar.
- Admin-Flotte: Kennung jetzt aufklappbar (analog Kunden-Geraeteliste).
- Merkmale: Kategorie-Checkbox-Filter (kombiniert mit Suche, neues
  filtereNachKategorie() in table-tools.js).
- Backends/Blueprints -> "Flottenmatrix" umbenannt (Label, ueberall).
- Organisation (Kunde): "Stammdaten/Konten" in zwei eigene Seiten
  aufgeteilt (/organisation, /organisation/konten) - Kolleg:innen-
  Verwaltung jetzt eigenstaendig statt Teil der Organisationsdaten-Seite.
- Admin "Kunden" -> "Organisationen" umbenannt (Nav-Label + Seiten),
  Detail-Seite von einer langen Seite auf sechs Reiter aufgeteilt
  (Organisationsdaten/Hostnamenkonvention/Aktivierungscodes/
  Bereitstellungsvorlagen/Kundenkonten/Gruppen) - jeder Reiter holt nur
  die Daten, die er braucht (_render_detail_tab()-Helper).
- Manueller Darkmode-Umschalter (static/theme-toggle.js) - Pico folgt
  bereits automatisch der Systemeinstellung, dieser Knopf erlaubt
  zusaetzlich eine manuelle, persistierte Wahl unabhaengig vom System.
- Emoji-Icons fuer die Hauptnavigation (Flotte/Organisation/
  Installationsmedium/Organisationen/Datenbank/Flows/Admin-Accounts).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-10 15:48:07 +02:00
adminandClaude Sonnet 5 ba8162497c Backends/Blueprints als Matrix statt zwei getrennter Listen
Backends als Spalten, Merkmale als Zeilen (_blueprint_matrix() in
admin_technik.py), Haken mit Rollenname als Tooltip statt Rollenname in
eigener Spalte. Merkmale ohne jeden Blueprint bleiben als komplett leere
Zeile sichtbar (Lücken-Anzeige). Installer-Typ (kickstart/autoinstall)
nicht mehr angezeigt (Nutzer-Wunsch, Daten unveraendert). Sortierung/
Suche via table-tools.js.

Icons je Zeile bewusst nicht Teil dieses Schritts (Nutzer-Entscheidung
10.09.2026: erst Struktur, Icon-Quelle ist ein eigener, spaeterer
Beschaffungsschritt - haben aktuell keine App-Icon-Quelle im System).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-10 14:56:20 +02:00
adminandClaude Sonnet 5 f80121537c Installationsmedium: Reiter statt einer langen Seite
Medien / Kontingent & Gültigkeit / Erstellen als eigene Unterseiten
(analog zum Organisation-Muster), Kontext-Ermittlung in
_installationsmedium_kontext() geteilt statt dreifach dupliziert. Ohne
konfiguriertes Medium verweisen "Medien"/"Kontingent" auf den
"Erstellen"-Reiter statt leer zu wirken.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-10 14:55:11 +02:00
adminandClaude Sonnet 5 24e9c53cbf GUI-Umbau: Sortierung+Suche, Workspace-Spalte, aufklappbare Kennung, Favicon-Tooltip
- Neues static/table-tools.js: abhaengigkeitsfreie Klick-Sortierung
  (th[data-sort]) + Volltextsuche (input[data-table-search]) fuer
  Tabellen, in base.html global eingebunden.
- Merkmale-Tabelle (Datenbank) + Geraeteliste (Admin-Flotte, Kunden-
  Flotte) damit ausgestattet.
- Kunden-Geraeteliste: neue Workspace-Spalte (install-time zugewiesener
  Workspace - fetch_devices_for_organization() in provisioning-server
  entsprechend erweitert), Kennung jetzt per <details> aufklappbar statt
  immer komplett sichtbar.
- Webapps: Favicon-<img> mit title-Tooltip ("Bild verlinkt zum
  Betreiber") - technisch bleibt es beim serverseitigen Speichern+
  Proxy (kein Hotlink, bessere Privacy fuer den Kunden), nur der Hinweis
  ist neu.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-10 14:51:52 +02:00
adminandClaude Sonnet 5 5f9990736e GUI-Umbau: getrennte Navigation Root vs. Kunden, neue Admin-Accounts-Seite
- base.html: linke Hauptnavigation branch nach user.is_superuser - root
  sieht Kunden/Datenbank/Flotte/Flows/Admin-Accounts statt der
  Kunden-Self-Service-Bereiche (Flotte/Organisation/Installationsmedium,
  die braucht root fuer die eigene Test-Org "Liebherr" nicht extra).
- admin/layout.html: die alte Zweitnavigation (Technik/Flotte/Flows/
  Kunden) entfernt - liegt jetzt direkt in der Hauptnavigation.
- Neue Admin-Accounts-Verwaltung (routers/admin_accounts.py,
  templates/admin/accounts.html): Plattform-Admin-Konten (is_superuser)
  getrennt von Kundenkonten. organization_id bleibt aus Schema-Gruenden
  gesetzt (Pflichtfeld), aber ohne sichtbare Bedeutung - Admin-Konten
  werden nur noch herausgefiltert+separat gezeigt (Nutzer-Entscheidung),
  keine Schema-Aenderung.
- fetch_benutzer_fuer_organisation(): liefert is_superuser-Konten nicht
  mehr mit, dafuer neue fetch_admin_konten() (organisationsuebergreifend).
- Kunden-Detail (Admin): "Tuxflotte-Admin"-Spalte + is_superuser-Checkbox
  aus dem Kundenkonto-Formular entfernt (waere seither ohnehin nie mehr
  in der eigenen Liste sichtbar gewesen).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-10 14:48:52 +02:00
adminandClaude Sonnet 5 5f89d59b7f GUI-Feinschliff Batch 1: Rand, Kolleg:innen-Sichtbarkeit, Labels, Hinweise
- Container-Breite auf breiten Bildschirmen deutlich vergrößert (Pico-
  Default liess ab 1536px beidseitig viel Platz ungenutzt).
- "Kolleg:innen"-Sektion auf /organisation jetzt komplett vor
  Nicht-Org-Admins verborgen (vorher nur die Bearbeiten-Knöpfe).
- Admin-Technik-Nav: "Kategorien" -> "App-Kategorien", "Workspaces" ->
  "Workspaces-Vorlagen" (reine Label-Umbenennung, URLs/Daten unveraendert).
- Debug-Modus-Hinweistext auf der Auftragskatalog-Seite ergaenzt.
- Struktur/Gruppen: erklaerende Hinweistexte mit Beispiel ergaenzt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-10 14:45:34 +02:00
adminandClaude Sonnet 5 4725f26f4c Workspace-Creator Schritt 4: Kategorie-Ansicht statt Einzel-Dropdown
Die alte Zusammensetzungs-Ansicht war ein einziges Dropdown mit ALLEN
~50 Merkmalen unsortiert durcheinander, ein Merkmal pro Formular-Absenden -
bei der inzwischen erreichten Katalog-Größe praktisch unbenutzbar. Ersetzt
durch nach Kategorie gruppierte Checkboxen (gleiches Muster wie
gruppe_detail.html/auftragskatalog.html) mit einem einzigen
Speichern-Knopf - Differenz zur bisherigen Zusammensetzung wird berechnet
und über die bestehenden Einzel-Endpunkte nachgezogen (Diff-Muster wie
geraete.py:gruppen_aktualisieren(), kein neuer anode-Endpunkt nötig).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-10 12:43:32 +02:00
adminandClaude Sonnet 5 f9f2625763 Hostnamenkonvention: Admin-Formular je Organisation
Neue Sektion auf der Organisations-Detailseite (Präfix, Stellen,
sequenziell/zufällig), analog zum bestehenden Organisationsdaten-Formular.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-10 12:07:11 +02:00
adminandClaude Sonnet 5 dc1fa16541 Fix: admin_geraete.py fehlte im device_merkmale-Ablöse-Umbau
Live beim Deploy gefunden: admin_geraete.py importierte noch
OPTIONEN_FELDER aus routers.geraete (dort entfernt) und hatte dieselben
select/deselect-Endpunkte wie geraete.py, die im vorigen Commit dort schon
entfernt wurden - Service crashte beim Start (ImportError). Gleiches
Muster wie geraete.py jetzt auch hier angewendet: Endpunkte entfernt,
eigene-gruppe-Route als admin-seitiges Gegenstück ergänzt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-10 10:45:10 +02:00
adminandClaude Sonnet 5 743b146c15 Gerätegruppen-Workspace-UI, device_merkmale-Ablösung im Kundenportal
- gruppen.py/admin_kunden.py: Workspace-Zuweisung/-Entfernung an
  Gerätegruppen (Kundenportal + Admin-Parallelansicht), Konflikt-
  Fehlermeldung bei Exklusivitätsverletzung (workspace_fehler-Query-Param,
  URL-encodiert).
- gruppe_detail.html (Kundenportal + Admin): neue Workspace-Sektion mit
  Auswahl-Dropdown (nur global + eigene Org, siehe neuer anode-Endpunkt)
  bzw. Entfernen-Knopf.
- gruppen_liste.html: Workspace-Spalte ergänzt.
- geraete.py: per-Geräte-Auftragskatalog-Select/Deselect entfernt
  (device_merkmale ist weg) - auftragskatalog.html jetzt reine
  Ist-Zustand-Anzeige, neue Route "eigene-gruppe" legt bei Bedarf direkt
  eine Ein-Geräte-Gruppe an und leitet dorthin weiter (ersetzt den alten
  Direkt-Toggle als GUI-Einstieg).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-10 10:43:51 +02:00
adminandClaude Sonnet 5 2618e5e468 Optionen-Eingabe auf OE-/Gruppen-Ebene + Text-/Passwort-Feldtypen
Bisher konnten Merkmal-Optionen nur pro Gerät gesetzt werden (nur
Checkboxen, siehe alte OPTIONEN_FELDER in geraete.py) - für die neuen
AD/Fileserver/Update-Proxy-Merkmale (admin/ansible-content) muss das aber
auf OE-/Gruppen-Ebene passieren (Entscheidung vom 09.09.2026: Geräte-
Einzel-Konfiguration ist nicht der vorgesehene Weg).

Neues gemeinsames Modul optionen_felder.py (ersetzt die bisher lokale,
duplizierte Map in geraete.py): Feld-Definitionen mit type
(checkbox/text/password), genutzt von geraete.py, struktur.py (neu) und
gruppen.py (neu). parse_optionen_form() ersetzt die bisherige reine
Checkbox-Parsing-Logik - Text-/Passwort-Felder werden nur übernommen,
wenn tatsächlich etwas eingegeben wurde (leer = "nicht ändern", siehe
merge_optionen() in admin/provisioning-server).

Templates (auftragskatalog.html, struktur_merkmale.html neu,
gruppe_detail.html neu) rendern je nach type ein Checkbox- oder Text-/
Passwort-Eingabefeld, mit Hinweis-Platzhalter, wenn für ein Passwort-Feld
bereits ein Wert hinterlegt ist (aus der maskierten optionen-Anzeige des
Servers, nie der Klartext).

Gehört zusammen mit admin/provisioning-server (encrypt/decrypt/merge-
Logik, erweiterte OE-/Gruppen-Select-Endpunkte) und admin/ansible-content
(die drei neuen Rollen, die diese Optionen konsumieren).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-10 09:39:02 +02:00
adminandClaude Sonnet 5 3b98d682d0 Webapp-Vorlagen: Admin-Verwaltung + Kunden-Self-Service-Seite /webapps
Admin: /admin/technik/webapp-templates (Anlegen/Bearbeiten/Loeschen
globaler Vorlagen).

Kunde (NEU, erster echter Workspace-Self-Service-Zugang - bisher gab es
nur OEs/Gruppen unter /struktur): /webapps zeigt je eigenem Workspace
(abgeleitet aus den eigenen Bereitstellungsvorlagen, dedupliziert -
es gibt noch keine direkte Workspace-Verwaltung) die aktiven Webapps,
noch nicht aktivierte Vorlagen zum Aktivieren, und ein Formular fuer
eigene freie Eintraege. Besitz-Validierung analog
find_oe_in_organization() (struktur.py, ADR-0011).

Beide Bereiche nutzen fuer Icon-Vorschauen denselben Streaming-Proxy
wie der bestehende ISO-Download (kundenplattform kennt anodes
oeffentliche Adresse nicht).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-09 17:42:41 +02:00
adminandClaude Sonnet 5 81b05fe563 Webapp-Mechanismus (Phase 2): Admin-UI unter /admin/technik/workspaces/{id}
Liste+Anlegen+Entfernen von Webapps (Name+URL) je Workspace,
Favicon-Vorschau per Proxy-Route (/admin/technik/webapp-icons/...) -
gleiches Streaming-Proxy-Muster wie der bestehende
ISO-Download (installationsmedium_download()), da kundenplattform die
oeffentliche anode-Adresse nicht kennt (ANODE_URL hier ist die interne
127.0.0.1-Adresse).

Bewusst vorerst im Admin-Bereich angesiedelt (Tuxflotte-Personal),
nicht im Kunden-Self-Service (struktur.py) - es gibt dort noch keine
Workspace-Verwaltung, nur OEs/Gruppen. Offene Scope-Frage, dem Nutzer
separat vorgeschlagen statt unilateral entschieden: Workspaces generell
dem Kunden-Self-Service oeffnen, oder Webapps auf OE-/Gruppen-Ebene
umziehen (analog zu oe_merkmale) statt Workspace-Ebene, damit der Kunde
sie wirklich selbst pflegen kann ('der Kunde hat diverse URLs').

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-09 14:59:37 +02:00
adminandClaude Sonnet 5 6b67cf6f94 README: provisioning-server laeuft seit ADR-0027 auch nicht mehr als root
Kleine Korrektur einer jetzt ueberholten Aussage.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KMczXJkjyv59Ds3TsDw8H3
2026-09-07 18:12:43 +02:00
admin adad94b11a admin: Installations-/Aktions-Log für Geräte (Merkmal- + Installationsereignisse)
Neue Route /admin/geraete/{device_id}/log fasst device_merkmal_events
(Auftragskatalog, bereits bestehend) und die neuen
device_installation_events (Migration 0025 in provisioning-server,
Boot-Medium-Meilensteine/Fehlschläge) chronologisch zusammen - eigenes
Template (templates/admin/geraete_log.html), bewusst getrennt von der
Kunden-Seite (templates/geraete_log.html bleibt unverändert), da noch
offen ist, ob/wie umfangreich Installationsereignisse dem Kunden selbst
gezeigt werden sollen.
2026-09-01 16:33:04 +02:00
admin 06ba70d970 installationsmedium: Erstellungsinfo hervorheben + ISO-Größe anzeigen
Nutzer-Feedback (01.09.2026, echter Hardware-Test):
- "Wenn man ein Installationsmedium erstellt... wird relativ weit oben die
  Erstellungsinfo sichtbar. Aber etwas unscheinbar. Vielleicht kann man
  das leicht farbig hinterlegen" - neue .highlight-box-Klasse (farbiger
  linker Rand + Picos eigene Sectioning-Hintergrundfarbe, Dark/Light-Mode-
  sicher da nur Pico-CSS-Variablen) auf der "Aktuelles Installationsmedium"
  Sektion.
- "Bitte nach ISO-Build auch die Größe anzeigen lassen" - zeigt
  iso_build.size_bytes (neu von provisioning-server geliefert, siehe
  dortiger Commit fb87a23) über Jinjas eingebauten filesizeformat-Filter an.
2026-09-01 16:08:26 +02:00
adminandClaude Opus 5 1d41f896ca SHA256-Pruefsumme in der Oberflaeche anzeigen
Ergaenzt die neue iso_builds.sha256-Spalte (provisioning-server) als
copy-paste-fertige sha256sum -c-Zeile in der Anleitung, plus als reiner
Wert oben beim Download-Button.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 11:31:15 +02:00
adminandClaude Opus 5 cf59a9bb44 Fix: dd-Anleitung referenzierte den alten festen Dateinamen
Direkte Folge des vorherigen Commits (sprechender Download-Dateiname) -
die Anleitung auf /installationsmedium zeigte weiterhin
'tuxflotte-installationsmedium.iso'. Namensbildung in _iso_dateiname()
extrahiert, von installationsmedium_ansicht() (fuer die Anzeige) UND
installationsmedium_download() (fuer den echten Header) gemeinsam
genutzt, damit beide nie auseinanderlaufen koennen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 11:06:37 +02:00
adminandClaude Opus 5 fe2a893fbf Sprechender Download-Dateiname statt immer 'tuxflotte-installationsmedium.iso'
Live-Fund beim Self-Service-Flow-Test: bei mehreren Bau-Durchlaeufen/
Workspaces war der Download-Dateiname nicht unterscheidbar. Jetzt
zusammengesetzt aus Backend+Version+Workspace (aus der verwendeten
Bereitstellungsvorlage) + Bau-Datum, z.B.
'tuxflotte-Linux-Mint-22-Entwickler-2026-08-28.iso'.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 11:02:58 +02:00
adminandClaude Opus 5 117e03cd23 Fix: WLAN-SSID zeigte b'...' statt Klartext (psycopg bytes-vs-str)
Live gefunden beim echten Self-Service-Flow-Test: fetch_konfiguration()
gab wifi_ssid roh weiter, ohne den bekannten bytes-Decode-Guard, den
andere Stellen im Projekt schon haben (siehe auth.py:_row_to_user()).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 10:59:53 +02:00
adminandClaude Opus 5 5b66974024 Debug-Modus-Toggle, Health-Anzeige, letzter Checkin im Log
Neue Sektion 'Debug-Modus' auf der Auftragskatalog-Seite (Kunden- und
Admin-Bereich, gleiches zurueck_url-Praefix-Muster wie die bestehenden
select/deselect-Formulare) mit Aktivieren/Beenden-Toggle. Geräteliste
zeigt jetzt ein kleines Debug-Badge. Hardware-Seite bekommt eine neue
Sektion 'Aktueller Zustand (letzter Checkin)' mit den Health-Metriken,
nur sichtbar wenn device.health vorhanden ist. Log-Seite zeigt jetzt
'Zuletzt gemeldet' oberhalb der Ereignistabelle - vorher nur in der
Geräteliste sichtbar, nicht in der Detail-Historie.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 10:00:58 +02:00
adminandClaude Opus 5 4dec7645a6 ISO-Ablauf: Restlaufzeit auf /installationsmedium anzeigen
Ergänzt um 'Zum Download verfügbar bis: ...' beim Download-Button, sobald
provisioning-server (siehe ADR-0014-Nachtrag) ein expires_at für den
Build liefert. Kein Router-Code nötig - das Feld kommt automatisch mit
der bestehenden iso_build-Antwort durch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 09:54:04 +02:00
adminandClaude Opus 5 1fdac84220 Self-Service: Passwort ändern (POST /organisation/passwort)
Neue Sektion 'Mein Konto' auf der bestehenden /organisation-Seite. Jeder
eingeloggte Nutzer darf sein eigenes Passwort ändern (kein
require_org_admin nötig, anders als bei /organisation/konten für fremde
Konten). Prüft aktuelles Passwort per user_manager.authenticate() (gleiches
Muster wie der Login), validiert das neue Passwort explizit gegen die
bestehende Policy (>=12 Zeichen, nicht gleich der E-Mail) aus der
Sicherheitshärtung vom 25.08.2026, zeigt Fehler inline auf derselben Seite
statt als nackte HTTPException.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 09:48:33 +02:00
adminandClaude Opus 5 fc03a3c3a9 Rename-Label 'Flotten-Management' + zweistufige Navigation
Siehe ADR-0020 (platform-docs) für die vollständige Begründung.

- base.html: Nav-Marke 'Kundenplattform' -> 'Flotten-Management'.
  Navigation von flacher horizontaler Liste auf vertikale Primär-Spalte
  (Flotte/Organisation/Installationsmedium/Admin) + horizontale
  Sekundär-Nav umgestellt. Struktur und Gruppen sind jetzt Unterpunkte
  von Organisation. Aktive Kategorie wird aus dem URL-Pfad abgeleitet,
  keine Router-Änderungen nötig, URLs unverändert.
- app.py: FastAPI-Titel ebenfalls angepasst (aktuell dormant, da /docs
  deaktiviert).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 09:45:27 +02:00
adminandClaude Opus 5 cb820b9790 Security-Hardening vor öffentlicher Freigabe: Rate-Limiting, Passwort-Policy, Docs-Endpunkte, Security-Header
Sicherheits-Review 25.08.2026 vor geplanter öffentlicher Erreichbarkeit
über Pangolin (bisher nur Pangolin-interne Auth vorgeschaltet):

- auth.py: In-Memory-Rate-Limiting auf /login (je Email UND je Quell-IP,
  8 Fehlversuche / 10 Minuten), UserManager.validate_password() setzt
  jetzt eine Mindestlänge von 12 Zeichen durch (fastapi_users' Default
  war ein No-Op, ließ auch leere Passwörter zu).
- routers/admin_kunden.py, routers/organisation.py: fangen
  InvalidPasswordException/UserAlreadyExists jetzt sauber ab statt 500.
- app.py: /docs, /redoc, /openapi.json deaktiviert (legten zuvor die
  komplette Routenstruktur inkl. /admin/* offen), Security-Header-
  Middleware (X-Content-Type-Options, X-Frame-Options, Referrer-Policy,
  CSP).
- scripts/reset_password.py: neues Skript, Gegenstück zu create_user.py,
  für die im selben Review gefundenen aktiven Testaccounts mit dem
  Passwort 'test123' (einer davon Superuser) gebraucht.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 12:14:34 +02:00
adminandClaude Sonnet 5 9265d47916 fix: Kontolöschung schlug mit 500 fehl (bytes-vs-str, psycopg-Eigenart)
Live bei der Verifikation gefunden: konto_loeschen() lud das Zielkonto
über get_by_email(ziel['email']) nach - der email-Wert kam aus
fetch_benutzer_fuer_organisation()'s sync-Verbindungspfad, wo psycopg
TEXT-Spalten teils als bytes statt str liefert (dasselbe bereits bekannte,
nicht deterministische Verhalten wie in _row_to_user(), siehe dortiger
Kommentar) - die async get_by_email()-Abfrage bekam dadurch einen
bytes-Parameter und scheiterte mit 'operator does not exist: text =
bytea'.

Zwei Fixes: konto_loeschen() nutzt jetzt user_manager.get(id) statt des
E-Mail-Umwegs (nur async-Pfad, umgeht das Problem strukturell).
fetch_benutzer_fuer_organisation() bekommt zusätzlich denselben
defensiven bytes-Decode wie _row_to_user(), damit das nicht an anderer
Stelle erneut zuschlägt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 07:45:11 +02:00
adminandClaude Sonnet 5 ec11de1ee9 feat: Self-Service-Kontenverwaltung + Organisationsdaten unter /organisation
Neue Rolle ist_organisationsadmin (auth.py), bewusst getrennt von
is_superuser: is_superuser bleibt exklusiv internes, organisationsuebergreifendes
Tuxflotte-Personal mit Zugriff auf /admin/* - ist_organisationsadmin ist
auf die eigene Organisation beschränkt und kann nie über Self-Service
gesetzt werden (organization_id/is_superuser kommen bei
POST /organisation/konten nie aus dem Formular, sondern immer aus der
Session).

/organisation zeigt jetzt Kontakt-/Stammdaten (bearbeitbar) und die
Kolleg:innen-Liste der eigenen Organisation (für alle sichtbar,
Verwaltung nur für Org-Admins/internes Personal via require_org_admin).
Selbstlöschung des eigenen Kontos wird verweigert.

fetch_benutzer_fuer_organisation() von admin_kunden.py nach auth.py
verschoben (jetzt von beiden Routern importiert, kein Duplikat) -
liefert zusätzlich id + ist_organisationsadmin. admin_kunden.py bekommt
eine neue Organisationsdaten-Sektion (nutzt denselben PATCH-Endpoint)
und die neue Checkbox beim Konto-Anlegen, um den ersten Org-Admin einer
neuen Organisation zu bootstrappen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 07:40:05 +02:00
adminandClaude Sonnet 5 36d0346172 feat: Logo im Header + 'Geräte' in der Navigation zu 'Flotte' umbenannt
pics/tuxflotte-beschriftet-schatten.svg (das laut platform-docs bereits
als 'das Logo' referenzierte Markenbild, siehe
13-live-provisioning-boot.md) als static/tuxflotte-logo.svg eingebunden,
im Header neben dem Kundenplattform-Link (wirkt dadurch auch auf der
Login-Seite, die base.html erbt). Passend dazu buchstabiert das Logo
selbst bereits 'Tux'/'Flotte' als Wortmarke.

Nav-Link '/geraete' zeigt jetzt 'Flotte' statt 'Geräte' - sowohl im
Kundenportal-Header (base.html) als auch im Admin-Sidebar
(admin/layout.html), für konsistente Beschriftung über die ganze
Weboberfläche. Reine Anzeigetext-Änderung, URLs/Routen unverändert.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-19 20:36:57 +02:00
adminandClaude Sonnet 5 24ac11137d feat: Gruppen-Mitgliedschaft direkt aus der Kundenportal-Geräteliste verwalten
Gegenstück zur bestehenden Verwaltung von der Gruppen-Seite aus
(/gruppen/{id}) - jetzt auch umgekehrt vom Gerät aus möglich, ohne
anode-Änderung (fetch_gruppen() aus routers/gruppen.py wiederverwendet,
Diff-Berechnung gegen bestehende add/remove-Endpunkte).

geraete_liste(): neue Spalte 'Gruppen' (aktuelle Mitgliedschaft),
Aktionen-Dropdown bekommt eine Checkbox-Liste aller Organisations-
Gruppen; POST /geraete/{id}/gruppen berechnet die Differenz zur
gewünschten Auswahl und ruft die bestehenden Endpunkte entsprechend auf.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-19 20:21:06 +02:00
adminandClaude Sonnet 5 ca25ce030e feat: Konfigurationsgruppen im Admin-Auftragskatalog-UI
- Bugfix: auftragskatalog.html hatte Select/Deselect-Formular-Ziele fest
  auf /geraete/... verdrahtet statt zurueck_url zu nutzen - Admin-Ansicht
  eines fremden-Org-Geraets scheiterte dadurch mit 404. Jetzt dynamisch.
- Herkunfts-Anzeige ('via OE'/'via Gruppe') ist jetzt anklickbar, sofern
  ein Sprungziel existiert (neue anreichere_mit_quelle_url()-Hilfsfunktion
  in routers/geraete.py, von geraete.py und admin_geraete.py genutzt).
- Admin-Geräteliste (/admin/geraete) bekommt OE-Spalte + 'In OE
  verschieben', analog zur Kundenportal-Liste.
- Neue, bewusst schlanke Admin-OE-Ansicht
  (/admin/kunden/{org}/organisationseinheiten/{oe}/merkmale) - nur der
  Merkmale-Editor als Sprungziel, kein OE-CRUD (bleibt laut ADR-0012
  Kunden-Self-Service unter /struktur).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 21:28:10 +02:00
adminandClaude Sonnet 5 ea92b3c54c feat: Gruppen-Selfservice im Kundenportal
Kund:innen können jetzt eigene Gruppen selbst verwalten (/gruppen) statt
nur intern über /admin/kunden - reiner Kundenplattform-Zusatz, keine
Backend-Änderung nötig (anode-API war bereits organisationsgebunden und
generisch aufgebaut, siehe ADR-0012). Admin-Zugang bleibt zusätzlich
bestehen (Support/Onboarding), beide teilen sich dieselbe anode-API.

Neuer Router routers/gruppen.py (eng an struktur.py angelehnt, gleiches
Besitz-Validierungsmuster), Templates gruppen_liste/gruppe_detail
(Merkmale-Tristate-Editor + Geräte-Mitgliedschaft, wie
admin/gruppe_detail.html)/gruppe_umbenennen/gruppe_loeschen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 20:57:48 +02:00
adminandClaude Sonnet 5 232d621245 feat: Struktur (OE-Selfservice) + Gruppen-Verwaltung (Admin)
Kundenportal:
- Neue Seite /struktur - OE-Baum (rekursives Jinja-Makro), Anlegen/
  Umbenennen/Löschen (Standard-OE 'Neue Geräte' geschützt), Merkmale-
  Editor pro OE mit Tristate aktiv/inaktiv/nicht festgelegt.
- geraete_liste.html: neue Spalte 'OE', Aktionen-Dropdown um 'In OE
  verschieben' erweitert.
- auftragskatalog.html: zeigt jetzt die Herkunft einer Einstellung an
  (OE/Gruppe/manuell), sofern sie nicht vom Workspace-Default kommt.

Admin (/admin/kunden/{id}):
- Neue Sektion 'Gruppen' - Anlegen/Löschen, verlinkt auf neue
  Detailseite (admin/gruppe_detail.html) mit Merkmale-Editor (wie OE)
  und Geräte-Mitgliedschaft-Checkliste. Bewusst nur intern verwaltbar
  in v1 (siehe Konfigurationsgruppen-Diskussion).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 16:41:04 +02:00
adminandClaude Sonnet 5 af6e93067f fix: E-Mail wird als bytes statt str angezeigt (b'...' in der Navbar)
_row_to_user() dekodierte bislang nur hashed_password von bytes zu str,
obwohl psycopg dieselbe Eigenart (TEXT-Spalte kommt teils als bytes
zurück) auch bei email zeigt. Sichtbar z.B. im Header als
b'name@domain.tld' statt name@domain.tld.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 13:53:33 +02:00
adminandClaude Sonnet 5 dd12dd7b74 feat: Aktionen-Spalte in der Geräteliste (/geraete)
Hardware-Informationen, Installations-/Aktions-Log, De-/Reprovisionieren,
Gerät löschen (Soft Delete, mit Bestätigungsseite) - via Pico-Dropdown,
kein zusätzliches JS. Spricht die neuen anode-Endpunkte aus
provisioning-server an.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 13:04:58 +02:00
adminandClaude Sonnet 5 d0cf308c0a hardening: systemd-Sandboxing fuer kundenplattform.service erweitern
Audit ergab: der Dienst laeuft korrekt als unprivilegierter User (nicht
root, kein sudo, nologin-Shell, bestaetigt per ps/id auf anode), aber
systemd-analyze security kam trotz der vier bestehenden Basis-Direktiven
(NoNewPrivileges/PrivateTmp/ProtectSystem=strict/ProtectHome) nur auf
8.3/10 "EXPOSED" - der volle Capability-Bounding-Set, kein
Syscall-Filter, keine Namespace-/Kernel-/Proc-Beschraenkungen.

Ergaenzt: CapabilityBoundingSet= (leer - der Dienst braucht keine
einzige Linux-Capability), SystemCallFilter=@system-service,
RestrictAddressFamilies (nur INET/INET6/UNIX), RestrictNamespaces,
RestrictSUIDSGID, LockPersonality, MemoryDenyWriteExecute,
ProtectKernel{Modules,Tunables,Logs}, ProtectControlGroups,
ProtectClock, ProtectHostname, ProtectProc=invisible, ProcSubset=pid,
PrivateDevices, RemoveIPC, RestrictRealtime, UMask=0077.

Vor dem Scharfschalten per systemd-run mit exakt dieser
Direktivenkombination gegen den echten Code getestet (nicht nur gegen
eine leere Testanwendung): Login/JWT-Signierung, Postgres-Lese-/
Schreibzugriff via psycopg, ausgehender HTTP-Aufruf via httpx an
provisioning-server, Fernet-Verschluesselung des WLAN-PSK - alles ohne
Fehler unter der vollen Sandbox.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-07 08:00:40 +02:00
adminandClaude Sonnet 5 43c75df1ed feat: Bereitstellungsvorlagen im Kunden-Admin-Bereich anlegen
Neue Sektion auf der /admin/kunden/{id}-Detailseite: Liste bestehender
Bereitstellungsvorlagen der Organisation + Formular zum Anlegen einer
neuen (Workspace, Backend, Root-Dateisystem, Default/Diskverschluesselung/
Secure-Boot), ruft den neuen provisioning-server-Endpoint auf. Schliesst
die in Phase 5 dokumentierte Luecke: bisher hatte eine frische
Kundenorganisation keine Moeglichkeit, ueberhaupt eine Vorlage zu
bekommen, und damit eine leere Auswahl auf der
Installationsmedium-Selfservice-Seite.

Lokal end-to-end verifiziert (siehe provisioning-server-Commit).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-07 01:59:34 +02:00
adminandClaude Sonnet 5 19b39af0ce feat: Selfservice-UI fuer Organisation + Installationsmedium (Phase 5)
Zwei neue Menuepunkte fuer eingeloggte Kunden (nicht der
organisationsuebergreifende /admin/-Bereich):

- "Organisation": Name bearbeiten (ruft Phase-3-PATCH auf
  provisioning-server auf).
- "Installationsmedium": WLAN-SSID/-Passwort, Vorlagenwahl, Kontingent
  und Gueltigkeitsdauer speichern, personalisierte Kunden-ISO per
  Klick erstellen (Phase-4-Endpoints), Fortschritt/Status-Anzeige
  (Meta-Refresh waehrend pending/running), Download + dd/Rufus-
  Anleitung. Getrennte "aufladen"-Aktion erhoeht Kontingent/
  Gueltigkeit der bestehenden Enrollment Session (neuer PATCH-
  Endpoint), ohne eine neue ISO zu erzeugen - die bereits ausgeteilte
  bleibt gueltig.

Neue Tabelle installationsmedium_konfiguration (Migration 0004, eigene
DB, kein Foreign Key auf provisioning-server-Ressourcen, siehe
ADR-0011) haelt SSID/verschluesselten PSK/Vorlage/Kontingent-Defaults
sowie die IDs der aktuell gueltigen Enrollment Session und des
zugehoerigen ISO-Baus.

WLAN-PSK wird Fernet-verschluesselt gespeichert (encryption.py, neuer
KUNDENPLATTFORM_ENCRYPTION_KEY) - echte Zugangsdaten, kein
Passwort-Hash-Fall. Download laeuft ueber einen Streaming-Proxy
(anode_stream() in anode_client.py) statt eines direkten Links, weil
der Browser kein KUNDENPLATTFORM_TOKEN besitzt.

Lokal end-to-end verifiziert (Wegwerf-Postgres fuer beide Services,
echter build_customer_iso.sh-Lauf ueber die Kundenplattform-UI):
Formular -> Enrollment Session + ISO-Bau -> Status-Polling ->
Download-Proxy liefert byte-identische, korrekt personalisierte Datei
(SSID/PSK/Aktivierungscode per xorriso-Extraktion gegengeprueft) ->
Aufladen erhoeht Kontingent/Gueltigkeit der bestehenden Session ohne
neuen Bau -> "neues Installationsmedium erstellen" erzeugt neue
Session + ISO, alte Session bleibt unangetastet nutzbar (alter
ISO-Bau wird laut Phase-4-Cleanup automatisch entfernt), leeres
PSK-Feld uebernimmt das zuvor gespeicherte PSK korrekt.
Organisation-Name-Aenderung ueber PATCH verifiziert. Unauthentifizierter
Zugriff auf beide neuen Routen leitet korrekt zu /login um.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-07 01:36:46 +02:00
adminandClaude Sonnet 5 576613ed0f feat: Auth auf fastapi-users umstellen
Ersetzt die handgestrickte bcrypt+Session-Auth durch fastapi-users
(CookieTransport+JWTStrategy statt Starlette-Session, custom
psycopg3-async-Adapter statt SQLAlchemy). Bestehende bcrypt-Hashes
bleiben gültig und werden beim nächsten Login automatisch auf Argon2
angehoben (pwdlib-Default-PasswordHelper). ist_admin wird zu
is_superuser, damit fastapi-users' eingebaute Semantik direkt nutzbar
ist - require_admin/get_current_user bleiben als Wrapper unter
gleichem Namen, alle Router-Dateien bis auf geraete.py (dict- zu
attribut-Zugriff auf user.organization_id) unverändert.

Lokal end-to-end gegen eine Wegwerf-Postgres-Instanz (podman) verifiziert:
Migration, Bestandskonto-Login mit altem Passwort samt Argon2-Upgrade,
Admin-Kontoanlage über beide Aufrufstellen (create_user.py und
admin_kunden.py), 403 für Nicht-Admins, Logout.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-06 08:29:51 +02:00
adminandClaude Sonnet 5 b4bd160536 feat: Flows-Verwaltung im Admin-Bereich (Enrollment Sessions, Neugerät-Bestätigung)
routers/admin_flows.py: pro Organisation unbestätigte Geräte anzeigen
und bestätigen (Bereitstellungsvorlage zuweisen), Enrollment Sessions
anlegen/widerrufen. Confirm-Aktion ruft direkt anodes bestehenden
resolve-Endpoint auf.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-05 09:32:57 +02:00
adminandClaude Sonnet 5 eb9fc6b8a7 feat: Technik/Datenbank-Verwaltung im Admin-Bereich
routers/admin_technik.py: Merkmale (anlegen/bearbeiten inkl. Kategorie
und Katalogfähigkeit), Kategorien (anlegen), Workspaces (anlegen +
Zusammensetzung aus Merkmalen - Grundbaustein des Workspace-Creators),
Backends/Blueprints nur zur Anzeige.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-05 09:21:11 +02:00
adminandClaude Sonnet 5 33c4aebfec feat: organisationsübergreifende Geräteverwaltung im Admin-Bereich
routers/admin_geraete.py mit echter Funktionalität: alle Geräte aller
Organisationen auflisten, pro Gerät der Auftragskatalog (Wiederverwendung
von auftragskatalog.html und der Logik aus routers/geraete.py, nur ohne
Org-Besitz-Check - Admins dürfen jedes Gerät direkt per ID öffnen).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-05 09:12:45 +02:00
adminandClaude Sonnet 5 b44096b48f feat: Kunden-Management (Organisationen, Aktivierungscodes, Konten)
routers/admin_kunden.py mit echter Funktionalität: Organisationen
auflisten/anlegen, pro Organisation Aktivierungscodes erzeugen und
Kundenkonten anlegen (direkt in der Kundenplattform-eigenen
benutzer-Tabelle, kein anode-Aufruf nötig). Löst das bisherige manuelle
create_user.py-Skript für den Alltagsgebrauch ab.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-05 09:03:34 +02:00
adminandClaude Sonnet 5 ed38f103f2 feat: modulare Struktur für Admin-Bereich (Schritt 1)
app.py auf APIRouter-Module umgestellt (db.py, templating.py,
anode_client.py, auth.py, routers/geraete.py + vier Admin-Router als
Platzhalter). benutzer.ist_admin (Migration 0002) + require_admin-
Dependency (403 ohne Admin-Flag). Admin-Nav-Link im Header nur für
Admin-Konten sichtbar. create_user.py um --admin-Flag erweitert.

Die vier Admin-Bereiche (Technik/Datenbank, Geräte, Flows, Kunden) sind
als Platzhalterseiten unter /admin/... verdrahtet und bekommen in den
nächsten Schritten echte Funktionalität.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-05 08:51:46 +02:00
adminandClaude Sonnet 5 c35145ef8e feat: Pico.css für Dark/Light-Mode und Responsivität
Klassenloses CSS-Framework (self-hosted unter static/, kein CDN), passt
zum bestehenden Ansatz ohne JS-Build-Schritt. Bringt automatisches
Dark/Light-Mode (prefers-color-scheme) und Responsivität ohne
Zusatzaufwand. Templates auf Picos semantische Konventionen umgestellt
(label/input-Paare, article für die Login-Karte, figure um Tabellen für
horizontales Scrollen). Eigene CSS-Regeln auf die zwei Farb-Utility-
Klassen (state-present/state-absent) reduziert, Rest kommt von Pico.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-05 08:16:43 +02:00
adminandClaude Sonnet 5 21c6210869 fix: password_hash aus DB als bytes statt str behandeln
psycopg liefert die TEXT-Spalte password_hash beim SELECT als bytes
zurück, nicht als str wie beim create_user.py-Insert - bcrypt.checkpw()
schlug dadurch bei jedem Login-Versuch mit AttributeError fehl. Gefunden
beim ersten E2E-Login-Test auf anode.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 17:06:18 +02:00
adminandClaude Sonnet 5 c4825acd61 feat: initiale Kundenplattform (Login + Auftragskatalog-Auswahl)
Neuer, von provisioning-server (anode) getrennter Dienst, siehe ADR-0011:
FastAPI + Jinja2, eigene Postgres-DB (benutzer-Tabelle), eigenes
Service-Token (TUXFLOTTE_KUNDENPLATTFORM_TOKEN) für die Kommunikation mit
anode. Org-Zugehörigkeits-Prüfung passiert hier in der Anwendung vor jedem
Aufruf gegen anode.

Umfang v1: Login, Geräteliste, Auftragskatalog geräteweise aus-/abwählen
(inkl. der standardbrowser-Option für Brave). systemd-Unit läuft unter
eigenem unprivilegiertem System-User statt root.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 17:00:03 +02:00