Files
patrick 89de794356
CI / Backend (go vet, go test -cover) (push) Has been cancelled
CI / Frontend (ESLint, tsc, next build) (push) Has been cancelled
FDN-02/FDN-03/FDN-07/FDN-08: Migrations-Rollback, Objekt-Storage-Interface, go.sum-Fix, Observability
- FDN-02: Rollback-fähige Down-Migrationen (024-026), archivdms seed dev CLI
- FDN-03: internal/objectstore Interface + lokaler WORM-Treiber, signierte Download-URLs
- FDN-07: go.mod/go.sum vervollständigt (fehlender go-ldap/v3-Eintrag), CI-Pipeline (.gitea/workflows/ci.yml, bereits in FDN-01 committet) damit lauffähig
- FDN-08: Request-ID-Middleware, /metrics-Endpoint, Panic-Recovery, Login/Logout/Me technisches Logging inkl. Access-Log je Anfrage
2026-08-11 22:27:52 +02:00
..

Migrations-Konvention

archivdms verwendet, wie archivmail, kein externes Migrationstool. Das tatsächliche Schema wird idempotent zur Laufzeit von initSchema()-Funktionen in den jeweiligen Store-Paketen angelegt/erweitert (CREATE TABLE IF NOT EXISTS, ALTER TABLE ... ADD COLUMN IF NOT EXISTS, CREATE INDEX IF NOT EXISTS). Das ist sicher gegen mehrfaches Ausführen und gegen bereits existierende Produktiv-Datenbanken.

Dieses Verzeichnis dokumentiert trotzdem jede Schemaänderung als nummerierte NNN_name.sql-Datei — rein informativ/Doku, nicht ausführbar über ein Migrationstool. Sie dient als:

  • lesbare Historie, welche Änderung wann und warum kam
  • Referenz für DBAs, die das Schema manuell nachvollziehen wollen
  • Grundlage für Code-Review von Schemaänderungen (PR zeigt sowohl den Go-Diff in initSchema() als auch die dazugehörige NNN_name.sql-Doku)

