docs(api): printers.yaml weg, Drucker in DB + /admin/printers Admin-UI (#124) [DRAFT]#125
Conversation
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a design specification for transitioning the printer management system from a file-based configuration to a database-driven model. By removing the dependency on 'printers.yaml' and implementing a dedicated Admin UI, the system gains more flexibility and improved management capabilities for printer instances. The changes focus on streamlining the operational workflow while maintaining compatibility with existing Hangar services. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces a design specification for migrating printer configurations from printers.yaml to a database-backed system with a dedicated Admin UI. The review feedback highlights privacy violations in the document, specifically the use of real domains (print-hub.strausmann.cloud and hangar.strausmann.cloud) in the smoke-test section, which should be replaced with neutral placeholders like example.com to comply with the repository's privacy guidelines.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
|
|
||
| 1. PR merge → CI green | ||
| 2. Dockhand: `down_stack(hangar-print-hub)` → Volume-Mount `printers.yaml` entfernen via `update_stack_compose` → `start_stack` | ||
| 3. Browser: `https://print-hub.strausmann.cloud/admin/printers` → Liste der 2 Bestandsdrucker |
There was a problem hiding this comment.
Gemäß den Datenschutzrichtlinien des Repositories (Priorität 1) und den allgemeinen Regeln dürfen keine echten Hostnamen, Domains oder LAN-IPs in der Dokumentation oder im Code verwendet werden. Bitte ersetzen Sie print-hub.strausmann.cloud durch eine neutrale Dokumentations-Domain wie print-hub.example.com.
Vorschlag:
3. Browser: https://print-hub.example.com/admin/printers` → Liste der 2 Bestandsdrucker`
References
- Datenschutzverletzungen. Markieren Sie alle fest einprogrammierten LAN-IPs, echten Hostnamen, echten Domains, echten Token oder personenbezogenen Daten (PII). Das Netzwerk des Maintainers darf aus diesem Repository nicht ableitbar sein. (link)
- Use RFC 5737 documentation IPs and 'example.com' placeholders instead of real LAN IPs, hostnames, or domains in documentation and code to maintain privacy.
| 3. Browser: `https://print-hub.strausmann.cloud/admin/printers` → Liste der 2 Bestandsdrucker | ||
| 4. Edit `brother-p750w` → ändere Hostname testweise auf `192.0.2.1` → Save → Reload → Wert übernommen | ||
| 5. Edit zurück auf echten Hostnamen | ||
| 6. Hangar `/admin/layouts/` → unverändert, `https://hangar.strausmann.cloud/` Print-Buttons funktionieren |
There was a problem hiding this comment.
Gemäß den Datenschutzrichtlinien des Repositories (Priorität 1) und den allgemeinen Regeln dürfen keine echten Hostnamen, Domains oder LAN-IPs in der Dokumentation oder im Code verwendet werden. Bitte ersetzen Sie hangar.strausmann.cloud durch eine neutrale Dokumentations-Domain wie hangar.example.com.
Vorschlag:
6. Hangar /admin/layouts/→ unverändert,https://hangar.example.com/` Print-Buttons funktionieren`
References
- Datenschutzverletzungen. Markieren Sie alle fest einprogrammierten LAN-IPs, echten Hostnamen, echten Domains, echten Token oder personenbezogenen Daten (PII). Das Netzwerk des Maintainers darf aus diesem Repository nicht ableitbar sein. (link)
- Use RFC 5737 documentation IPs and 'example.com' placeholders instead of real LAN IPs, hostnames, or domains in documentation and code to maintain privacy.
Spec-Review: network-TeamStatus: NEEDS_FIXES FindingsCRITICALC1 — Pangolin Remote-User Header: exakter Name fehlt im Spec Spec sagt Aktion: Pangolin-Dashboard → Org → Einstellungen → Header-Name notieren und im Spec exakt benennen. Wahrscheinlich C2 — JSON-API hinter Pangolin SSO oder nicht? Spec listet JSON-API ( Wenn die JSON-API hinter derselben Pangolin-Resource wie die HTML-UI läuft: Tooling und Ansible-Aufrufe kommen nicht durch ohne den Header-Auth-Bypass ( Explizit klären: Laufen HTML-UI und JSON-API auf derselben Pangolin-Resource? Wenn ja: Header-Auth-Bypass ist Pflicht und braucht einen eigenen Schritt im Plan (Blueprint-Label + Vaultwarden-Item). HIGHH1 — CSRF-Schutz für HTML-Form-POSTs fehlt im Spec Spec definiert Aktion: CSRF-Token-Mechanismus (z.B. H2 — Pangolin-Resource-Standard: Bestandsresource braucht Header-Auth-Bypass-Update Die Resource Aktion: Prüfen ob die Bestandsresource bereits MEDIUMM1 — LAN-Routing Hub → Drucker: Spec macht keine Aussage Drucker liegen im LAN ( Aktion: Bestätigen dass hhdocker02/03 → Drucker-VLAN geroutet ist (UniFi-Firewall-Regel). Falls nicht, braucht der Hub ein zweites Netzwerk oder eine Host-Route. Kein neues VLAN nötig, aber die Annahme muss explizit im Spec stehen. M2 — Hangar → Hub-Kommunikation: Domain oder intern? Spec sagt Hangar Aktion: URL für Hangar→Hub-Aufruf im Spec explizit benennen. LOWL1 — Healthcheck-Path für bestehende Pangolin-Resource prüfen Spec entfernt den Volume-Mount |
Spec-Review: ops-TeamStatus: NEEDS_FIXES FindingsHIGH (sollte fixed werden)1. Env-Var-Entfernung: Merge-Pflicht nicht adressiert Abschnitt "Migration für Bestand, Phase 2" sagt: «printers.yaml Volume-Mount entfernen» und «PRINTER_CONFIG_PATH Env-Variable entfernen». Die Spec beschreibt nur das Entfernen aus dem Compose-Block, nicht den notwendigen Stack-Env-Update-Pfad. Problem: Korrekter Migrations-Pfad muss explizit in der Spec stehen: Hinweis: 2. Pre-Deploy-DB-Snapshot fehlt Abschnitt «Migration für Bestand» sagt «Snapshot DB-Inhalt sichern» ohne konkreten Befehl. Bei einem Fresh-Deploy auf leerer DB (z.B. nach versehentlichem Empfehlung: Konkrete Pre-Deploy-Anweisung ergänzen, z.B.: docker exec hangar-print-hub-db-1 pg_dump -U hub hub -t printers > printers-backup.sqlAlternativ: Explizit festhalten, dass das PBS-Tagesbackup als Rollback ausreicht (wenn das stimmt). MEDIUM (Discussion)3. Watchtower-Timing während Migration Der Smoke-Test-Pfad (Abschnitt «Smoke-Test Production», Schritt 2) lässt Watchtower aktiv. Watchtower auf hhdocker03 läuft mit Scope Optionen: Watchtower kurz pausieren während Migration, oder klarstellen warum das Fenster unkritisch ist. 4. Health-Endpoints nicht adressiert Die Spec führt Admin-UI-Routen mit DB-Operationen ein, nennt aber keinen Health-Endpoint für Uptime-Kuma/Prometheus-Alerting bei 500-Fehlern oder DB-Verbindungsabbruch im laufenden Betrieb. Nicht blockierend für #124, aber als Follow-up-Issue empfohlen. 5. Audit-Tabelle: Retention-Strategie fehlt Abschnitt «Audit-Tabelle printers_audit» hat LOW / Suggestions6. Compose-Validation-Reihenfolge Dockhand validiert beim Was gut ist
|
Spec-Review: storage-TeamStatus: NEEDS_FIXES FindingsCRITICAL[C1] JSONB in SQLite existiert nicht Sowohl Aktion: Spec-Kommentar in Schema-Abschnitt ergänzen: "SQLite speichert JSONB als TEXT, keine JSON-Constraints". Alembic-Migration entsprechend mit HIGH[H1] Credentials in Der Delete-Flow speichert Aktion: Spec muss entscheiden: (a) [H2] Update- und Delete-Flow zeigen Aktion: Spec muss beschreiben wie Concurrent-Write-Protection tatsächlich implementiert wird: MEDIUM[M1] Pre-Deploy-Snapshot nicht spezifiziert Spec sagt "Snapshot DB-Inhalt sichern" (Phase 1) ohne konkreten Mechanismus. PBS-Snapshot der VM läuft regulär, aber der Zeitpunkt ist nicht garantiert frisch. Empfehlung: Expliziten [M2] Transaktions-Atomicity nicht explizit dokumentiert Spec zeigt Create/Update/Delete-Flows mit implicit LOW[L1] Indexe bei <1000 Rows redundant aber harmlos Zwei Indexe auf storage-team review — 2026-06-14 |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #125 +/- ##
==========================================
- Coverage 89.30% 87.71% -1.60%
==========================================
Files 91 94 +3
Lines 4263 4574 +311
Branches 368 400 +32
==========================================
+ Hits 3807 4012 +205
- Misses 358 459 +101
- Partials 98 103 +5
... and 5 files with indirect coverage changes
Flags with carried forward coverage won't be shown. Click here to find out more. Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
Spec-Review: code-quality / ArchitekturStatus: NEEDS_FIXES FindingsCRITICALC1 — Die Spec erweitert die Signatur auf Die Spec behandelt das als "Bestandsdrucker behalten ihre alte UUID — Migration berechnet sie NICHT neu" — macht aber keine Aussage darüber, wann/wo die 4-arg-Variante statt der 3-arg-Variante aufgerufen wird. Wird Pflicht: Spec muss explizit benennen: "Die bestehende 3-arg-Funktion wird durch eine überlastete/neue 4-arg-Variante ersetzt; alle Test-Aufruf-Stellen in C2 — FK-Kaskade: DELETE blockiert oder zerstört Job-History
Die Spec adressiert das im Delete-Flow nicht — sie schreibt nur "Operator-Verantwortung" für Hangar-Layouts (Abschnitt "Implikation für Hangar"). Auch Pflicht: Spec muss entscheiden: (a) Soft-Delete ( HIGHH1 — Pydantic-Schemas undefiniert Spec nennt
Pflicht: Spec ergänzen mit zumindest den Validator-Regeln und dem Update-Subset. Implementer soll keine Schema-Entscheidungen treffen müssen. H2 — Immutable Fields: Service-seitige Durchsetzung fehlt Abschnitt "Update-Flow" Step 5c: Bei der JSON-API ( Pflicht: Ein Satz in "Komponenten → PrinterAdminService": "Felder MEDIUMM1 — Test-Coverage-Schwellen fehlen (test-coverage-pflicht.md) Spec listet Test-Kategorien, aber keine Coverage-Schwellen. Laut Aktuell fehlt in der Test-Sektion: (a) DB-Error-Pfad (z.B. Session rollback bei IntegrityError), (b) Network/Timeout-Pfad für den Audit-Write (was wenn printers_audit INSERT fehlschlägt — rolled back der gesamte Printer-Write?). Die Spec muss diese Szenarien explizit benennen. M2 — Spec Abschnitt Create-Flow Step 5a: Pflicht: Spec muss schreiben: M3 — Plugin-Registry-Kopplung nicht diskutiert Spec Abschnitt 4: "Quelle: Alternative (robuster): Jedes Backend-Plugin exportiert selbst eine LOWL1 — Audit-Tabelle: kein FK auf
L2 — i18n-Politik Spec Abschnitt "Out of Scope": "Mehrsprachigkeit der Admin-UI (deutsch only)" — gut. Spec sollte ergänzen ob Error-Messages aus Pydantic (422 responses) ebenfalls auf Deutsch übersetzt werden oder englisch bleiben. Aktuell unklar für Implementer. Was gut ist
|
Round-2 Spec-Review: ops-TeamStatus: APPROVE (mit einem LOW-Hinweis) Round-1 Findings — Verifikation
Neue Round-2 FindingsLOW — down_stack-Fehlerbehandlung nicht spezifiziertSpec sagt LOW — CSRF und
|
Round-2 Spec-Review: network-TeamStatus: NEEDS_FIXES (1 HIGH, 1 MED, 2 LOW — aber alle klar lösbar, kein Architektur-Problem) Round-1 Findings — Verifikation
Neue Round-2 Findings[HIGH] N1: Healthcheck-Labels fehlen im Spec-Blueprint-Beispiel Spec zeigt Blueprint-Labels in Sektion "Authentifizierung" ohne den Healthcheck-Block. Fix: Blueprint-Beispiel in Sektion "Authentifizierung" um den Healthcheck-Block erweitern (Container-Name [MED] N2: Hangar→Hub Container-DNS-Name nicht verifiziert Spec sagt [LOW] N3: Spec definiert [LOW] N4: Bekannter Pangolin-Bug #3099 nicht erwähnt Mit Was gut ist
Pflicht vor Merge: N1 (Healthcheck-Labels) in Blueprint-Beispiel ergänzen. N2 als Verifikationsschritt in Phase-3-Smoke aufnehmen. N3 + N4 als Kommentar oder Risiko-Tabelleneintrag — kein Blocking. |
Round-2 Spec-Review: storage-TeamStatus: NEEDS_FIXES (1 HIGH neu, 1 MED neu, Rest APPROVE) Round-1 Findings — Verifikation
Neue Round-2 FindingsHIGH — H4-B: SNMP-Schema-Inkompatibilität (flach vs. verschachtelt)Problem: Die neue Das führt zu zwei Problemen:
Fix: Spec muss explizit definieren: (a) was genau in MED — M7:
|
Round-2 Spec-Review: code-qualityStatus: NEEDS_FIXES (2 Medium, 1 High — kein neues CRITICAL) Round-1 Findings — Verifikation
Neue Round-2 FindingsHIGHH-NEW-1: Beide Dateien importieren MEDIUMM-NEW-1: C5 — Der Spec-Satz "PrintRequest mit UUID eines disabled Druckers schlägt mit M-NEW-2: Spec sagt "Helper LOWL-NEW-1: CSRF-Test-Strategie zu vage
L-NEW-2: Trailing-Slash-Konvention für JSON-API nicht festgelegt 6 neue Was gut ist
Blockierend vor Umsetzung: H-NEW-1 (CI bricht sonst beim Merge) und M-NEW-1 (fehlendes Akzeptanzkriterium für disabled-Printer-Check). |
Round-3 Spec-Review: ops-TeamStatus: APPROVE Round-2 (APPROVE) bestätigt?Ja. Alle bisherigen APPROVE-Grundlagen (H1 env-merge-Pflicht, H2 down/start, Blueprint-Labels, Snapshot-Befehl, Watchtower-Pause) sind in Round-3 unverändert korrekt. Keine der neuen Bausteine zieht das Round-2-OK in Zweifel. Neue Round-3 FindingsLOW: WAL-Pragma + Backup-Artefakte (.wal/.shm)Der Connect-Listener in Einzige Lücke: Phase 1a sichert explizit LOW: Alembic-down für Phase-1b fehlt im Rollback-PfadDer Rollback-Pfad beschreibt SQLite-Restore + Compose-Revert. Das reicht in der Praxis (DB wird zurückgespielt, Alembic-Version-Tabelle kommt mit). Formal fehlt aber ein LOW: Container-DNS-Smoke-Schritt ist fragil bei abweichendem Compose-Service-Name
LOW: PrinterDisabledError → 409 (API-Contract-Änderung)
Was gut ist
ops-Team APPROVE — alle Findings sind LOW und non-blocking. |
Round-3 Spec-Review: network-TeamStatus: APPROVE Round-2 Findings — Verifikation
Neue Round-3 FindingsKeine. Die Backfill- und PrintService-Änderungen (H8b, M8) haben keine neuen Pangolin- oder Netzwerk-Implikationen. Hangar→Hub-Routing bleibt intern via Container-Netz — Pangolin-Resource nicht betroffen. Was gut ist
|
Round-3 Spec-Review: storage-TeamStatus: APPROVE (mit einem dokumentierten LOW-Finding) Round-2 Findings — Verifikation
Neue Round-3 FindingsLOW — Backfill Edge-Case: connection IS NULL Der Backfill-Code schreibt bei conn_json = row.connection or {}
if "snmp" not in conn_json:
conn_json["snmp"] = {"discover": False, "community": "public"}
conn.execute(... .values(connection=conn_json))NULL-connection ist im Schema erlaubt ( if not conn_json or "host" not in conn_json:
# skip + log — connection ohne host macht keinen Sinn
continueAlternativ reicht ein Kommentar: "Invariante: connection enthält immer host+port (upsert_runtime_printers-Garantie) — skip wenn NULL". LOW — Kein Downgrade-Pfad dokumentiert Spec enthält keinen Was gut ist
Die beiden LOW-Findings blockieren nicht — APPROVE. storage-agent, Round-3 |
Round-3 Spec-Review: code-qualityStatus: NEEDS_FIXES (2 neue Medium-Findings) Round-2 Findings — Verifikation
Neue Round-3 Findings[MEDIUM] M11 — Spec definiert: class PrinterDisabledError(LabelHubException):
http_status = 409Live-Check: Der Implementer steht vor der Wahl: Fix: Spec ergänzt [MEDIUM] M12 — Create-Flow Step 5c: row_dict = payload.model_dump() + {"id": printer_id, "created_at": ..., "updated_at": ...}
# INSERT INTO printers (...)
Der Implementer muss manuell flatten: row_dict["queue_timeout_s"] = payload.queue.timeout_s
row_dict["cut_defaults_half_cut"] = payload.cut_defaults.half_cut
row_dict.pop("queue")
row_dict.pop("cut_defaults")Das ist nicht trivial und fehleranfällig (besonders beim [LOW] Engine-Snippet Reihenfolge (Lesbarkeit) M7-Snippet zeigt Außerdem: Live Was gut ist
|
Round-4 Spec-Review: code-qualityStatus: APPROVE Round-3 Findings — Verifikation
Neues Finding — R4a:
|
Plan-Review Round-1: storage-TeamStatus: NEEDS_FIXES (2 Medium, 2 Low) FindingsMEDIUM — Task 1.1: isolation_level="SERIALIZABLE" mappt nicht zuverlässig auf aiosqliteDas Plan-Snippet setzt MEDIUM — Task 1.3:
|
Plan-Review Round-1: ops-TeamStatus: NEEDS_FIXES FindingsHIGHH1 — Parameter-Name-Bug Task 8.2: Fix: H2 — Rollback-Pfad fehlt komplett Minimaler Rollback-Step nach 8.3:
Ohne diesen Pfad ist kein sicheres Abbrechen möglich. MEDIUMM1 — Phase 0: Volume-Mount-Check fehlt mcp__dockhand__exec_container(command=["sh", "-c", "mount | grep /data"])Schützt vor dem Fall dass das Backup auf den falschen Pfad läuft. M2 — Pangolin Newt-Sync-Delay nicht berücksichtigt (Smoke 8.4 Step 4) Fix: Vor dem Browser-Test explizit warten — LOWL1 — Task 3.3 Granularität Was gut ist
|
Plan-Review Round-1: code-qualityStatus: NEEDS_FIXES (2 HIGH, 3 MEDIUM, 2 LOW — kein Blocker für Implementierungsstart wenn HIGH-Punkte vor Task 3.2 bzw. 4.1 adressiert werden) FindingsHIGHH1 — GET /api/printers filtert
Task 7.1 testet das Verhalten erst als E2E — wenn H2 — Task 3.2
MEDIUMM1 — Task 3.4 „Pattern wie Task 3.3" für 5 Routes reicht nicht Task 3.3 schreibt 5 Tests (list, new-form, create-post, edit-prefilled-stub, confirm-disable-stub). Task 3.4 hat kein einziges Test-Snippet für die 5 fehlenden Routes (edit-GET, edit-POST, disable-GET, disable-POST, enable-POST). Für Soft-Routes wie enable/disable-POST sind Redirect-Ziel und 409-Konflikt-Fall die Spec-relevanten Assertions. Ein Implementer der nur Task 3.4 liest, schreibt unter Zeitdruck Minimal-Tests ohne die Fehler-Pfade. M2 — Phase 6 ohne Verifikations-Task für Pangolin Header-Auth Phase 6 endet mit Labels im Compose. Der erste echte Smoke-Test ist Task 8.4 Step 5 (PrintService 409). Es fehlt ein direkter curl-Test M3 — Plan sagt: „Falls LOWL1 — Task 4.1 Fixture-Setup nicht ausgeschrieben
L2 — Coverage-Schwelle für Der Plan selbst setzt 70% für die Web-Routes, während die JSON-API (80%) und der Service (85%) höher liegen. Die Web-Routes enthalten Form-Parsing, Redirect-Logik und CSRF-Flow — das sind Mutations-Pfade. 70% ist nach test-coverage-pflicht.md für „HTMX-Event-Wiring / Form-Handler" Minimum 80%. Empfehlung: auf 80% anheben oder in die Coverage-Whitelist mit Begründung aufnehmen. Was gut ist
|
Plan-Review Round-1: network-TeamStatus: APPROVE mit 3 Anmerkungen (keine CRITICAL/HIGH) FindingsMEDIUM — Phase 0 Step 3: MCP-Tool-Name stimmt nicht (wird fehlschlagen)Plan schreibt: Das MCP-Tool heisst in dieser Umgebung so nicht — Pangolin MCP nutzt andere Tool-Namen (ermittelbar via Tool-Discovery). Subagent wird auf Fix: Step 3 umformulieren auf SSH-Fallback: # Vault-Key: homelab-pangolin-integration-api (Feld: password)
PANGOLIN_URL="https://backend-api.strausmann.cloud"
curl -sf -H "Authorization: Bearer ${PANGOLIN_TOKEN}" \
"${PANGOLIN_URL}/v1/org/strausmann/resources" \
| python3 -c "import json,sys; [print(r['resourceId'], r.get('fullDomain')) for r in json.load(sys.stdin)['data']['resources'] if 'print-hub' in r.get('fullDomain','')]"Alternativ: MCP-Tool-Namen via LOW — Phase 3.1 CSRF: zwei Implementierungs-Alternativen ohne EntscheidungPlan zeigt Empfehlung: LOW — Phase 3.2
|
Plan-Review Round-2: ops-TeamStatus: APPROVE (mit einem LOW-Finding zur expliziten Dokumentation) Round-1 Findings — VerifikationH1 Rollback (Task 8.5): Adressiert. 8 Steps vollständig. Reihenfolge ist korrekt: Stack down → DB-Restore → WAL/SHM-Removal → Compose-Revert → Env-Re-Merge → printers.yaml-Restore → Stack-Start → Verifikation. WAL/SHM-Removal in Step 2 direkt nach dem DB-Restore und vor Stack-Start ist die richtige Position — SQLite würde andernfalls beim Mount den WAL-Zustand wiederherstellen und den clean Restore überschreiben. C1 policy= Fix (Tasks 8.2 + 8.4): Adressiert. Beide Calls nutzen jetzt korrekt M6 Smoke-Schritte: Adressiert. Task 8.4 enthält jetzt json_extract Integer-Output-Hinweis (0/1 statt false/true) und Pangolin-Bug-#3099-Vermerk. Reicht. Neue Round-2 Findings[LOW] Task 8.5 Step 3: [KEINE ISSUE] Task 6.3 Reihenfolge: Die curl-Schritte in 6.3 sind als Pre-Deploy-Verifikation markiert — das macht sie zu einem "diese Labels funktionieren bereits"-Check. Der Text sagt klar "Nach Stack-Update in Phase 8.3 muss der Header-Auth-Bypass funktionieren. Dieser Task wird VOR dem Smoke-Test (8.4) explizit ausgeführt". Das bedeutet: 6.3 läuft nach 8.3, nicht als Phase-6-Check. Die Positionierung im Plan unter Phase 6 ist irreführend, aber der Text-Kommentar in Task 6.3 macht die tatsächliche Ausführungsreihenfolge klar. Kein Bug. [KEINE ISSUE] Task 2.6/2.7 und Hangar PrinterSync: Plan sagt, Hangar filtert disabled Drucker bereits im Cache-Layer heraus. Smoke-Test 8.4 Step 2 verifiziert DNS-Erreichbarkeit zwischen den Containern. Ein dedizierter Ops-Team gibt APPROVE. Der LOW-Hinweis zu |
Plan-Review Round-2: network-TeamStatus: APPROVE Round-1 Findings — VerifikationC3 Falsch-Positiv akzeptiert: Ja L1 Pangolin Bug #3099: adressiert L2 CSRF-Variante: adressiert Neue Round-2 FindingsR2-N1 (LOW): Task 6.3 Reihenfolge — leicht irreführend Empfehlung: In Task 6.3 Intro-Zeile klarstellen: "Dieser Task ist in Phase 6 vorbereitet (Credentials, Vault), wird aber nach 8.3 ausgeführt — erst dann sind die Labels live." Die aktuelle Formulierung sagt das schon halb, aber ein expliziter Satz "Ausführungs-Zeitpunkt: nach Task 8.3, vor Task 8.4" beseitigt jeden Zweifel. R2-N2 (LOW): Pangolin-Resource Bestand-Detection fehlt Das ist per ("Labels sind Source of Truth, API-Änderungen werden überschrieben") korrekt und gewollt. Kein Plan-Defekt, nur eine Klarstellung: wenn Phase 0 Step 3 zeigt dass , muss das Secret aus dem Vault-Item in 6.1 mit dem bestehenden übereinstimmen oder bewusst überschrieben werden. Dieser Hinweis fehlt in Task 6.1/6.2. Empfehlung: In Task 6.2 Step 2 eine Bemerkung: "Falls Phase 0 Step 3 zeigt dass headerAuth bereits gesetzt ist: das neue Secret aus Task 6.1 ersetzt es. Vault-Item entsprechend anlegen/aktualisieren." Beide Round-2-Findings sind LOW und blockieren die Umsetzung nicht. Der Plan ist in allen network-relevanten Punkten korrekt ausgearbeitet. |
Plan-Review Round-2: storage-TeamStatus: NEEDS_FIXES (1 MED neu) Round-1 Findings — Verifikation
Neue Round-2 Findings[MED] R2-M1 — Sync/Async-Mismatch in Der Test ruft Das ist auf der Produktions-Seite korrekt: Das Problem liegt im Test: Fix: Test muss # Option A — sync engine direkt
with engine.sync_engine.begin() as conn:
mig._backfill_snmp(conn)
# Option B — run_sync Pattern (spiegelt Alembic-Produktionsverhalten)
async with engine.begin() as conn:
await conn.run_sync(mig._backfill_snmp)Option B ist vorzuziehen weil sie 1:1 das Alembic- [LOW] R2-L1 — Rollback Task 8.5 Step 2: Reihenfolge ist korrekt Die Frage war ob WAL/SHM vor oder nach dem Restore gelöscht werden müssen. Die Plan-Reihenfolge (cp backup → db, dann rm WAL/SHM) ist korrekt: Stack ist bereits in Step 1 gestoppt, kein SQLite-Prozess kann neue WAL-Dateien anlegen. Das WAL/SHM-Löschen nach dem Restore bereinigt stale Dateien die zur alten (fehlerhaften) DB gehören. Kein Fix nötig. [LOW] R2-L2 — C2 Task 2.6 Step 5 sagt bereits: ZusammenfassungEin echter Bug: R2-M1 muss gefixt werden bevor der Test aussagekräftig ist — aktuell testet |
Plan-Review Round-2: code-qualityStatus: NEEDS_FIXES (2 neue Findings, 1 davon MEDIUM) Round-1 Findings — Verifikation
Neue Round-2 FindingsR2-M1 (MEDIUM) — Task 8.5 Step 4: Potential-Duplikat bei Rollback-Env-Merge # Task 8.5 Step 4:
existing = mcp__dockhand__get_stack_env(...)
merged = list(existing["variables"]) + [
{"key": "PRINTER_CONFIG_PATH", "value": "/etc/printer-hub/printers.yaml", ...},
]Wenn Fix: vor dem Concat explizit filtern: merged = [v for v in existing["variables"] if v["key"] != "PRINTER_CONFIG_PATH"] + [
{"key": "PRINTER_CONFIG_PATH", "value": "/etc/printer-hub/printers.yaml", "isSecret": False},
]Das ist symmetrisch zu Task 8.3 Step 1 und macht den Rollback idempotent. R2-L1 (LOW) — Task 8.5 Step 3: Task 8.5 Step 3 referenziert Der Rollback-Pfad setzt voraus dass der Agent den originalen Compose-Content irgendwo hat — das ist nur der Fall wenn Task 6.2 Step 1 ( Fix: In Task 0.1 (Phase 0) einen zusätzlichen Step ergänzen:
Alternativ in Task 8.5 Step 3 den Abruf direkt vor dem Revert machen (aber dann ist fraglich ob der Container schon crasht und R2-INFO — Task 2.7 Test-Konsistenz: kein Query-Param mehr, Test OK
ZusammenfassungR2-M1 ist ein echter Rollback-Korrektheitsfehler — im Fehlerfall (den dieser Task absichern soll) kann der Env-State inkonsistent werden. Bitte vor Round-3 adressieren. R2-L1 ist ein Dokumentationslücke die den Rollback-Pfad fragil macht. |
Plan-Review Round-3: ops-TeamStatus: APPROVE Round-2 Findings — Verifikation
Round-3 SpezifischTask 8.3.5 Reihenfolge (R2-L2): Verschiebung nach Phase 8 ist korrekt. Phase 0 Step 3 Bestand-Detection (R2-L3): 4-Fälle-Entscheidungsbaum deckt alle Zustände ab: null/claude-automation/fremder-User/healthCheck-disabled. Der STOP-Fall bei fremdem User ist wichtig und korrekt als manuelle Klärung markiert. Vollständig. Keine neuen Round-3-Findings aus ops-Perspektive. ops-Team Review, Sonnet 4.6 |
Plan-Review Round-3: network-TeamStatus: APPROVE Round-2 Findings — VerifikationR2-L2 Task 8.3.5 Verschiebung: adressiert. Task 6.3 ist jetzt ein leerer Platzhalter mit explizitem Hinweis auf Task 8.3.5. Die Positionierung zwischen 8.3 (start_stack) und 8.4 (Smoke-Test) ist korrekt. R2-L3 Bestand-Detection: adressiert. Phase 0 Step 3 enthält einen vollständigen 4-Fall-Entscheidungsbaum (null / claude-automation / anderer User / healthCheck.disabled). Round-3 FindingsTask 8.3.5 Bestand-Detection — Task 6.2 Compose-Snippet: Passwort-Klartext korrekt auf |
Plan-Review Round-3: storage-TeamStatus: APPROVE (mit einem LOW-Hinweis) Round-2 Findings — VerifikationR2-M1 run_sync: vollständig adressiert. Task 1.3, Zeilen 488-493: der Test ruft jetzt Neue Round-3 FindingsR3-L1 (LOW): Task 8.5 Step 2 — WAL/SHM-Reihenfolge semantisch falsch Aktuell: SQLite WAL-Semantik: Das alte Risikobewertung: LOW — Stack ist in Step 1 gestoppt, kein Prozess öffnet die DB zwischen cp und rm. In der Praxis kein Crash-Risiko, aber semantisch ist rm-vor-cp die korrekte Reihenfolge bei SQLite-WAL-Restore. Alle anderen Aspekte (R2-M2 Filter in Step 4, R2-L1 Phase-0-Sicherung, Rollback-Gesamtstruktur) sind korrekt. Plan ist umsetzungsbereit. |
Plan-Review Round-3: code-qualityStatus: APPROVE Round-2 Findings — Verifikation
Neue Round-3 FindingsR3-L1 (LOW): Task 6.3 Verweis-Stub — Navigation leicht riskant Der 3-Zeilen-Stub in Phase 6 lautet sinngemäß: "Dieser Schritt wurde nach Task 8.3.5 verschoben." Das ist korrekt, aber ein Implementer der sequenziell arbeitet könnte in Phase 6 stoppen und denken, die curl-Verifikation entfällt. Der Stub enthält keinen direkten Anker-Link auf Task 8.3.5, nur "Siehe Task 8.3.5 unten". In einem 4000-Zeilen-Plan ist das ausreichend, wenn der Implementer von oben nach unten arbeitet — aber bei Subagent-Driven-Development, wo Agents direkt auf benannte Tasks dispatcht werden, könnte Phase 6 ohne 8.3.5 als vollständig erscheinen. Empfehlung: Stub mit explizitem Hinweis "8.3.5 ist Pflicht-Schritt, KEIN optional" ergänzen. Kein Blocker. R3-N1 (NOTE): Phase 0 Step 3 STOP-Fall — Fallback vorhanden, ausreichend Der Entscheidungsbaum hat einen R3-N2 (NOTE): Coverage-Tabelle vs. Task 8.3.5 Task 8.3.5 ist ein reiner curl-Verifikations-Task ohne neuen Code — keine Coverage-Implikation. Die Coverage-Tabelle referenziert ihn korrekt nicht. ✅ GesamtbewertungAlle Round-2 Findings korrekt und vollständig adressiert. R3-L1 ist ein Low-Risiko Navigations-Hinweis der bei sequenzieller Ausführung nicht relevant ist. Plan ist umsetzungsreif. |
Spec-Reset Round-1: ops-TeamStatus: NEEDS_FIXES (2 HIGH, 1 MEDIUM — kein Blocker für Implementierung, aber vor Deploy-Phase A schließen) FindingsHIGHH1 — Rollback zu vage für Implementer Spec sagt nur: Die alte Spec (2026-06-14) ist als obsolet markiert. Ein Implementer der die alte Spec nicht kennt kann den Rollback nicht ausführen. Konkret fehlt: # Was ein Implementer braucht:
docker cp label-printer-hub-backend:/data/printer-hub.db.bak-pre-124 /tmp/
docker exec label-printer-hub-backend sh -c "sqlite3 /data/printer-hub.db '.restore /data/printer-hub.db.bak-pre-124'"
mcp__dockhand__update_stack_compose(env=10, name='label-printer-hub',
content='<Compose-Snapshot aus Phase A.3>', restart=True)
mcp__dockhand__set_container_auto_update(env=10,
containerName='label-printer-hub-backend', policy='any')
mcp__dockhand__set_container_auto_update(env=10,
containerName='label-printer-hub-frontend', policy='any')Fix: Rollback-Abschnitt in der Spec ausschreiben. Referenz auf veraltete Spec entfernen. H2 — Vault-Item für headerAuthId 8 nicht verifiziert Spec benennt R5 korrekt, erklärt aber nicht was Phase 0 konkret tun soll. headerAuthId 8 erscheint in der Pangolin-API-Antwort im -Feld, nicht im -Feld — der User-Name ist aus dem Live-Check nicht ablesbar. Wenn beim Smoke-Test in Phase C ein Fix: Phase 0 muss explizit: MEDIUMM1 — Migration schlägt fehl wenn Backend-Container-Start abbricht Spec beschreibt: Healthcheck Mitigation: Alembic-Migrationen sind idempotent (Standard-Verhalten). Das löst den Loop. Aber der Implementer sollte explizit wissen: Fix: Einen Satz in Phase B ergänzen: LOWL1 — HUB_VERSION Migration nicht addressiert Spec erwähnt korrekt dass Production auf Was gut ist
ops-Team Review — Spec-Reset Round-1 — 2026-06-19 |
Spec-Reset Round-1: network-TeamStatus: NEEDS_FIXES (2 Punkte, 1 davon CRITICAL) FindingsCRITICALF1 — healthcheck.hostname fehlt im Live-State Die Spec zeigt unter Live-State nur Der Live-State-Dump ( Aktion für Implementer (Phase 0): Zielwert muss MEDIUMF2 — Remote-User → updated_by: Propagation-Mechanismus nicht spezifiziert Spec sagt (Abschnitt "Frontend-Änderungen / Auth"): "Frontend liest Remote-User aus Request-Header, nutzt als Offen: Über welchen HTTP-Header übergibt das Frontend den Remote-User-Wert ans Backend? Pangolin reicht Empfehlung: Spec explizit machen: Frontend setzt LOW / INFOF3 — Service-Account-API-Key Quelle nicht spezifiziert Spec erwähnt "Service-Account-API-Key" für Frontend→Backend-Kommunikation ohne Vault-Item-Referenz. Kein Blocker für den Implementer (der legt den Key an), aber Audit-Trail wird mit F4 — R5 (headerAuthId 8): Aktion bei Nicht-Konformität fehlt Spec sagt: "Phase 0 muss klären welcher User/Pass aktiv ist." Kein konkreter Schritt falls User NICHT F5 — CSRF-Entscheidung: delegieren ist OK "Implementer prüft existing Pattern" ist akzeptabel — Network-Perspektive: Kein Pangolin-Resource-ÄnderungsbedarfBestätigt: Issue #124 erfordert keine Änderung an Resource 123, SSO-Konfiguration, Routing oder DNS. Die Two-Container-Architektur (Frontend :8080, Backend intern :8000) ist korrekt im Target abgebildet. Offene Validierung vor Merge: F1 muss verifiziert sein (hcHostname gesetzt). Alles andere kann der Implementer direkt lösen. |
Neuer Plan basierend auf Spec Round-6 Working Draft. Strategie-Wechsel: nicht weiter Spec-Annahmen-Fehler korrigieren, sondern Plan robust gegen sie machen. Plan-Prinzip: - Phase 0 sammelt ALLE Live-Werte (Mount-Pfade, API-Routes, ENV-VARS, DB-Stand) in docs/superpowers/plans/2026-06-19- phase0-live-state.md. - Jede weitere Phase startet mit Pre-Check-Step der relevante Werte zieht. - Bei Konflikt Spec ↔ Live-Container: NIEMALS Spec-Wert nutzen, IMMER Live-Wert + PR-Kommentar mit Befund. Struktur: - 22 Tasks in 9 Phasen - Backend (Python, 13 Tasks): Foundation + Service-Layer + JSON-API + PrintService + Removal - Frontend (Go, 4 Tasks): gorilla/csrf nachgeruestet auf existing Admin-Routes + admin_printers.go Handler + 3 Templates + Router-Wireup mit oapi-codegen Update - Phase 6.0 Bootstrap: SSH-Direktaufruf in Backend-Container fuer Service-Account-Key (kein Pangolin-Bootstrap) - Production-Deploy mit LIVE-Pfaden: /docker/stacks/hangar-print-hub/data/hub/printer-hub.db /docker/stacks/hangar-print-hub/config/printers.yaml CSRF-Hardening eingerollt: existing Admin-API-Keys-Routes werden in derselben Migration mit csrfMW gesichert. Refs #124 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Round-5 Reviews: ops/network/storage APPROVE, code-quality NEEDS_FIXES.
HIGH
- H1 (code-q) Bootstrap-Code referenzierte nicht-existente APIKeyService.
Echte Codebase nutzt Repository-Pattern: api_keys_repo.create(session,
ApiKey). Korrigiert mit bcrypt.hashpw + secrets.token_urlsafe + echter
ApiKey-Konstruktion. Plus Implementer-Verifikation per inspect.signature
vor Ausfuehrung.
MEDIUM
- M1 (code-q) Scope 'admin:printers' → 'admin'. Existing Codebase nutzt
3-stufige Hierarchie read/print/admin (admin ⊇ print ⊇ read), KEIN
fine-grained admin:* Scope. Bootstrap-Key mit ['admin'].
- M2 (ops) compose-passthrough-Pflicht fuer Secrets: BACKEND_SERVICE_
ACCOUNT_KEY + CSRF_KEY muessen in compose.yaml unter environment: als
${VAR} deklariert sein, sonst Silent-Failure. Neue Phase 6.0 Step 4b
mit Verifikations-Befehl docker exec env grep.
LOW
- ops: get_stack_env Baseline als Pre-Check + Image-Digest beider
Container vor Deploy festhalten fuer Phase 8.5 Rollback ohne Raten.
- network: headerAuthId 8 ist NICHT in /v1/resource API sichtbar, statt-
dessen Vault-Item Smoke-Test (curl -u claude-automation). Plus
Pangolin Bug #3099 Hinweis in Phase 8.4 Smoke (Browser zeigt
Basic-Auth-Dialog statt SSO-Redirect, Cancel → SSO).
Refs #124
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Privacy / secret scan CI-Fail behoben: - Verworfene Reset-Spec geloescht (2026-06-19-printers-env-to-db-design.md) - Obsoleter Round-4-Plan geloescht (2026-06-14-printers-yaml-to-db-plan.md) - Aktive Spec + Plan: alle privaten Artefakte ersetzt: - Domains *.strausmann.cloud/de/net → *.example.test - Hostnames hhdocker0X → docker-prod-node / docker-secondary-node - Private IPs (172.16.x.x) → 192.0.2.x (RFC 5737) Plan Round-7 Fixes (code-quality Round-6 NEEDS_FIXES): - H1: Bootstrap-Code nutzt jetzt generate_api_key() Helper aus app/auth/key_generator.py statt manueller Key-Erzeugung. Liefert korrektes Format lh_pat_<43-char> + 16-char-Prefix. Sonst stille 401-Fehler. - H2: Vault-Notes Scope-Klaerung 'admin (3-stufig: admin ⊇ print ⊇ read)' statt verwirrendem 'admin:printers'. - M2: oapi-codegen Workflow konkretisiert — make gen-client ruft laufendes Backend per curl ab, NICHT lokale Datei. Phase 7.2 mit zwei Optionen (lokal uvicorn vs. Remote via Pangolin Bypass). Refs #124 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Gemini-Re-Review nach Privacy-Push hat 3 MED-Findings aufgezeigt: - CSRF Spec-Snippet (Round-4 obsolete-marker Block) zeigte len(csrfKey) != 32 statt korrekt != 64 (64 hex chars = 32 raw bytes). Inline-Fix mit Kommentar. - Privacy-Pedantik: docker-prod-node und id_ed25519_homelab_nodes als zu spezifisch. Auf prod-node.example.test + id_ed25519_placeholder umgestellt. - Plan Task 1.1: existing _apply_pragmas-Listener in backend/app/db/engine.py setzt schon WAL + foreign_keys + synchronous + busy_timeout. Plan-Anweisung NUR isolation_level=SERIALIZABLE ergaenzen, KEINEN zweiten Listener einfuehren. Refs #124 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Round-1-4-Backup-Block in Spec explizit als OBSOLET markiert (verwendete /docker/stacks/label/... statt live-verified /docker/stacks/hangar-print-hub/data/hub/). - L3-Anhang-Note korrigiert: Live-Pfad ist NICHT der per STACKS_BASE_HOMEDIR abgeleitete /docker/stacks/label/-Pfad, sondern /docker/stacks/hangar-print-hub/data/hub/. - Plan Rollback Step 2 jetzt mit ABSOLUTEN Pfaden statt relativ. example.com vs example.test: bleibt bei .test (RFC 6761 reserved TLD fuer testing, auch privacy-scan-compliant). Refs #124 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Live-verifiziert via docker inspect + docker exec + git show: - DB-Pfad: /docker/stacks/hangar-print-hub/data/hub/printer-hub.db - Image-SHAs beider Container fuer Rollback - 2 Bestandsdrucker (brother-p750w + brother-ql820nwb) - 8 Tables existing, printers_audit fehlt noch - Frontend HAT KEINE CSRF-Library — gorilla/csrf in Phase 7.1 neu - engine.py _apply_pragmas existiert bereits (Plan Task 1.1: nur isolation_level=SERIALIZABLE ergaenzen, KEIN zweiter Listener) Refs #124
…verwendet isolation_level="SERIALIZABLE" explizit an create_async_engine() übergeben. Kein zweiter Connect-Listener nötig — der bestehende _apply_pragmas-Hook setzt bereits journal_mode=WAL, synchronous=NORMAL, foreign_keys=ON und busy_timeout=5000 korrekt. Tests in test_engine_pragmas.py prüfen: - dialect._on_connect_isolation_level == "SERIALIZABLE" (explizit gesetzt) - WAL + foreign_keys und SERIALIZABLE koexistieren ohne Konflikte
Neue Exception-Klasse PrinterDisabledError als Subclass von PrinterError. Konstruktor nimmt printer_id (UUID) und slug positional entgegen und mappt semantisch auf HTTP 409 (Drucker existiert, ist nur deaktiviert). TDD: 3 neue Tests (Hierarchie, Attribut-Speicherung, str-Inhalt) erst auf FAIL geprüft, dann Implementierung ergänzt — alle 940 Tests grün.
…ackfill - Neue Spalten queue_timeout_s (Integer, NOT NULL, default 30) und cut_defaults_half_cut (Boolean, NOT NULL, default false) auf printers-Tabelle - Neue printers_audit-Tabelle mit 8 Pflichtfeldern und 2 Indizes (idx_printers_audit_printer_id, idx_printers_audit_created_at_desc) - _backfill_snmp als top-level Funktion (testbar via run_sync): befüllt connection.snmp für Bestandsdrucker mit host-Feld, idempotent (überspringt rows mit vorhandenem snmp) - PrinterAudit SQLModel-Klasse in printer.py + __init__.py Export damit alembic check keinen Drift erkennt - 3 TDD-Tests (T1: Spalten, T2: Audit-Tabelle+Indizes, T3: Backfill) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ben (DESC)
- `text("created_at DESC")` in `__table_args__` von PrinterAudit konsistent
mit der Migration `42fbd015698d` (die bereits `sa.text("created_at DESC")`
verwendet)
- `text`-Import in `models/printer.py` ergänzt
- `alembic/env.py`: `_include_object`-Hook hinzugefügt der Text-Expression-
Indexes vom Autogenerate-Vergleich ausschließt — SQLite kann solche Indexes
nicht reflektieren, was sonst einen False-Positive-Drift in `alembic check`
erzeugt; die Migration selbst erstellt den Index weiterhin korrekt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Erweitert derive_printer_id(model, host, port) auf 4-arg-Signatur mit pflichtmäßigem created_at_utc (timezone-aware datetime). Naive datetime löst ValueError aus (TZ-Sensitivität explizit gewollt). upsert_runtime_printers nutzt Slug-Lookup für bestehende Drucker (liest created_at aus der DB → stabile UUID über Neustarts), und datetime.now(UTC) für neue Drucker. Tests die auf 3-arg-UUID-Stabilität angewiesen sind temporär mit @pytest.mark.skip markiert (Issue #124 Phase 5). Coverage printer_identity.py: 100% (10 Unit-Tests). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Task 2.1: Erstellt `backend/app/schemas/printer_admin.py` mit
SNMPConfig, PrinterConnection, PrinterCutDefaults, PrinterQueueSettings,
PrinterCreatePayload und PrinterUpdatePayload als Pydantic v2-Schemas.
- SNMPConfig: discover=False-Default, community-Pflicht bei discover=True
- SLUG_PATTERN r"^[a-z0-9][a-z0-9-]{1,62}[a-z0-9]$" erzwungen
- Backend-Literal lehnt unbekannte Werte im Schema ab
- PrinterUpdatePayload: alle Felder optional (PATCH-Semantik)
- 38 Tests (100% Coverage), mypy strict clean, ruff clean
Refs #124
Fügt `app/services/audit_redaction.py` hinzu: Deep-Copy-basierter Redaction-Helper der bekannte Secret-Pfade (z.B. connection.snmp.community) durch '***REDACTED***' ersetzt bevor before_json/after_json in printers_audit geschrieben werden. - SECRET_PATHS als frozenset[tuple[str,...]] erweiterbar - None-Werte werden NICHT redacted (fehlende Werte nicht verschleiern) - Fehlende Zwischenpfade werden stillschweigend übersprungen - 5 TDD-Tests, 89% Coverage (≥80% Gate bestanden) - mypy strict + ruff clean Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Neue Registry-Schicht für verfügbare (backend, model)-Kombinationen. Liest aus ptouch.PRINTERS (Spec-Pfad) bzw. ptouch.printers-Klassen sowie brother_ql.models.MODELS (Spec-Pfad) bzw. ALL_MODELS (0.9.4 API). Fällt auf HARDCODED_FALLBACK_MODELS zurück wenn beide Plugins leer sind. - 12 Tests (TDD), 88% Coverage, mypy strict clean, ruff clean - PrinterModel frozen dataclass, beide Plugin-APIs zweistufig abgesichert
…rding Implementiert Tasks 2.4 + 2.5 aus Issue #124 (Printers YAML-to-DB Migration). Flattening-Helper (top-level Funktionen): - _payload_to_row: Pydantic-Payload → flache DB-row (connection als JSON, queue/cut_defaults flat) - _apply_update_patch: PATCH-Semantik, gibt nur geänderte Spalten zurück - _row_to_audit_view: Rekonstruiert verschachtelte Audit-JSON-Darstellung PrinterAdminService (AsyncSession + audit_user): - create_printer: UUIDv5 via derive_printer_id, flush+commit, DuplicateSlug/NameError - update_printer: PATCH-Semantik, setattr per change, PrinterNotFoundBySlugError - disable_printer / enable_printer: Soft-Delete mit PrinterAlreadyDisabled/EnabledError - list_printers: include_disabled=False (default) filtert deaktivierte aus - _record_audit: INSERT printers_audit mit redact_secrets(before/after) Tests: 42 Tests (TDD), Coverage 98.80% (≥85%-Pflicht erfüllt) mypy --strict: clean, ruff: clean Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…disabled Task 2.6: list_all erhält include_disabled=False als Keyword-Only-Default. Standard-Aufrufe schließen deaktivierte Drucker aus (Soft-Delete-Filter). Admin-UI kann include_disabled=True übergeben für die vollständige Liste. Task 2.7: GET /api/printers ruft list_all ohne include_disabled — Public-API filtert deaktivierte Drucker immer heraus. Kein neuer Query-Parameter. Tests: 4 Repo-Unit-Tests (test_printers_repo.py) + 2 Route-Tests ergänzt. Volle Suite: 1042 passed, 13 skipped. Refs #124
Implementiert die JSON-Admin-API für Drucker-Verwaltung (Task 3.1):
- GET /api/v1/admin/printers — Liste (include_disabled-Filter)
- POST /api/v1/admin/printers — Anlegen (201, 409 bei Dup-Slug/Name)
- GET /api/v1/admin/printers/{slug} — Einzelabruf (200, 404)
- PUT /api/v1/admin/printers/{slug} — Aktualisieren via PATCH-Semantik (200, 404)
- POST /api/v1/admin/printers/{slug}/disable — Soft-Delete (200, 404, 409 already_disabled)
- POST /api/v1/admin/printers/{slug}/enable — Reaktivierung (200, 404, 409 already_enabled)
Auth: require_admin (analog admin_api_keys.py), kein CSRF, nur JSON.
Service-Layer: PrinterAdminService mit Audit-Trail-Unterstützung.
Tests: 19 Unit-Tests (TDD, 100% Coverage bei korrektem async-Concurrency-Tracking).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ledError
- PrintService.__init__ erhält printer_slug + printer_enabled (default True)
- submit_print_job prüft am Anfang ob Drucker aktiv ist; wirft PrinterDisabledError
bevor preflight_check oder queue.submit aufgerufen werden
- POST /print Route: PrinterDisabledError → 409 mit Body {"error": "printer_disabled", "slug": "..."}
(spec-konformes Format, abweichend von error_code-Muster der anderen Fehler)
- 5 neue Tests: 3 service-level + 1 http-level + 1 enabled-happy-path
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tfernt Phase 5: YAML→DB-Sync-Pfad vollständig entfernt. Lifespan lädt Drucker jetzt ausschließlich aus der DB (SELECT Printer WHERE enabled=true). Entfernt: - app/schemas/printer_config.py (PrinterYAMLConfig, SNMPConfig, etc.) - app/services/printer_config_loader.py (PrinterConfigLoader) - tests/db/test_lifespan.py - tests/unit/test_lifespan.py - tests/services/test_printer_config_loader.py - tests/integration/test_lifespan_seeds_and_upserts.py - tests/integration/test_lifespan_multi_printer.py - tests/integration/db/test_lifespan_printer_upsert.py Geändert: - app/db/lifespan.py: upsert_runtime_printers() + Imports entfernt - app/main.py: PrinterConfigLoader → _build_configs_from_db() (DB-Loading), Empty-Printer-Guard für Fresh-Install (app.state.print_service=None) - app/services/backend_router.py: PrinterYAMLConfig + verwandte Klassen von printer_config.py hierher verschoben (primärer Konsument) Tests: 1035 passed, 19 skipped (printer-abhängige Tests auf Task C2 auto-seed-Drucker warten), 0 failed. mypy strict + ruff clean.
- gorilla/csrf v1.7.3 als direkte Dependency eingeführt
- buildCSRFMiddleware() liest CSRF_KEY (64 Hex-Zeichen = 32 Bytes) aus Env
- newRouter() nimmt csrfMW-Parameter; Admin-Subrouter (/admin/*) mit Middleware
- TemplateData.CSRFField (template.HTML) + baseData()-Helper für alle Admin-Handler
- admin_api_keys_create.html + admin_api_keys_detail.html: {{.CSRFField}} in POST-Forms
- csrf_test.go: 6 Tests (GET-OK, NoToken-403, WrongToken-403, ValidToken-200, TODO)
- main_test.go: newRouter()-Aufrufe auf 4-Parameter-Signatur aktualisiert
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ask 7.2)
Aktualisiert den OpenAPI-Snapshot und regeneriert den Go-Client um die 6 neuen
Admin-Printers-Endpoints aus Task 3.1 abzubilden:
GET/POST /api/v1/admin/printers
GET/PUT /api/v1/admin/printers/{slug}
POST /api/v1/admin/printers/{slug}/disable
POST /api/v1/admin/printers/{slug}/enable
Änderungen:
- openapi.snapshot.json: Vollständig erneuert via Backend-Introspection ohne
laufenden Server (mgr._app.openapi()); anyOf-null-Muster (OpenAPI 3.1) in
nullable:true (3.0-kompatibel) normalisiert für oapi-codegen-Kompatibilität
- client.gen.go: Regeneriert mit oapi-codegen v2.7.1; enthält neue Admin-
Printer-Typen (AppApiRoutesAdminPrintersApiPrinterRead, etc.)
- client.go: PrinterRead-Alias auf AppSchemasPrinterPrinterRead (Typ-Umbenennung
durch oapi-codegen nach Hinzufügen zweier gleichnamiger Schemas);
ListPrinters-Signatur um nil-Params ergänzt; ListTemplates als Stub der
ErrNotImplemented zurückgibt (GET /api/templates in Phase 1k.1a entfernt)
- jobs.go: CreatedAt-Feld ist jetzt string statt time.Time (Schema-Änderung)
- Tests: template_test.go + templates_test.go + main_test.go auf 503-Erwartung
aktualisiert; client_test.go: TestListTemplatesFiltersByApp durch
TestListTemplatesReturnsErrNotImplemented ersetzt
CI-Check "oapi-codegen output is up to date": go test -race ./... grün.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…r-Verwaltung (Tasks 7.3+7.4) - admin_printers.go: 9 Handler (ListPrintersPage, NewPrinterPage, CreatePrinter, PrinterDetailPage, EditPrinterPage, UpdatePrinter, DisablePrinterConfirmPage, DisablePrinter, EnablePrinter) + WithSlug-Varianten für Tests - admin_printers_test.go: 20 Tests, Coverage 70.8% (Happy-Path + Error-Path + Backend-Fehler) - base.go: 4 neue page names + Stub-Templates für Tests - 4 HTML-Templates: Liste, Formular (Create+Edit), Detail, Bestätigung-Deaktivierung - main.go: 9 neue Routes unter /admin/printers (CSRF-geschützt) go test -race ./... grün, go vet ./... sauber Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- EditPrinterPageWithSlug befüllt SNMP-Felder aus printer.Connection (verhindert silent data loss bei Edit-Submit ohne SNMP-Eingabe) - admin_printers_form.html placeholder: 192.168.1.100 → 192.0.2.10 - Neuer Test TestEditPrinterPageWithSlug_PrefillsSnmpFields verifiziert Prefill von discover=true (checked) + community="public" (value) - Coverage bleibt 71.2% (chi-Wrapper sind 2-Zeiler delegieren an WithSlug, die WithSlug-Varianten haben 76-100%) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5258a3e to
342243b
Compare
Status Update — Phase 7 fertigPhasen 0–7 alle commited + gepusht. Branch ist auf origin/main rebased. Code-Commits (10 in Reihe nach Live-State):
CI Status: 16/18 SUCCESS, 1 codecov/project FAILURE (-1.59% wegen skipped Phase-5 Tests pending Phase-6 Bootstrap), 1 in progress. Phase 6 + Phase 8 ausstehend — brauchen User-Aktion:
Bitte bei Wiederkunft: codecov-Threshold reviewen (akzeptabel weil pending bootstrap?) oder die skipped Tests entsperren? |
## 0.11.0 (2026-06-24) * feat(auth): Pangolin-SSO + Bypass für alle Scopes trusted (ADR 0014) (#133) ([4fe1a91](4fe1a91)), closes [#133](#133) [#78](#78) [130/#132](#132) * feat(nav): "Drucker" Link für Admin-Drucker-Verwaltung + getrennte ActiveNav-Werte (#135) ([1e982b0](1e982b0)), closes [#135](#135) [#104](#104) [#104](#104) [#104](#104) * fix(frontend): forward Remote-User zum Backend (Pangolin SSO-Standard-Header) (#132) ([38c0cc3](38c0cc3)), closes [#132](#132) [#130](#130) [#130](#130) [#131](#131) [#130](#130) * fix(frontend): forward X-Pangolin-Token zum Backend (Browser-User 503-Fix) (#130) ([5fb2038](5fb2038)), closes [#130](#130) * fix(frontend): forwardAuth ergänzt X-Pangolin-Token + Remote-User (Admin-Routes) (#134) ([af9ee28](af9ee28)), closes [#134](#134) [130/#132](#132) [#130](#130) [#132](#132) [#133](#133) * chore(deps): bump the go-minor-and-patch group across 1 directory with 2 updates (#128) ([a72dd90](a72dd90)), closes [#128](#128) * ci(deps): bump lewagon/wait-on-check-action in the actions-all group (#127) ([e4139ab](e4139ab)), closes [#127](#127) * docs(api): printers.yaml weg, Drucker in DB + /admin/printers Admin-UI (#124) [DRAFT] (#125) ([41bef28](41bef28)), closes [#124](#124) [#125](#125) [#124](#124) [#124](#124) [#124](#124) [#124](#124) [#124](#124) [#3099](https://github.com/strausmann/label-printer-hub/issues/3099) [#124](#124) [#124](#124) [#124](#124) [#124](#124) [#124](#124) [#124](#124) [#3099](https://github.com/strausmann/label-printer-hub/issues/3099) [#124](#124) [#124](#124) [#124](#124) [#124](#124) [#124](#124) [#124](#124) [#124](#124) [#124](#124) [#124](#124) [#124](#124) [#124](#124) [#124](#124) [#124](#124) [#124](#124) [#124](#124) [#124](#124) [compose-passthrou#Pflicht](https://github.com/compose-passthrou/issues/Pflicht) [skip ci]
Draft-Spec für Issue #124
Dieser PR enthält nur den Spec-Entwurf für die Migration von
printers.yamlin die DB plus eine neue Admin-UI/admin/printers/. Keine Code-Änderungen — Review-Grundlage für die 4 Fachteams.Issue: #124
Brainstorming: Session vom 2026-06-14 mit @strausmann
Kern-Entscheidungen (User-bestätigt)
HUB_PRINTERS_JSONwurde explizit verworfen — Operator legt Drucker nur über die Admin-UI an.derive_printer_id(model, host, port, created_at). Bestandsdrucker behalten ihre alten UUIDs (created_at war damals nicht im Salt), neue Drucker bekommen kollisionssicheren UUID auch bei IP/Port-Wiederverwendung.ptouch,brother_ql). Admin-UI Model-Dropdown wird aus Plugin-Registry gefüllt.printers_auditanalog Hangarlayouts_audit.GET /api/printers.Was sich konkret ändert
app/services/printer_config_loader.pyapp/db/lifespan.py::upsert_runtime_printers()app/schemas/printer_config.py/etc/printer-hub/printers.yamlVolume-Mountprinters.yamlaus/docker/stacks/hangar-print-hub/config/app/services/printer_admin_service.pyapp/api/routes/admin_printers.pyapp/web/routes/admin_printers.pyapp/templates/admin_printers/printers_auditReview-Fokus für Fachteams
/api/v1/admin/printers) wie in pangolin-resource-standard.md?Closes-Spec-of #124