Add trust features: history, trash, export, and collection sharing
release gate / check (push) Failing after 6m36s

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.
This commit is contained in:
Andrew S Erwin
2026-10-19 23:07:38 -05:00
parent 9db69efe4b
commit 5eab58f99c
22 changed files with 1496 additions and 76 deletions
+45
View File
@@ -236,6 +236,22 @@ CREATE TABLE "PageLink" (
);
CREATE INDEX "PageLink_targetPageId_idx" ON "PageLink"("targetPageId");
-- 2.13c PageRevision: server-side content history. A snapshot is recorded for
-- every committed content version (including restore, which is a new version)
-- so an old draft can be inspected and restored without ever rewriting the
-- past. Revision rows cascade with their page.
CREATE TABLE "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 "PageRevision_pageId_version_idx" ON "PageRevision"("pageId", version DESC);
-- 2.14 SignalSource
CREATE TABLE "SignalSource" (
id text PRIMARY KEY,
@@ -294,6 +310,22 @@ CREATE TABLE "AppSetting" (
"updatedAt" timestamptz NOT NULL DEFAULT now()
);
-- 2.16b Trash: recoverable deletions (trust phase). The row is a private
-- per-owner snapshot taken in the same transaction as the hard delete. It
-- keeps intrinsic data and owner-scoped relationships, but deliberately not
-- access grants (bookmark shares, public flags, collection shares): restoring
-- an item never resurrects a grant that was revoked while it sat in the bin.
CREATE TABLE "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 "Trash_ownerId_deletedAt_idx" ON "Trash"("ownerId", "deletedAt" DESC);
-- 2.16 SharedDate
CREATE TABLE "SharedDate" (
id text PRIMARY KEY,
@@ -325,6 +357,19 @@ CREATE TABLE "Collection" (
);
CREATE INDEX "Collection_ownerId_idx" ON "Collection"("ownerId");
-- 2.17b CollectionShare: explicit, revocable read access to a collection.
-- Recipients get the narrowed bookmark projection for member bookmarks (via
-- BookmarkVisible); sharing therefore widens access only by the owner's
-- deliberate action, is disclosed in the share UI, and revocation removes
-- access immediately. Mutations remain owner-only.
CREATE TABLE "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 "CollectionShare_userId_idx" ON "CollectionShare"("userId");
CREATE TABLE "CollectionProperty" (
id text PRIMARY KEY,
"collectionId" text NOT NULL REFERENCES "Collection"(id) ON DELETE CASCADE,