Regeln

  1. Jede neue Migration bekommt eine fortlaufende Nummer (001, 002, ...).
  2. Der Dateiname beschreibt die Änderung kurz (002_add_reminders.sql).
  3. Der SQL-Inhalt muss exakt dem entsprechen, was initSchema() (oder das jeweilige Store-Paket) zur Laufzeit ausführt.
  4. Migrationen werden nie verändert oder gelöscht, nur ergänzt.
  5. Zu jeder neuen Migration gehört eine Down-Datei NNN_name.down.sql (siehe Abschnitt „Rollback-Pfad").

Rollback-Pfad (NNN_name.down.sql)

initSchema() ist ausschließlich vorwärtsgerichtet — es gibt bewusst keinen automatischen Rollback-Runner (kein Migrationstool, kein Zustandstabellen- Tracking). Für den Ernstfall (fehlerhaftes Release, Rückbau eines Features) braucht es trotzdem ein reviewtes Rückbau-SQL. Deshalb gilt ab FDN-02:

Jede neue NNN_name.sql bekommt eine gleichnamige NNN_name.down.sql mit dem exakten Rückbau der Vorwärts-Migration. Separate Datei statt -- DOWN-Abschnitt in derselben Datei, weil Regel 4 („Migrationen werden nie verändert") sonst verletzt würde und weil sich eine Down-Datei fehlerfrei per psql -f einspielen lässt, ohne vorher Abschnitte herauszuschneiden.

Anforderungen an eine Down-Datei:

  1. Header-Kommentar mit Bezug auf die Vorwärts-Migration und der zugehörigen PROJ-/Ticket-Nummer.
  2. Precondition benennen: welche Go-Stelle (initSchema, Store-Datei, aufrufende Queries) vorher entfernt bzw. deployt sein muss. Sonst legt der nächste Prozessstart das Objekt sofort wieder an.
  3. Datenverlust explizit benennen — was ist danach unwiederbringlich weg, was ist regenerierbar (z.B. abgeleitete Indizes wie ocr_words).
  4. WORM/GoBD-Hinweis: klarstellen, dass kein archiviertes File unter store/ und kein retain_until berührt wird. Down-SQL darf niemals Dokument-Nutzdaten oder Aufbewahrungssperren löschen.
  5. Idempotent formulieren (DROP ... IF EXISTS) und in BEGIN; ... COMMIT; klammern.

Ausführung ist immer manuell und bewusst (psql -f internal/storage/migrations/NNN_name.down.sql), nie automatisch beim Start.

Beispiele (rückwirkend ergänzt, dienen als Vorlage): 024_ocr_words.down.sql, 025_document_date_score.down.sql, 026_accounting_api_keys.down.sql. Ältere Migrationen (001023) haben keine Down-Datei — sie beschreiben das etablierte Kernschema, dessen Rückbau kein realistisches Szenario ist.

Vorhandene Migrationen

  • 001_initial.sqltenants, users, token_blacklist, documents, audit_log
  • 002_reminders.sqlreminders (Wiedervorlage)
  • 003_documents_unique_hash.sqlUNIQUE INDEX (tenant_id, content_hash) auf documents (DB-seitiger Duplikatschutz für die Upload-Pipeline)
  • 004_sftp_credentials.sqlsftp_credentials (per-Mandant SFTP-Zugangsdaten für den eingebetteten SFTP-Server, internal/sftpserver)
  • 005_taxonomy.sqltags/document_types/correspondents/document_tags (strukturierte Entitäten mit Matching-Algorithmus + Barcode-Wert) plus documents.doc_type_id/correspondent_id/barcode_values-ALTER
  • 006_custom_fields.sqlcustom_field_defs/document_type_fields/document_field_values (benutzerdefinierte Felder pro Mandant/Dokumenttyp/Dokument)
  • 007_trash.sql — Papierkorb + gestaffeltes Löschkonzept: documents.deleted_at/deleted_by-ALTER (Soft-Delete) plus document_delete_requests (Vier-Augen-Workflow für finales WORM-Löschen, Retention-Prüfung, Tombstone)
  • 011_classification_templates.sqlclassification_templates/classification_template_tags/classification_template_field_defaults (Klassifizierungsvorlagen: benannte Bündel aus Dokumenttyp/Tags/Custom-Field-Defaults/Aufbewahrungsdauer, anwendbar per Preview+Commit; keine persistente Kopplung an documents, Retention nur verlängerbar)
  • 021_title_template.sqlclassification_templates.title_template-ALTER + tenants.default_title_template-ALTER (Titel-Vorlage pro Klassifizierungsvorlage mit tenant-weitem Default-Fallback; Go-text/template-Rendering in classification_templates_title.go, angewendet in ApplyTemplate für manuellen Endpoint UND Workflow-Trigger; setzt Titel nur wenn title_manually_set=false und lässt das Flag false)
  • 022_retention_rules.sqlretention_rules (GoBD-Aufbewahrungsregeln / "Disposition Schedules" pro Dokumenttyp bzw. tenant-weiter Default mit doc_type_id IS NULL): trigger_type (document_date/upload_date/fixed_date/event) + retention_years/days berechnen documents.retain_until über den Batch-Job archivdms retention apply (initRetentionRulesSchema/ApplyRetentionRules). Setzt nur retain_until (WORM-Sperre), löscht nie und verkürzt nie; die Vernichtung läuft weiter über 007_trash.sql. event-Regeln werden nicht auto-berechnet (künftiger Erweiterungspunkt)
  • 024_ocr_words.sqlocr_words (word-level OCR bounding boxes per Dokument, Phase 2 des Text-Highlight/Overlay-Features, internal/storage/ocr_words.go/initOCRWordsSchema): document_id FK ON DELETE CASCADE, kein tenant_id (immer über document_id mediiert), ReplaceOCRWords löscht+re-inserted atomar bei jedem (Re-)OCR-Lauf (Upload-Job, /reprocess-Endpoint, documents reprocess-all CLI), damit kein Duplikat-Anhäufen entsteht
  • 025_document_date_score.sqldocuments.document_date_score (NUMERIC, nullable, kein Backfill): persistiert die Konfidenz (0.4-0.9 automatische Keyword-Proximity-Heuristik, 1.0 = manuell bestätigt/überschrieben via PUT /api/documents/{id}/document-date) der bereits vorhandenen document_date-Erkennung, vorbereitend für die geplante Buchhaltungs-Pull-API (Score >= 0.75 als Pull-Filter)
  • 020_ml_classifier.sqlml_classifier_tokens/ml_classifier_classes/ml_classifier_runs (Naive-Bayes-Retraining-Klassifizierung Phase 1, ergänzt die Regel-Engine) plus document_tags.assigned_via/documents.doc_type_assigned_via/documents.correspondent_assigned_via-ALTER (Provenienz: manual/rule/ml_accepted, Default manual zum Schutz bestehender Trainingsdaten)