Page data model and CRUD API with REST-persisted Yjs state #23

Closed
opened 2026-07-04 14:52:08 +02:00 by fable-5 · 1 comment
Collaborator

Context

Pages carry a Yjs document from day one (ADR 0003) so M3 can switch transport without data migration — but in M2 the state is saved via REST.

Scope

Prisma models pages, page_updates, page_content_cache per data-model.md. Endpoints: POST /ponds/:id/pages (title → empty Yjs doc state), GET /pages/:id (meta + base64 state), PUT /pages/:id/state (client-encoded Yjs state, size-limited), PATCH /pages/:id (title/slug), DELETE (soft). On state save, derive and store plain_text/markdown/html/outline in page_content_cache using the shared schema (#24) — implement the derivation in packages/shared so the collab server reuses it in M3.

Acceptance criteria

  • create/read/update/delete pages works via API (e2e)
  • state saves are rejected beyond the document size limit (operations.md) with a localized error
  • content cache updates on every state save (verify markdown/plain extraction for a fixture doc)
  • title changes keep the slug stable; explicit slug change validates uniqueness per pond

Technical notes

  • ADR 0003/0004, data-model.md §Content.
  • The derivation function signature must not assume HTTP context (reused by collab in #35).

Dependencies

Depends on #21.

Size: ~2 days


Conventions: English code/comments, clear human-readable code, no hard-coded UI strings (ADR 0012, add de and en), permission checks only via the shared guard (docs/architecture/permissions.md). Read the referenced ADRs before starting.

## Context Pages carry a Yjs document from day one (ADR 0003) so M3 can switch transport without data migration — but in M2 the state is saved via REST. ## Scope Prisma models `pages`, `page_updates`, `page_content_cache` per data-model.md. Endpoints: `POST /ponds/:id/pages` (title → empty Yjs doc state), `GET /pages/:id` (meta + base64 state), `PUT /pages/:id/state` (client-encoded Yjs state, size-limited), `PATCH /pages/:id` (title/slug), `DELETE` (soft). On state save, derive and store `plain_text`/`markdown`/`html`/`outline` in `page_content_cache` using the shared schema (#24) — implement the derivation in `packages/shared` so the collab server reuses it in M3. ## Acceptance criteria - [ ] create/read/update/delete pages works via API (e2e) - [ ] state saves are rejected beyond the document size limit (operations.md) with a localized error - [ ] content cache updates on every state save (verify markdown/plain extraction for a fixture doc) - [ ] title changes keep the slug stable; explicit slug change validates uniqueness per pond ## Technical notes - ADR 0003/0004, data-model.md §Content. - The derivation function signature must not assume HTTP context (reused by collab in #35). ## Dependencies Depends on #21. **Size**: ~2 days --- *Conventions: English code/comments, clear human-readable code, no hard-coded UI strings (ADR 0012, add `de` **and** `en`), permission checks only via the shared guard (docs/architecture/permissions.md). Read the referenced ADRs before starting.*
fable-5 added this to the M2 — Ponds & pages, single-user editing milestone 2026-07-04 14:52:08 +02:00
fable-5 added the
backend
label 2026-07-04 14:52:08 +02:00
Collaborator

Implemented in 98e159a — full pipeline green (CI lint/typecheck/test, auth e2e, CD build/deploy/smoke/promote): https://gitea.101010.cloud/stwaidele/dorfteich/commit/98e159a

8 new e2e tests cover create (empty Yjs state, title-derived slug + duplicate-title suffixes), content-cache derivation on state save (verified against a real Yjs-encoded fixture doc), the 5 MiB size limit (413 page_document_too_large), invalid Yjs bytes (400 invalid_page_state), title-only rename keeping the slug stable, explicit slug change with uniqueness validation (409 slug_taken), soft delete, and pond-scoped 404s for outsiders.

Note: this issue's scope needed #24 (editor schema in packages/shared) as a real prerequisite for the content-cache derivation, even though only #21 was listed under Dependencies — implemented #24 first in this session, closed separately.

Implemented in 98e159a — full pipeline green (CI lint/typecheck/test, auth e2e, CD build/deploy/smoke/promote): https://gitea.101010.cloud/stwaidele/dorfteich/commit/98e159a 8 new e2e tests cover create (empty Yjs state, title-derived slug + duplicate-title suffixes), content-cache derivation on state save (verified against a real Yjs-encoded fixture doc), the 5 MiB size limit (413 `page_document_too_large`), invalid Yjs bytes (400 `invalid_page_state`), title-only rename keeping the slug stable, explicit slug change with uniqueness validation (409 `slug_taken`), soft delete, and pond-scoped 404s for outsiders. Note: this issue's scope needed #24 (editor schema in packages/shared) as a real prerequisite for the content-cache derivation, even though only #21 was listed under Dependencies — implemented #24 first in this session, closed separately.
Sign in to join this conversation.
No project
No Assignees
2 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: stwaidele/dorfteich#23
No description provided.