# archivdms – Dev Log ## 2026-08-11 – FDN-07: CI-Pipeline & Testharness (Gitea Actions) **Zeit:** ca. 0,7 h (Ticket lesen, Makefile/go.mod/package.json/.eslintrc.json sichten, Workflow-Datei schreiben, README-Doku, Verifikation auf 192.168.1.204) **Ziel:** Automatisierte Prüfstrecke für jeden Push, bisher gab es keine CI. `.gitea/workflows/ci.yml` neu angelegt (Gitea-Actions-Syntax, kompatibel zu GitHub Actions): Job `backend-lint-test` (`go vet ./...`, `go test ./... -cover` gegen frischen Postgres-Service-Container, danach `make build` als Artefakt), Job `frontend-lint-test-build` (`npm ci`/`npm install`-Fallback, `npm run lint`, `npx tsc --noEmit`, `make build-web` als Artefakt). Roter Schritt bricht den jeweiligen Job hart ab (kein `continue-on-error`) — blockiert Merge sobald in Gitea Branch-Protection auf diese Status-Checks gesetzt ist. README um Abschnitt "CI-Pipeline (FDN-07)" ergänzt. **Wichtig — Pipeline greift noch nicht:** archivdms hat kein Gitea-Remote, Repo ist nur lokal `git init`-isiert. Die Datei liegt bewusst bereit, wird aber erst nach Push zu einer Gitea-Instanz mit aktiviertem Runner scharf. **Verifikation auf 192.168.1.204 (nur Befehle geprüft, keine Produktivdienste verändert):** - `go vet ./...` läuft durch (Exit 0), meldet aber pro Paket "missing go.sum entry" — **`go.sum` fehlt im Repo komplett** (weder lokal noch auf dem Server vorhanden). Das ist kein CI-Bug, sondern eine bestehende Repo-Lücke: ohne `go.sum` schlägt `go test ./...` in Paketen mit DB-/Crypto-Importen mit `[setup failed]` fehl (bestätigt: `internal/sftpserver`, `internal/storage`, `internal/tenantstore`, `internal/userstore`). Pakete ohne Fremdabhängigkeiten (`barcode`, `dateformat`, `llm`, `matching`, `ocr`, `thumbnail`) liefern `coverage: 0.0%` (keine Tests vorhanden — erwartet, `*_test.go`-Suche ergab 0 Treffer im ganzen Repo). - `npm run lint` auf dem Server bricht mit `next: not found` ab, weil `/opt/archivdms-src` dort produktiv kein `node_modules` vorhält (Build läuft über `update.sh` in einem separaten Verzeichnis) — kein Hinweis auf ein Problem der CI-Konfiguration selbst, in der CI läuft `npm ci` vorher. - **Offen/Folgeaufgabe (nicht Teil von FDN-07):** `go.sum` committen (`go mod tidy` mit funktionierender Toolchain), sonst schlägt der CI-Job `backend-lint-test` beim ersten echten Lauf fehl, sobald das Repo zu Gitea gepusht wird. **Nicht gemacht (Nutzerfreigabe nötig):** kein Commit, kein Branch `feature/fdn-07-ci-pipeline-testharness`, kein Push, kein Merge, kein Deploy — Ticket-Vorgabe "anhalten" nach Dateianlage befolgt. --- ## 2026-08-02 – Bulk-Dokument-Export als ZIP (Backend) **Zeit:** ca. 0,8 h (Filter-Recherche ListDocuments/SearchQuery, Handler, CSV-Schema, Route, Audit-Event, Symbolprüfung) **Ziel:** Mehrere Dokumente in einem Rutsch exportieren — als Vorstufe für den späteren DATEV-Formatter. **Endpunkt:** `POST /api/documents/export` (`s.auth`). Body entweder explizite IDs oder Filter: ```json {"ids": [1,2,3]} {"filter": {"doc_type_id": 4, "correspondent_id": 9, "tag_ids": [7,8], "document_date_from": "2026-01-01", "document_date_to": "2026-03-31", "uploaded_from": "2026-01-01", "uploaded_to": "2026-06-30"}} ``` `ids` hat Vorrang, wenn beides gesetzt ist. Antwort: `application/zip`, `Content-Disposition: attachment; filename="export-bulk-.zip"`. Inhalt: pro Dokument `doc-/` mit Originaldatei + `metadata.json` + `ocr_text.txt` (identische Struktur/Feldnamen wie der Einzel-Export, `documentExportMetadata` wiederverwendet), auf oberster Ebene `index.csv` (Semikolon, UTF-8-BOM für Excel; Spalten `document_id;title;doc_type;correspondent;document_date;tags;uploaded_at` — bewusst gleiche snake_case-Namen wie in `metadata.json`, damit der DATEV-Formatter später aus einem Vokabular mappen kann) und `errors.txt` nur wenn etwas übersprungen wurde. **Filter statt neuer SQL:** `ListDocuments` kennt bislang keine Filterparameter (nur `tenantID` + `aclUserID`), `index.SearchQuery` deckt nur Volltext/Tag/DocType über Manticore ab. Deshalb Filterung in Go auf dem bereits tenant- und ACL-gescopten Ergebnis von `ListDocuments` — kein neuer Query-Pfad, an dem ein `tenant_id`-Filter fehlen könnte. Tag-Filter läuft zuletzt über `ListDocumentTags` je Kandidat. **ACL/Mandantentrennung:** jede ID einzeln — `GetDocument` (`WHERE tenant_id`) plus für Rolle `user` `IsDocumentVisible`; `domain_admin`/`superadmin` überspringen den Per-Dokument-Check. Nicht sichtbare/nicht gefundene/nicht lesbare Dokumente brechen den Request **nicht** ab, sondern landen mit Grund in `errors.txt`. **Grenze:** `maxBulkExportDocuments = 500`. Mehr IDs → 400 mit Klartext; ein Filter, der mehr als 500 trifft, wird ebenfalls mit 400 abgelehnt (statt still zu kürzen — ein unvollständiger Export darf nicht vollständig aussehen). **Audit:** neues Event `document_bulk_export` (`audit.EventDocumentBulkExport`), genau **ein** Eintrag pro Aufruf mit `Detail: "zip_bulk_export: exported= skipped="`, inkl. Fehlschlägen (decode_failed, empty_selection, too_many_ids, filter_too_broad, filter_invalid, list_failed, empty_result, stream_failed). **Dateien:** - `internal/api/document_bulk_export_handlers.go` (neu): `handleBulkExportDocuments`, `writeBulkExportDoc`, `filterBulkExportDocs`, `parseBulkExportDate`, `bulkExportRequest`, `bulkExportFilter`, `bulkExportCSVHeader`, `maxBulkExportDocuments`. - `internal/audit/audit.go`: `EventDocumentBulkExport`. - `internal/api/server.go`: Route registriert (literaler Pfad, kollidiert nicht mit `/api/documents/{id}`). Kein Schema-Change → keine Migration. **Geprüfte Symbole** (kein Go-Toolchain lokal): `Store.GetDocument(ctx,id,tenantID) (*Document,error)`, `Store.ListDocuments(ctx,tenantID,*int64) ([]Document,error)`, `Store.IsDocumentVisible(ctx,id,tenantID,userID) (bool,error)`, `Store.DocumentTaxonomyNames(ctx,id,tenantID) (string,string,error)`, `Store.ListDocumentTags(ctx,docID,tenantID) ([]TaxonomyEntity,error)` (Feld `.ID`/`.Name`), `Store.ListDocumentFieldValues(ctx,docID,tenantID) ([]DocumentFieldValue,error)`, `auth.HasRole`, `userstore.RoleDomainAdmin`, `safeDownloadName(title,ext)`, `documentExportMetadata`/`exportCustomFields`/`exportCreatorName`, `audit.Entry`-Felder, `storage.Document`-Felder (`DocTypeID`, `CorrespondentID`, `DocumentDate`, `DocumentDateScore`, `CreatedAt`, `OCRText`). ## 2026-08-01 – Export-Button in der Dokumentansicht (Frontend) **Zeit:** ca. 0,2 h (Aktionsleisten-Muster prüfen, Button ergänzen, TS-Konsistenz) **Ziel:** Den fertigen Backend-Endpunkt `GET /api/documents/{id}/export` in der UI erreichbar machen. Umsetzung: In `DocumentPreview.tsx` in der Kopf-Aktionsleiste links neben "Neu verarbeiten" ein `