Files
archivdms/internal/storage/migrations/README.md
T
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

82 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.sql``tenants`, `users`, `token_blacklist`, `documents`,
`audit_log`
- `002_reminders.sql``reminders` (Wiedervorlage)
- `003_documents_unique_hash.sql``UNIQUE INDEX (tenant_id, content_hash)` auf `documents` (DB-seitiger Duplikatschutz für die Upload-Pipeline)
- `004_sftp_credentials.sql``sftp_credentials` (per-Mandant SFTP-Zugangsdaten für den eingebetteten SFTP-Server, `internal/sftpserver`)
- `005_taxonomy.sql``tags`/`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.sql``custom_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.sql``classification_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.sql``classification_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.sql``retention_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.sql``ocr_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.sql``documents.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.sql``ml_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)