- 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
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örigeNNN_name.sql-Doku)
Regeln
- Jede neue Migration bekommt eine fortlaufende Nummer (
001,002, ...). - Der Dateiname beschreibt die Änderung kurz (
002_add_reminders.sql). - Der SQL-Inhalt muss exakt dem entsprechen, was
initSchema()(oder das jeweilige Store-Paket) zur Laufzeit ausführt. - Migrationen werden nie verändert oder gelöscht, nur ergänzt.
- 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:
- Header-Kommentar mit Bezug auf die Vorwärts-Migration und der zugehörigen PROJ-/Ticket-Nummer.
- 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. - Datenverlust explizit benennen — was ist danach unwiederbringlich weg,
was ist regenerierbar (z.B. abgeleitete Indizes wie
ocr_words). - WORM/GoBD-Hinweis: klarstellen, dass kein archiviertes File unter
store/und keinretain_untilberührt wird. Down-SQL darf niemals Dokument-Nutzdaten oder Aufbewahrungssperren löschen. - Idempotent formulieren (
DROP ... IF EXISTS) und inBEGIN; ... 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_log002_reminders.sql—reminders(Wiedervorlage)003_documents_unique_hash.sql—UNIQUE INDEX (tenant_id, content_hash)aufdocuments(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) plusdocuments.doc_type_id/correspondent_id/barcode_values-ALTER006_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) plusdocument_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 inclassification_templates_title.go, angewendet inApplyTemplatefür manuellen Endpoint UND Workflow-Trigger; setzt Titel nur wenntitle_manually_set=falseund lässt das Flag false)022_retention_rules.sql—retention_rules(GoBD-Aufbewahrungsregeln / "Disposition Schedules" pro Dokumenttyp bzw. tenant-weiter Default mitdoc_type_id IS NULL): trigger_type (document_date/upload_date/fixed_date/event) + retention_years/days berechnendocuments.retain_untilüber den Batch-Jobarchivdms retention apply(initRetentionRulesSchema/ApplyRetentionRules). Setzt nurretain_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_idFKON DELETE CASCADE, keintenant_id(immer überdocument_idmediiert),ReplaceOCRWordslöscht+re-inserted atomar bei jedem (Re-)OCR-Lauf (Upload-Job,/reprocess-Endpoint,documents reprocess-allCLI), damit kein Duplikat-Anhäufen entsteht025_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 viaPUT /api/documents/{id}/document-date) der bereits vorhandenendocument_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) plusdocument_tags.assigned_via/documents.doc_type_assigned_via/documents.correspondent_assigned_via-ALTER (Provenienz:manual/rule/ml_accepted, Defaultmanualzum Schutz bestehender Trainingsdaten)