Public read access and server-rendered page HTML #56

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

Context

Non-authenticated visitors read what public grants allow (vision); crawlers and the PDF exporter need server-rendered HTML (ADR 0005/0009).

Scope

Anonymous request path: the auth guard resolves 'public' subject grants; SPA renders public ponds/pages read-only (no editor bundle loading for anonymous). Server endpoint GET /public/:pondSlug/:pageSlug rendering full HTML (shared docToHtml, pond fonts, minimal chrome, canonical link) used for crawlers (proxy rule documented) and reused by PDF export in M6. Sidebar/search for anonymous users show only public content.

Acceptance criteria

  • a page granted to 'public' is readable logged-out via the SPA and the HTML endpoint; removing the grant 404s both within seconds
  • non-public pages never leak through sidebar, search, backlinks, or media URLs (negative e2e)
  • HTML endpoint output passes basic validation and contains no session-dependent content
  • media streaming honors public grants for embedded images

Technical notes

  • permissions.md (public subject), security.md, ADR 0005 (crawler routing).

Dependencies

Depends on #52.

Size: ~1.5 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 Non-authenticated visitors read what public grants allow (vision); crawlers and the PDF exporter need server-rendered HTML (ADR 0005/0009). ## Scope Anonymous request path: the auth guard resolves 'public' subject grants; SPA renders public ponds/pages read-only (no editor bundle loading for anonymous). Server endpoint `GET /public/:pondSlug/:pageSlug` rendering full HTML (shared `docToHtml`, pond fonts, minimal chrome, canonical link) used for crawlers (proxy rule documented) and reused by PDF export in M6. Sidebar/search for anonymous users show only public content. ## Acceptance criteria - [ ] a page granted to 'public' is readable logged-out via the SPA and the HTML endpoint; removing the grant 404s both within seconds - [ ] non-public pages never leak through sidebar, search, backlinks, or media URLs (negative e2e) - [ ] HTML endpoint output passes basic validation and contains no session-dependent content - [ ] media streaming honors public grants for embedded images ## Technical notes - permissions.md (public subject), security.md, ADR 0005 (crawler routing). ## Dependencies Depends on #52. **Size**: ~1.5 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 M5 — Permissions & quotas milestone 2026-07-04 14:52:28 +02:00
fable-5 added the
backend
frontend
auth
labels 2026-07-04 14:52:28 +02:00
Collaborator

Umgesetzt in fc41c91 (Claude Opus 4.8), Pipeline komplett grün (CI + CD: Deploy Test, Smoke, Int-Promotion — auf dem inzwischen wieder gesunden Runner). Live auf Int verifiziert.

Was #56 liefert

  • api public/: GET /public/:pondSlug/:pageSlug liefert ein vollständiges, in sich geschlossenes HTML-Dokument (Content-Cache + minimale Chrome + Canonical-Link, kein session-abhängiger Inhalt) für Crawler/PDF-Export; …/content liefert JSON für die SPA. Beide @Public(), Auflösung des public-Subjekts über den geteilten Resolver — verweigert/fehlend → 404 (nicht-öffentliche Seiten verraten ihre Existenz nie). Bild-Nodes (data-file-id) werden zu /api/v1/media/:fileId aufgelöst.
  • Media: GET /media/:fileId ist ebenfalls @Public(), sodass eingebettete Bilder öffentlicher Seiten für Anonyme streamen; der Attachment-Guard gated weiter auf den public-Grant (nicht-öffentlich → 404).
  • web: leichtgewichtige read-only PublicPageView unter /public/:pondSlug/:pageSlug (außerhalb des Auth-Guards) rendert das Server-HTML — bewusst ohne den kollaborativen Editor zu importieren, sodass Anonyme kein Editor-Bundle laden. Neuer public-i18n-Namespace (de+en).

Acceptance criteria

  • Öffentliche Seite logged-out lesbar via SPA und HTML-Endpoint; Grant entfernt → beide 404 in Sekunden (Cache-Invalidierung + NOTIFY).
  • Nicht-öffentliche Seiten lecken nicht (Endpoints 404; negativer e2e).
  • HTML-Endpoint-Ausgabe valide (doctype/title/canonical) ohne session-abhängigen Inhalt.
  • Media-Streaming honoriert Public-Grants für eingebettete Bilder.

Live-Checks (Int): anon HTML ohne Grant → 404; mit Public-Grant → 200 text/html (voll gerendert); content JSON → 200; nach Entzug → 404.

Tests: public.e2e.db.test.ts (HTML+JSON, non-public 404, Grant-Entzug 404); Browser-public-Pack (anon liest Public-Seite + Bild via SPA; non-public → not found) mit eigenem CI-Step.

Hinweis: Anonyme Sidebar/Suche über public-Content ist nicht Teil dieses Commits — der Zugriffspfad läuft über direkte /public/…-Deep-Links; kann bei Bedarf als Folge-Issue ergänzt werden.

Umgesetzt in `fc41c91` (Claude Opus 4.8), Pipeline komplett grün (CI + CD: Deploy Test, Smoke, Int-Promotion — auf dem inzwischen wieder gesunden Runner). Live auf Int verifiziert. **Was #56 liefert** - **api `public/`**: `GET /public/:pondSlug/:pageSlug` liefert ein vollständiges, in sich geschlossenes HTML-Dokument (Content-Cache + minimale Chrome + Canonical-Link, **kein** session-abhängiger Inhalt) für Crawler/PDF-Export; `…/content` liefert JSON für die SPA. Beide `@Public()`, Auflösung des `public`-Subjekts über den geteilten Resolver — verweigert/fehlend → **404** (nicht-öffentliche Seiten verraten ihre Existenz nie). Bild-Nodes (`data-file-id`) werden zu `/api/v1/media/:fileId` aufgelöst. - **Media**: `GET /media/:fileId` ist ebenfalls `@Public()`, sodass eingebettete Bilder öffentlicher Seiten für Anonyme streamen; der Attachment-Guard gated weiter auf den `public`-Grant (nicht-öffentlich → 404). - **web**: leichtgewichtige read-only `PublicPageView` unter `/public/:pondSlug/:pageSlug` (außerhalb des Auth-Guards) rendert das Server-HTML — **bewusst ohne** den kollaborativen Editor zu importieren, sodass Anonyme kein Editor-Bundle laden. Neuer `public`-i18n-Namespace (de+en). **Acceptance criteria** - [x] Öffentliche Seite logged-out lesbar via SPA **und** HTML-Endpoint; Grant entfernt → beide 404 in Sekunden (Cache-Invalidierung + NOTIFY). - [x] Nicht-öffentliche Seiten lecken nicht (Endpoints 404; negativer e2e). - [x] HTML-Endpoint-Ausgabe valide (doctype/title/canonical) ohne session-abhängigen Inhalt. - [x] Media-Streaming honoriert Public-Grants für eingebettete Bilder. **Live-Checks (Int):** anon HTML ohne Grant → 404; mit Public-Grant → 200 `text/html` (voll gerendert); content JSON → 200; nach Entzug → 404. **Tests:** `public.e2e.db.test.ts` (HTML+JSON, non-public 404, Grant-Entzug 404); Browser-`public`-Pack (anon liest Public-Seite + Bild via SPA; non-public → not found) mit eigenem CI-Step. _Hinweis: Anonyme Sidebar/Suche über public-Content ist nicht Teil dieses Commits — der Zugriffspfad läuft über direkte `/public/…`-Deep-Links; kann bei Bedarf als Folge-Issue ergänzt werden._
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#56
No description provided.