Files
Aether/backend/db/migrations/000017_correspondents.up.sql
HoneyandClaude Opus 5 31a027c93a feat(aether): initial commit – self-hosted Cloud, Phasen 1–7
Go-Backend (chi, pgx, golang-migrate) + React/TS-Frontend als PWA.
Enthalten: Auth inkl. OIDC, Dateien mit Versionen und Papierkorb,
Freigaben (Links + intern), WebDAV, Gruppenordner, Admin/Theming,
Volltextsuche mit OCR, eigener PDF-Betrachter und WOPI-Office
(Collabora/OnlyOffice).

Nicht im Repo: deploy/.env (Sitzungsschlüssel, DB- und Admin-Passwort)
sowie alle Daten- und Build-Verzeichnisse – siehe .gitignore.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 09:25:36 +02:00

53 lines
2.7 KiB
SQL
Raw Permalink 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.
-- Absender („Korrespondent") an Dateien.
--
-- Warum neben Etiketten noch ein Feld: Es sind **zwei verschiedene Fragen**.
-- Ein Etikett sagt, *wofür* ein Dokument gebraucht wird („absetzbar",
-- „garantie") – davon trägt eine Datei beliebig viele. Der Absender sagt, *von
-- wem* es kommt („Stadtwerke", „Finanzamt") – davon hat eine Datei genau einen.
--
-- Als Etikett gelöst stünden beide in derselben Liste: „Stadtwerke" neben
-- „absetzbar", Absender und Verwendungszweck durcheinander. Bei zwanzig
-- Etiketten fällt das nicht auf, bei achtzig ist die Liste unbenutzbar. Und
-- „alle Rechnungen von X" ließe sich nicht von „alles Absetzbare" trennen –
-- genau die Verknüpfung, für die es gebaut wird.
--
-- Die Eins-Regel ist dabei kein Mangel, sondern der Zweck: Sie erzwingt eine
-- Entscheidung und hält die Liste kurz.
CREATE TABLE correspondents (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Prinzipal-Modell wie bei tags: entweder ein Konto oder eine Gruppe.
-- In einem Gruppenordner müssen alle dieselben Absender sehen, sonst
-- sortiert jeder dieselben Dokumente für sich neu ein.
owner_id UUID REFERENCES users(id) ON DELETE CASCADE,
group_id UUID REFERENCES groups(id) ON DELETE CASCADE,
name TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT correspondents_one_principal CHECK (
(owner_id IS NOT NULL AND group_id IS NULL) OR
(owner_id IS NULL AND group_id IS NOT NULL)
),
CONSTRAINT correspondents_name_not_empty CHECK (length(btrim(name)) > 0)
);
-- Eindeutig je Prinzipal, ohne Rücksicht auf Groß-/Kleinschreibung – dieselbe
-- Regel wie bei Etiketten. Hier wiegt sie schwerer: „Amazon" und „amazon"
-- nebeneinander hieße, dass „alle Rechnungen von Amazon" nur die Hälfte
-- findet, und zwar unauffällig.
CREATE UNIQUE INDEX idx_correspondents_owner_name
ON correspondents (owner_id, lower(btrim(name))) WHERE owner_id IS NOT NULL;
CREATE UNIQUE INDEX idx_correspondents_group_name
ON correspondents (group_id, lower(btrim(name))) WHERE group_id IS NOT NULL;
-- Am Datei-Datensatz, nicht in einer Verknüpfungstabelle: Genau einer je
-- Datei – das steht damit im Schema und nicht bloß in einer Zusicherung des
-- Codes.
--
-- ON DELETE SET NULL statt CASCADE: Wer einen Absender löscht, will ihn aus
-- der Liste haben – nicht seine Rechnungen.
ALTER TABLE files ADD COLUMN correspondent_id UUID REFERENCES correspondents(id) ON DELETE SET NULL;
-- „Alles von Stadtwerke" ist die Abfrage, für die das Feld gebaut wird.
CREATE INDEX idx_files_correspondent ON files (correspondent_id) WHERE correspondent_id IS NOT NULL;