[Docgen] Pro-Node aktivierbar + Konfig in DB statt .env + Task-Center/Audit-Integration #210

Open
opened 2026-07-20 22:12:38 +00:00 by chinux · 1 comment
Owner

Der Docgen (#17) ist heute global und ausschließlich über .env (DOCGEN_*) konfiguriert. Damit lässt sich nichts im UI schalten und keine Node gezielt ein-/ausnehmen. Dieses Issue macht Docgen pro Node aktivierbar, verlagert die Konfiguration in die DB und hängt jeden Lauf an Task-Center (#155) + Audit-Log.

Warum

  • Pro-Node-Opt-in: Nicht jede Node soll dokumentiert werden (Test-Nodes, fremde Umgebungen, Mandanten ohne Doku-Vertrag).
  • Kill-Switch im UI: .env ändern + Container-Restart ist kein Kill-Switch.
  • Nachvollziehbarkeit: Ein Prozess, der Infrastruktur-Daten in ein externes Git-Repo schiebt, muss lückenlos protokolliert sein — wer hat ihn ausgelöst, was wurde gepusht, welcher Commit.

Bestand, den wir nutzen

  • models/settings.pySetting (key/value JSONB) existiert und wird bereits von services/notifications.py und settings/settings_router.py genutzt.
  • services/task_hub.pytask_hub.start/log/progress/finish/is_cancelled — fertiges Muster, siehe admin_router.py:597 (QEMU-Scan).
  • models/task.pyTASK_KINDS muss nur erweitert werden.
  • models/audit_log.pyAuditLog(action, resource, details, user_id, ...).
  • Frontend-Muster für einen Node-Toggle: NodeSettingsModal.svelte, Zeilen um vpn_transport (#189) — Checkbox + Änderungs-Flag + PATCH.

Prompt für Claude Code

Mache den Docgen in theProx pro Node aktivierbar, verlagere seine Konfiguration
von .env in die settings-Tabelle und protokolliere jeden Lauf ueber Task-Hub
(#155) + Audit-Log.

--- 1. MIGRATION 0028 ---
server/migrations/versions/0028_node_docgen.py
(revision "0028_node_docgen", down_revision "0027_auto_update_include_host",
Format exakt wie 0027)

    op.add_column("nodes", sa.Column("docgen_enabled", sa.Boolean(),
                  server_default="false", nullable=False))

server/models/node.py: Spalte ergaenzen UND in to_dict() ausgeben
("docgen_enabled": bool(self.docgen_enabled)).

--- 2. KONFIG: .env -> settings-Tabelle ---
Neuer Settings-Key "docgen" (JSONB), Shape:

    {
      "enabled": false,
      "repo_url": "",
      "git_user": "",
      "git_token": "",          # write-only, s.u.
      "branch": "main",
      "interval_hours": 24,     # NEU: ersetzt die feste Stunde
      "hour": 3,                # Startstunde des Fensters
      "author_name": "theProx Docgen",
      "author_email": "docgen@example.com",
      "workdir": "/tmp/theprox-docgen"
    }

_cfg() in services/docgen.py umbauen: liest den Settings-Key aus der DB,
faellt pro Feld auf den bisherigen os.getenv(DOCGEN_*)-Wert zurueck. So bleiben
bestehende .env-Installationen ohne Migration lauffaehig. _cfg() wird damit
async — alle Aufrufer anpassen.

TOKEN-HANDLING (wichtig):
  - git_token wird NIE im GET zurueckgegeben. Stattdessen "git_token_set": bool.
  - Beim PATCH: leerer/fehlender git_token laesst den gespeicherten Wert
    unveraendert; ein gesetzter Wert ueberschreibt ihn.
  - _redact() beim Loggen bleibt wie es ist und muss weiter greifen.

--- 3. NODE-FILTER ---
In _fetch(): nur Nodes mit docgen_enabled == True laden.
Wenn KEINE Node aktiviert ist, bricht run_docgen() frueh ab mit
{"status":"skipped","reason":"keine Node fuer Docgen aktiviert"} und pusht
NICHTS (kein leeres Repo pushen, sonst loescht der naechste Lauf die Doku).

WICHTIG — Loeschverhalten: _build_and_push() wischt heute mandanten/ komplett
und baut neu. Wenn eine Node deaktiviert wird, verschwindet ihre Doku damit
still aus dem Repo. Das ist gewollt, muss aber sichtbar sein: zaehle vor dem
Commit die geloeschten Pfade (git status --porcelain, Zeilen mit "D ") und
schreibe sie in die Task-Summary + Commit-Message
("docgen: Infra-Snapshot <stamp> (+N/-M Dateien)").

--- 4. API ---
server/routers/docgen_router.py erweitern (alle require_superadmin):

  GET   /api/docgen/config   -> Config ohne Token, mit git_token_set: bool
  PATCH /api/docgen/config   -> Teil-Update (Pydantic-Modell mit Optional-Feldern)
  POST  /api/docgen/run      -> bleibt (force=True), gibt jetzt task_id zurueck
  GET   /api/docgen/status   -> {enabled, last_run_at, last_status, last_commit,
                                 nodes_enabled: int, next_run_at}

last_* in einem zweiten Settings-Key "docgen.state" persistieren (ueberlebt
Neustarts — heute liegt _LAST_RUN_DATE nur im Prozessspeicher, d.h. nach jedem
Container-Restart laeuft Docgen in derselben Stunde erneut).

PATCH auf einen Node (admin_router.py, NodeUpdate um ~Zeile 70 / Apply um ~287,
Muster: vpn_transport) um docgen_enabled: Optional[bool] erweitern.

--- 5. INTERVALL STATT FIXER STUNDE ---
maybe_run_docgen() umbauen: statt "nur wenn now.hour == cfg.hour und heute noch
nicht gelaufen" jetzt "wenn last_run_at aelter als interval_hours ist, und die
aktuelle Stunde >= cfg.hour (bei interval_hours >= 24)". Bei interval_hours < 24
zaehlt nur der Abstand. Das Slot-Claiming (erst Zeit setzen, dann laufen)
beibehalten, sonst laufen zwei Ticks parallel.

--- 6. TASK-HUB + AUDIT ---
models/task.py: TASK_KINDS um "docgen" erweitern.

run_docgen() umbauen auf das task_hub-Muster (Vorlage: admin_router.py:597):

    task_id = await task_hub.start(kind="docgen", title="Docgen-Lauf",
                                   total=<anzahl nodes>)
    task_hub.log(task_id, "…")            # pro Mandant/Node eine Zeile
    await task_hub.progress(task_id, done=…)
    await task_hub.finish(task_id, "ok"|"error", summary=…)

task_hub.is_cancelled(task_id) im Node-Loop pruefen -> Status "cancelled",
kein Push.

Geloggt werden MUSS pro Lauf:
  - Ausloeser (Scheduler | User <name>)
  - Anzahl Mandanten / Nodes / Guests
  - pro Node: geschrieben / uebersprungen
  - Commit-Hash + Anzahl geaenderter/geloeschter Dateien, oder "no changes"
  - Fehler im Klartext, Token IMMER redacted

AuditLog-Eintrag am Ende jedes Laufs:
  action="docgen.run", resource=<repo_url ohne Token>,
  details={"trigger":…, "nodes":…, "guests":…, "commit":…, "status":…}
Ebenso action="docgen.config_changed" bei jedem PATCH (details = geaenderte
Keys, NIEMALS der Token-Wert selbst) und "docgen.node_toggled" beim
Node-Umschalten.

--- 7. FRONTEND ---
a) NodeSettingsModal.svelte: Checkbox "Doku-Generierung fuer diese Node"
   (Muster: vpn_transport-Block, ~Zeile 84). Hinweistext darunter:
   "Beim Deaktivieren wird die Doku dieser Node beim naechsten Lauf aus dem
   Ziel-Repo entfernt (Git-Historie bleibt)."
b) Neuer Settings-Tab "Dokumentation" analog TenantsTab.svelte:
   - Master-Toggle enabled (der Kill-Switch)
   - Repo-URL / User / Token (Token als password-Feld, Placeholder
     "•••• gesetzt" wenn git_token_set)
   - Branch, Intervall (Stunden), Startstunde
   - Liste der Nodes mit ihrem docgen_enabled-Status (read-only, Link ins
     Node-Modal)
   - Status-Block: letzter Lauf, Ergebnis, Commit, naechster Lauf
   - Button "Jetzt ausfuehren" -> POST /api/docgen/run, danach Task-Panel oeffnen
   Svelte 5 Runes ($state/$derived), keine Stores neu erfinden.

--- 8. TESTS ---
server/tests/test_docgen_config.py:
  - _cfg() Fallback settings -> env
  - GET /config liefert keinen Token, aber git_token_set
  - PATCH mit leerem Token laesst den alten stehen
  - keine Node aktiviert -> run_docgen() pusht nicht
  - Intervall-Logik: laeuft nicht vor Ablauf von interval_hours

.env.example: DOCGEN_*-Block als "Fallback/Initialwerte, ab #<ISSUE> in den
Einstellungen pflegbar" kommentieren, nicht entfernen.

Definition of Done

  • Migration 0028 sauber up/down
  • Docgen läuft nur über explizit aktivierte Nodes
  • Konfiguration vollständig im UI pflegbar, Token nie im GET
  • Master-Toggle wirkt sofort ohne Container-Restart
  • Jeder Lauf erscheint im Task-Panel mit vollem Log und ist abbrechbar
  • last_run überlebt einen Container-Neustart
  • Audit-Einträge für Lauf, Config-Änderung und Node-Toggle
Der Docgen (#17) ist heute **global** und ausschließlich über `.env` (`DOCGEN_*`) konfiguriert. Damit lässt sich nichts im UI schalten und keine Node gezielt ein-/ausnehmen. Dieses Issue macht Docgen pro Node aktivierbar, verlagert die Konfiguration in die DB und hängt jeden Lauf an Task-Center (#155) + Audit-Log. ## Warum - **Pro-Node-Opt-in:** Nicht jede Node soll dokumentiert werden (Test-Nodes, fremde Umgebungen, Mandanten ohne Doku-Vertrag). - **Kill-Switch im UI:** `.env` ändern + Container-Restart ist kein Kill-Switch. - **Nachvollziehbarkeit:** Ein Prozess, der Infrastruktur-Daten in ein externes Git-Repo schiebt, muss lückenlos protokolliert sein — wer hat ihn ausgelöst, was wurde gepusht, welcher Commit. ## Bestand, den wir nutzen - `models/settings.py` → `Setting` (key/value JSONB) existiert und wird bereits von `services/notifications.py` und `settings/settings_router.py` genutzt. - `services/task_hub.py` → `task_hub.start/log/progress/finish/is_cancelled` — fertiges Muster, siehe `admin_router.py:597` (QEMU-Scan). - `models/task.py` → `TASK_KINDS` muss nur erweitert werden. - `models/audit_log.py` → `AuditLog(action, resource, details, user_id, ...)`. - Frontend-Muster für einen Node-Toggle: `NodeSettingsModal.svelte`, Zeilen um `vpn_transport` (#189) — Checkbox + Änderungs-Flag + PATCH. --- ## Prompt für Claude Code ``` Mache den Docgen in theProx pro Node aktivierbar, verlagere seine Konfiguration von .env in die settings-Tabelle und protokolliere jeden Lauf ueber Task-Hub (#155) + Audit-Log. --- 1. MIGRATION 0028 --- server/migrations/versions/0028_node_docgen.py (revision "0028_node_docgen", down_revision "0027_auto_update_include_host", Format exakt wie 0027) op.add_column("nodes", sa.Column("docgen_enabled", sa.Boolean(), server_default="false", nullable=False)) server/models/node.py: Spalte ergaenzen UND in to_dict() ausgeben ("docgen_enabled": bool(self.docgen_enabled)). --- 2. KONFIG: .env -> settings-Tabelle --- Neuer Settings-Key "docgen" (JSONB), Shape: { "enabled": false, "repo_url": "", "git_user": "", "git_token": "", # write-only, s.u. "branch": "main", "interval_hours": 24, # NEU: ersetzt die feste Stunde "hour": 3, # Startstunde des Fensters "author_name": "theProx Docgen", "author_email": "docgen@example.com", "workdir": "/tmp/theprox-docgen" } _cfg() in services/docgen.py umbauen: liest den Settings-Key aus der DB, faellt pro Feld auf den bisherigen os.getenv(DOCGEN_*)-Wert zurueck. So bleiben bestehende .env-Installationen ohne Migration lauffaehig. _cfg() wird damit async — alle Aufrufer anpassen. TOKEN-HANDLING (wichtig): - git_token wird NIE im GET zurueckgegeben. Stattdessen "git_token_set": bool. - Beim PATCH: leerer/fehlender git_token laesst den gespeicherten Wert unveraendert; ein gesetzter Wert ueberschreibt ihn. - _redact() beim Loggen bleibt wie es ist und muss weiter greifen. --- 3. NODE-FILTER --- In _fetch(): nur Nodes mit docgen_enabled == True laden. Wenn KEINE Node aktiviert ist, bricht run_docgen() frueh ab mit {"status":"skipped","reason":"keine Node fuer Docgen aktiviert"} und pusht NICHTS (kein leeres Repo pushen, sonst loescht der naechste Lauf die Doku). WICHTIG — Loeschverhalten: _build_and_push() wischt heute mandanten/ komplett und baut neu. Wenn eine Node deaktiviert wird, verschwindet ihre Doku damit still aus dem Repo. Das ist gewollt, muss aber sichtbar sein: zaehle vor dem Commit die geloeschten Pfade (git status --porcelain, Zeilen mit "D ") und schreibe sie in die Task-Summary + Commit-Message ("docgen: Infra-Snapshot <stamp> (+N/-M Dateien)"). --- 4. API --- server/routers/docgen_router.py erweitern (alle require_superadmin): GET /api/docgen/config -> Config ohne Token, mit git_token_set: bool PATCH /api/docgen/config -> Teil-Update (Pydantic-Modell mit Optional-Feldern) POST /api/docgen/run -> bleibt (force=True), gibt jetzt task_id zurueck GET /api/docgen/status -> {enabled, last_run_at, last_status, last_commit, nodes_enabled: int, next_run_at} last_* in einem zweiten Settings-Key "docgen.state" persistieren (ueberlebt Neustarts — heute liegt _LAST_RUN_DATE nur im Prozessspeicher, d.h. nach jedem Container-Restart laeuft Docgen in derselben Stunde erneut). PATCH auf einen Node (admin_router.py, NodeUpdate um ~Zeile 70 / Apply um ~287, Muster: vpn_transport) um docgen_enabled: Optional[bool] erweitern. --- 5. INTERVALL STATT FIXER STUNDE --- maybe_run_docgen() umbauen: statt "nur wenn now.hour == cfg.hour und heute noch nicht gelaufen" jetzt "wenn last_run_at aelter als interval_hours ist, und die aktuelle Stunde >= cfg.hour (bei interval_hours >= 24)". Bei interval_hours < 24 zaehlt nur der Abstand. Das Slot-Claiming (erst Zeit setzen, dann laufen) beibehalten, sonst laufen zwei Ticks parallel. --- 6. TASK-HUB + AUDIT --- models/task.py: TASK_KINDS um "docgen" erweitern. run_docgen() umbauen auf das task_hub-Muster (Vorlage: admin_router.py:597): task_id = await task_hub.start(kind="docgen", title="Docgen-Lauf", total=<anzahl nodes>) task_hub.log(task_id, "…") # pro Mandant/Node eine Zeile await task_hub.progress(task_id, done=…) await task_hub.finish(task_id, "ok"|"error", summary=…) task_hub.is_cancelled(task_id) im Node-Loop pruefen -> Status "cancelled", kein Push. Geloggt werden MUSS pro Lauf: - Ausloeser (Scheduler | User <name>) - Anzahl Mandanten / Nodes / Guests - pro Node: geschrieben / uebersprungen - Commit-Hash + Anzahl geaenderter/geloeschter Dateien, oder "no changes" - Fehler im Klartext, Token IMMER redacted AuditLog-Eintrag am Ende jedes Laufs: action="docgen.run", resource=<repo_url ohne Token>, details={"trigger":…, "nodes":…, "guests":…, "commit":…, "status":…} Ebenso action="docgen.config_changed" bei jedem PATCH (details = geaenderte Keys, NIEMALS der Token-Wert selbst) und "docgen.node_toggled" beim Node-Umschalten. --- 7. FRONTEND --- a) NodeSettingsModal.svelte: Checkbox "Doku-Generierung fuer diese Node" (Muster: vpn_transport-Block, ~Zeile 84). Hinweistext darunter: "Beim Deaktivieren wird die Doku dieser Node beim naechsten Lauf aus dem Ziel-Repo entfernt (Git-Historie bleibt)." b) Neuer Settings-Tab "Dokumentation" analog TenantsTab.svelte: - Master-Toggle enabled (der Kill-Switch) - Repo-URL / User / Token (Token als password-Feld, Placeholder "•••• gesetzt" wenn git_token_set) - Branch, Intervall (Stunden), Startstunde - Liste der Nodes mit ihrem docgen_enabled-Status (read-only, Link ins Node-Modal) - Status-Block: letzter Lauf, Ergebnis, Commit, naechster Lauf - Button "Jetzt ausfuehren" -> POST /api/docgen/run, danach Task-Panel oeffnen Svelte 5 Runes ($state/$derived), keine Stores neu erfinden. --- 8. TESTS --- server/tests/test_docgen_config.py: - _cfg() Fallback settings -> env - GET /config liefert keinen Token, aber git_token_set - PATCH mit leerem Token laesst den alten stehen - keine Node aktiviert -> run_docgen() pusht nicht - Intervall-Logik: laeuft nicht vor Ablauf von interval_hours .env.example: DOCGEN_*-Block als "Fallback/Initialwerte, ab #<ISSUE> in den Einstellungen pflegbar" kommentieren, nicht entfernen. ``` ## Definition of Done - [ ] Migration 0028 sauber up/down - [ ] Docgen läuft nur über explizit aktivierte Nodes - [ ] Konfiguration vollständig im UI pflegbar, Token nie im GET - [ ] Master-Toggle wirkt sofort ohne Container-Restart - [ ] Jeder Lauf erscheint im Task-Panel mit vollem Log und ist abbrechbar - [ ] `last_run` überlebt einen Container-Neustart - [ ] Audit-Einträge für Lauf, Config-Änderung und Node-Toggle
Author
Owner

Teil 2/3. Setzt #209 nicht zwingend voraus, kann parallel laufen. Danach: #211.

Teil 2/3. Setzt #209 nicht zwingend voraus, kann parallel laufen. Danach: #211.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
chinux/theProx#210
No description provided.