Add connected pages: hierarchy, backlinks, and live embeds
release gate / check (push) Failing after 7m7s
release gate / check (push) Failing after 7m7s
Implement Notion-like upgrade phase 3: owner-scoped nested pages with cycle rejection and child promotion on delete; Markdown-derived internal links and backlinks; live collection embeds resolved in the content-save transaction. Add migration 008, API/docs, integration coverage, and page/sidebar UI.
This commit is contained in:
@@ -196,6 +196,10 @@ CREATE TABLE "Page" (
|
||||
icon text,
|
||||
pinned boolean NOT NULL DEFAULT false,
|
||||
position int NOT NULL DEFAULT 0,
|
||||
-- Connected pages (phase 3): a page may live under another page. Deleting
|
||||
-- the parent promotes its children to the top level instead of deleting
|
||||
-- them; cycle rejection is enforced by the API.
|
||||
"parentId" text REFERENCES "Page"(id) ON DELETE SET NULL,
|
||||
-- Optimistic concurrency revision (review DATA-01): content saves bump it;
|
||||
-- a stale base version in PATCH is rejected with 409.
|
||||
version integer NOT NULL DEFAULT 1,
|
||||
@@ -204,6 +208,7 @@ CREATE TABLE "Page" (
|
||||
"updatedAt" timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX "Page_ownerId_position_idx" ON "Page"("ownerId", position);
|
||||
CREATE INDEX "Page_parentId_idx" ON "Page"("parentId");
|
||||
-- Index-backed substring search over the two searchable fields.
|
||||
CREATE INDEX "Page_title_trgm_idx" ON "Page" USING gin (title gin_trgm_ops);
|
||||
CREATE INDEX "Page_content_trgm_idx" ON "Page" USING gin (content gin_trgm_ops);
|
||||
@@ -218,6 +223,19 @@ CREATE TABLE "PageBookmark" (
|
||||
);
|
||||
CREATE INDEX "PageBookmark_bookmarkId_idx" ON "PageBookmark"("bookmarkId");
|
||||
|
||||
-- 2.13b PageLink: resolved internal page links, rebuilt from page content on
|
||||
-- every content save. Targets are only stored when the author owns the target
|
||||
-- page, so a backlink can never reveal an inaccessible title. Self-links are
|
||||
-- ignored by the writer and refused here as defense in depth.
|
||||
CREATE TABLE "PageLink" (
|
||||
"sourcePageId" text NOT NULL REFERENCES "Page"(id) ON DELETE CASCADE,
|
||||
"targetPageId" text NOT NULL REFERENCES "Page"(id) ON DELETE CASCADE,
|
||||
"createdAt" timestamptz NOT NULL DEFAULT now(),
|
||||
PRIMARY KEY ("sourcePageId", "targetPageId"),
|
||||
CONSTRAINT "PageLink_no_self" CHECK ("sourcePageId" <> "targetPageId")
|
||||
);
|
||||
CREATE INDEX "PageLink_targetPageId_idx" ON "PageLink"("targetPageId");
|
||||
|
||||
-- 2.14 SignalSource
|
||||
CREATE TABLE "SignalSource" (
|
||||
id text PRIMARY KEY,
|
||||
@@ -348,4 +366,21 @@ CREATE TABLE "CollectionView" (
|
||||
"updatedAt" timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX "CollectionView_collectionId_idx" ON "CollectionView"("collectionId");
|
||||
|
||||
-- 2.18 PageEmbed (Notion-like upgrades phase 3): a live collection view
|
||||
-- embedded in a page. The reference is resolved from the page's Markdown
|
||||
-- (`{{collection:<id>|view=<id>}}`) on every content save. Only collections
|
||||
-- owned by the page's author are stored; embedding displays data and never
|
||||
-- publishes it or widens bookmark access. Deleting the collection or the
|
||||
-- chosen view removes/falls back rather than leaving a dangling embed.
|
||||
CREATE TABLE "PageEmbed" (
|
||||
"pageId" text NOT NULL REFERENCES "Page"(id) ON DELETE CASCADE,
|
||||
"collectionId" text NOT NULL REFERENCES "Collection"(id) ON DELETE CASCADE,
|
||||
"viewId" text REFERENCES "CollectionView"(id) ON DELETE SET NULL,
|
||||
position integer NOT NULL DEFAULT 0,
|
||||
"createdAt" timestamptz NOT NULL DEFAULT now(),
|
||||
PRIMARY KEY ("pageId", "collectionId")
|
||||
);
|
||||
CREATE INDEX "PageEmbed_collectionId_idx" ON "PageEmbed"("collectionId");
|
||||
CREATE INDEX "PageEmbed_viewId_idx" ON "PageEmbed"("viewId");
|
||||
COMMIT;
|
||||
|
||||
Reference in New Issue
Block a user