Files
brain/db/migrations/009_trust.sql
T
Andrew S Erwin 5eab58f99c
release gate / check (push) Failing after 6m36s
Add trust features: history, trash, export, and collection sharing
Implement Notion-like upgrade phase 4: append-only page revisions with restore-as-a-new-version; recoverable bookmark/page/collection deletions whose restores never resurrect public flags or shares; owner-scoped JSON export; and explicit, revocable read-only collection sharing with owner-only mutations and no transitive third-party bookmark leaks. Add migration 009, API/docs, integration coverage, and page/collections/trash UI.
2026-10-19 23:07:38 -05:00

49 lines
2.1 KiB
SQL

-- 009_trust.sql — Notion-like upgrades phase 4 (trust).
--
-- Server-side page revision history, recoverable deletions, and explicit
-- collection sharing.
--
-- Trust rules:
-- * Every committed page content version is snapshotted. Restoring a
-- revision writes a NEW version (history is append-only) and the restore
-- itself is recorded.
-- * Trash is a per-owner snapshot taken in the same transaction as the
-- delete. It restores intrinsic data and owner-scoped relationships, but
-- never access grants (bookmark public flags, bookmark/tag shares,
-- collection shares). A granted access revoked while an item was in the
-- trash cannot come back with the item.
-- * Collection sharing is read-only and revocable. Recipients see member
-- bookmarks through the narrowed shared projection; mutations stay
-- owner-only. BookmarkVisible gains the explicit collection-share edge.
CREATE TABLE IF NOT EXISTS "PageRevision" (
id text PRIMARY KEY,
"pageId" text NOT NULL REFERENCES "Page"(id) ON DELETE CASCADE,
version integer NOT NULL,
title text NOT NULL,
content text NOT NULL,
icon text,
"createdAt" timestamptz NOT NULL DEFAULT now(),
UNIQUE ("pageId", version)
);
CREATE INDEX IF NOT EXISTS "PageRevision_pageId_version_idx" ON "PageRevision"("pageId", version DESC);
CREATE TABLE IF NOT EXISTS "Trash" (
id text PRIMARY KEY,
"ownerId" text NOT NULL REFERENCES "User"(id) ON DELETE CASCADE,
type text NOT NULL CHECK (type IN ('bookmark','page','collection')),
"originalId" text NOT NULL,
data jsonb NOT NULL,
"deletedAt" timestamptz NOT NULL DEFAULT now(),
UNIQUE (type, "originalId")
);
CREATE INDEX IF NOT EXISTS "Trash_ownerId_deletedAt_idx" ON "Trash"("ownerId", "deletedAt" DESC);
CREATE TABLE IF NOT EXISTS "CollectionShare" (
"collectionId" text NOT NULL REFERENCES "Collection"(id) ON DELETE CASCADE,
"userId" text NOT NULL REFERENCES "User"(id) ON DELETE CASCADE,
"createdAt" timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY ("collectionId", "userId")
);
CREATE INDEX IF NOT EXISTS "CollectionShare_userId_idx" ON "CollectionShare"("userId");