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>
This commit is contained in:
HoneyandClaude Opus 5 committed 2026-08-24 09:25:36 +02:00
commit 31a027c93a
225 files changed
+38042

No files matched your search

@@ -0,0 +1,35 @@
-- Gruppenordner: ein Ordner, der keinem einzelnen Konto gehört, sondern einer
-- Gruppe. Wer Mitglied ist, sieht ihn; wer die Gruppe verlässt, nicht mehr.
--
-- Ordner und Dateien gehören ab jetzt entweder einem Nutzer ODER einer Gruppe,
-- nie beidem und nie keinem. Deshalb wird owner_id nullbar und bekommt eine
-- CHECK-Bedingung als Gegengewicht – ohne die könnte ein Datensatz herrenlos
-- werden und wäre über keine Abfrage mehr erreichbar.
ALTER TABLE folders ALTER COLUMN owner_id DROP NOT NULL;
ALTER TABLE files ALTER COLUMN owner_id DROP NOT NULL;
ALTER TABLE folders ADD COLUMN group_id UUID REFERENCES groups(id) ON DELETE CASCADE;
ALTER TABLE files ADD COLUMN group_id UUID REFERENCES groups(id) ON DELETE CASCADE;
ALTER TABLE folders ADD CONSTRAINT folders_one_principal
CHECK (num_nonnulls(owner_id, group_id) = 1);
ALTER TABLE files ADD CONSTRAINT files_one_principal
CHECK (num_nonnulls(owner_id, group_id) = 1);
-- Gruppen bekommen ein eigenes Kontingent. Die Alternative – „zählt beim
-- Ersteller" – wäre für die Mitglieder nicht nachvollziehbar: Der Ordner wäre
-- plötzlich voll, weil jemand anders privat etwas hochgeladen hat.
ALTER TABLE groups ADD COLUMN quota_bytes BIGINT NOT NULL DEFAULT 10737418240;
-- Die bestehende UNIQUE (owner_id, parent_id, name) greift für Gruppenordner
-- nicht: In SQL kollidiert NULL nicht mit NULL, zwei Gruppenordner gleichen
-- Namens rutschten also durch.
CREATE UNIQUE INDEX idx_folders_group_name ON folders (group_id, parent_id, name)
WHERE group_id IS NOT NULL AND parent_id IS NOT NULL;
-- Auf oberster Ebene ist parent_id NULL, das braucht einen eigenen Index.
CREATE UNIQUE INDEX idx_folders_group_root_name ON folders (group_id, name)
WHERE group_id IS NOT NULL AND parent_id IS NULL;
CREATE INDEX idx_folders_group ON folders (group_id) WHERE group_id IS NOT NULL;
CREATE INDEX idx_files_group ON files (group_id) WHERE group_id IS NOT NULL;