- 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
82 lines
6.8 KiB
Markdown
82 lines
6.8 KiB
Markdown
# 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 (001–023) 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)
|