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>
52 lines
3.1 KiB
SQL
52 lines
3.1 KiB
SQL
-- Interne Freigaben: Ordner oder Datei an einen anderen Nutzer oder eine Gruppe
|
||
-- geben, ohne dass die Bytes umziehen. Der Empfänger sieht sie virtuell unter
|
||
-- „Geteilt mit mir"; auf der Platte bleibt alles beim Eigentümer.
|
||
--
|
||
-- Abweichung vom Projektplan: dort war eine polymorphe Tabelle skizziert
|
||
-- (subject_type + subject_id als Text/UUID ohne Fremdschlüssel). Hier stattdessen
|
||
-- vier nullbare Fremdschlüssel mit CHECK-Bedingung. Grund: polymorph kann die
|
||
-- Datenbank nichts erzwingen – ein gelöschter Nutzer oder Ordner hinterließe
|
||
-- Einträge, die auf nichts mehr zeigen, und aus so einer Leiche später eine
|
||
-- Freigabe für einen neu vergebenen Namen zu machen wäre ein Sicherheitsloch.
|
||
-- Mit echten Fremdschlüsseln räumt ON DELETE CASCADE das von selbst auf.
|
||
CREATE TABLE acl_entries (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
||
-- Empfänger: genau eines von beiden.
|
||
subject_user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
||
subject_group_id UUID REFERENCES groups(id) ON DELETE CASCADE,
|
||
|
||
-- Freigegebene Ressource: genau eines von beiden.
|
||
resource_folder_id UUID REFERENCES folders(id) ON DELETE CASCADE,
|
||
resource_file_id UUID REFERENCES files(id) ON DELETE CASCADE,
|
||
|
||
permission TEXT NOT NULL CHECK (permission IN ('read', 'write')),
|
||
|
||
-- Wer die Freigabe erteilt hat. SET NULL statt CASCADE: die Freigabe soll
|
||
-- nicht verschwinden, nur weil das Konto des Erteilers gelöscht wurde.
|
||
granted_by UUID REFERENCES users(id) ON DELETE SET NULL,
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
|
||
CONSTRAINT acl_one_subject CHECK (num_nonnulls(subject_user_id, subject_group_id) = 1),
|
||
CONSTRAINT acl_one_resource CHECK (num_nonnulls(resource_folder_id, resource_file_id) = 1)
|
||
);
|
||
|
||
-- Zwei Freigaben derselben Ressource an denselben Empfänger wären mehrdeutig:
|
||
-- welches Recht gilt dann? Partielle Unique-Indizes, weil NULL in einem
|
||
-- gewöhnlichen UNIQUE nicht mit sich selbst kollidiert.
|
||
CREATE UNIQUE INDEX idx_acl_user_folder ON acl_entries (subject_user_id, resource_folder_id)
|
||
WHERE subject_user_id IS NOT NULL AND resource_folder_id IS NOT NULL;
|
||
CREATE UNIQUE INDEX idx_acl_user_file ON acl_entries (subject_user_id, resource_file_id)
|
||
WHERE subject_user_id IS NOT NULL AND resource_file_id IS NOT NULL;
|
||
CREATE UNIQUE INDEX idx_acl_group_folder ON acl_entries (subject_group_id, resource_folder_id)
|
||
WHERE subject_group_id IS NOT NULL AND resource_folder_id IS NOT NULL;
|
||
CREATE UNIQUE INDEX idx_acl_group_file ON acl_entries (subject_group_id, resource_file_id)
|
||
WHERE subject_group_id IS NOT NULL AND resource_file_id IS NOT NULL;
|
||
|
||
-- Die Zugriffsprüfung läuft bei jedem Request; sie fragt nach Ressource.
|
||
CREATE INDEX idx_acl_folder ON acl_entries (resource_folder_id) WHERE resource_folder_id IS NOT NULL;
|
||
CREATE INDEX idx_acl_file ON acl_entries (resource_file_id) WHERE resource_file_id IS NOT NULL;
|
||
-- „Geteilt mit mir" fragt nach Empfänger.
|
||
CREATE INDEX idx_acl_subject_user ON acl_entries (subject_user_id) WHERE subject_user_id IS NOT NULL;
|
||
CREATE INDEX idx_acl_subject_group ON acl_entries (subject_group_id) WHERE subject_group_id IS NOT NULL;
|