dorfteich/apps/api/prisma/migrations/20260705200714_pages/migration.sql
Claude Sonnet 5 98e159ab50
All checks were successful
CD / Build and push images (push) Successful in 1m52s
CI / Lint, typecheck, test (push) Successful in 1m34s
CI / Auth e2e pack (push) Successful in 1m44s
CI / Build container images (push) Has been skipped
CD / Deploy to Test (push) Successful in 8s
CD / Smoke tests against Test (push) Successful in 1m7s
CD / Promote to Int (push) Successful in 10s
Add page CRUD and Yjs state persistence (#23)
Prisma models `pages`/`page_updates`/`page_content_cache` per
data-model.md. Endpoints: POST /ponds/:id/pages (title -> empty Yjs doc
state, seeded via y-prosemirror), GET /pages/:id (meta + base64 state),
PUT /pages/:id/state (client-encoded Yjs state, rejected above the 5 MiB
operations.md limit or if it doesn't decode into a valid document for
the schema), PATCH /pages/:id (title/slug — explicit slug changes
validate uniqueness per pond, title-only renames keep the slug),
DELETE (soft). Access follows InterimAccessService via the page's pond,
same 404-not-403 interim rule as ponds.

State saves decode the Yjs update with yjs + y-prosemirror and run it
through the #24 shared derivation functions (docToPlainText/
docToMarkdown/docToHtml/extractOutline) to refresh page_content_cache.
The Yjs XmlFragment name ("default") and the derivation call are
factored so the collab server's persistence hooks (#35) can reuse both.

Raised the API's JSON body limit to 8 MiB (main.ts and the e2e test
app) to fit base64-encoded page state.

Closes #23
2026-07-05 22:25:41 +02:00

60 lines
1.9 KiB
SQL

-- CreateTable
CREATE TABLE "pages" (
"id" TEXT NOT NULL,
"pond_id" TEXT NOT NULL,
"title" TEXT NOT NULL,
"slug" TEXT NOT NULL,
"ydoc_state" BYTEA NOT NULL,
"sort_key" TEXT NOT NULL,
"created_by" TEXT NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,
"deleted_at" TIMESTAMP(3),
"deleted_by" TEXT,
CONSTRAINT "pages_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "page_updates" (
"id" TEXT NOT NULL,
"page_id" TEXT NOT NULL,
"seq" INTEGER NOT NULL,
"update" BYTEA NOT NULL,
CONSTRAINT "page_updates_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "page_content_cache" (
"page_id" TEXT NOT NULL,
"plain_text" TEXT NOT NULL,
"markdown" TEXT NOT NULL,
"html" TEXT NOT NULL,
"outline" JSONB NOT NULL,
"updated_at" TIMESTAMP(3) NOT NULL,
CONSTRAINT "page_content_cache_pkey" PRIMARY KEY ("page_id")
);
-- CreateIndex
CREATE INDEX "pages_pond_id_idx" ON "pages"("pond_id");
-- CreateIndex
CREATE UNIQUE INDEX "pages_pond_id_slug_key" ON "pages"("pond_id", "slug");
-- CreateIndex
CREATE UNIQUE INDEX "page_updates_page_id_seq_key" ON "page_updates"("page_id", "seq");
-- AddForeignKey
ALTER TABLE "pages" ADD CONSTRAINT "pages_pond_id_fkey" FOREIGN KEY ("pond_id") REFERENCES "ponds"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "pages" ADD CONSTRAINT "pages_created_by_fkey" FOREIGN KEY ("created_by") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "page_updates" ADD CONSTRAINT "page_updates_page_id_fkey" FOREIGN KEY ("page_id") REFERENCES "pages"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "page_content_cache" ADD CONSTRAINT "page_content_cache_page_id_fkey" FOREIGN KEY ("page_id") REFERENCES "pages"("id") ON DELETE CASCADE ON UPDATE CASCADE;