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:
commit
31a027c93a
225 files changed
+38042
No files matched your search
@@ -0,0 +1,103 @@
|
||||
-- Aether – Initial schema
|
||||
-- Phase 0: users, sessions, folders, files (no email)
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
|
||||
|
||||
-- ── Users ─────────────────────────────────────────────────────────────────────
|
||||
CREATE TABLE users (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
username TEXT NOT NULL UNIQUE,
|
||||
password TEXT NOT NULL, -- Argon2id hash
|
||||
is_admin BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
quota_bytes BIGINT NOT NULL DEFAULT 10737418240, -- 10 GB default
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ── Sessions ──────────────────────────────────────────────────────────────────
|
||||
CREATE TABLE sessions (
|
||||
id TEXT PRIMARY KEY,
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
data BYTEA NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
expires_at TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_sessions_user_id ON sessions(user_id);
|
||||
CREATE INDEX idx_sessions_expires ON sessions(expires_at);
|
||||
|
||||
-- ── Folders ───────────────────────────────────────────────────────────────────
|
||||
CREATE TABLE folders (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
owner_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
parent_id UUID REFERENCES folders(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (owner_id, parent_id, name)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_folders_owner ON folders(owner_id);
|
||||
CREATE INDEX idx_folders_parent ON folders(parent_id);
|
||||
|
||||
-- ── Files ─────────────────────────────────────────────────────────────────────
|
||||
CREATE TABLE files (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
owner_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
folder_id UUID REFERENCES folders(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
mime_type TEXT NOT NULL DEFAULT 'application/octet-stream',
|
||||
size BIGINT NOT NULL DEFAULT 0,
|
||||
checksum TEXT NOT NULL, -- SHA-256 hex
|
||||
storage_key TEXT NOT NULL, -- path within blob storage
|
||||
version INTEGER NOT NULL DEFAULT 1,
|
||||
is_deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
deleted_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_files_owner ON files(owner_id);
|
||||
CREATE INDEX idx_files_folder ON files(folder_id);
|
||||
CREATE INDEX idx_files_deleted ON files(is_deleted);
|
||||
CREATE INDEX idx_files_storage ON files(storage_key);
|
||||
|
||||
-- Partial unique index: no two non-deleted files can share name+folder for the same owner.
|
||||
CREATE UNIQUE INDEX idx_files_unique_name
|
||||
ON files(owner_id, folder_id, name)
|
||||
WHERE NOT is_deleted;
|
||||
|
||||
-- ── File versions ─────────────────────────────────────────────────────────────
|
||||
-- Previous versions of files (kept for Soft-Delete + versioning).
|
||||
CREATE TABLE file_versions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
file_id UUID NOT NULL REFERENCES files(id) ON DELETE CASCADE,
|
||||
version INTEGER NOT NULL,
|
||||
size BIGINT NOT NULL,
|
||||
checksum TEXT NOT NULL,
|
||||
storage_key TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_file_versions_file ON file_versions(file_id);
|
||||
|
||||
-- ── Updated_at trigger ────────────────────────────────────────────────────────
|
||||
CREATE OR REPLACE FUNCTION update_updated_at()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER trg_users_updated_at
|
||||
BEFORE UPDATE ON users
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
||||
|
||||
CREATE TRIGGER trg_folders_updated_at
|
||||
BEFORE UPDATE ON folders
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
||||
|
||||
CREATE TRIGGER trg_files_updated_at
|
||||
BEFORE UPDATE ON files
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
||||
Reference in new issue
Block a user