Public changelog page with admin CRUD. Multi-locale JSONB content following the CMS pattern. Toggle via appConfig.features.changelog.
The public feed displays up to 100 published entries by default. Configure publicFeedLimit in config/changelog.ts (1–1,000). Ordering uses publication date descending, with undated publications first and entry ID descending for ties. Hourly ISR and localized rendering remain in place.
Public reads use the shared Prisma runtime in a read-only transaction. PostgreSQL grants only the eight public columns and RLS hides drafts even without an application publication filter. Initialization verifies these capabilities; a failed database read fails the render instead of publishing an empty feed. Admin CRUD retains its separate authorization path.
Admin lists, detail reads and mutations also use shared Prisma, through private SQL capabilities that verify the active platform administrator. The uncached list includes drafts, excludes the heavy content field and returns up to 200 entries ordered by creation date descending and ID ascending. The limit comes from config/changelog.ts. A detail lookup returns the full entry. Missing IDs return no result; database failures are reported instead of appearing as an empty list or missing entry.
Creation, editing and deletion use atomic transactions. Omitted update fields are preserved. Publication requires a boolean: publishing sets a missing publication date, repeated publishing preserves it, and unpublishing clears it. Version labels may repeat. Successful mutations invalidate the public changelog page for every configured locale after commit. Missing entries return 404, malformed or invalid input returns 400 and unexpected failures use generic responses with centralized logging.
Write limits in config/changelog.ts cover a 2 MiB UTF-8 payload, 50-character version labels, 200-character localized titles and 100,000-character localized Markdown content. The API retains its 1 MiB request-body limit. Validation runs before and after locale normalization and sanitization.
Public Page
Timeline layout at /changelog. Version badges, color-coded type badges. Markdown rendering. ISR (1h). JSON-LD structured data.
Admin CRUD
/admin-dashboard/changelog — create, edit, publish/unpublish, delete. Locale tabs for title + content.
Entry Types
Feature (violet), Improvement (blue), Fix (green), Breaking (red), Security (orange). DB-level check constraint.
Security
Markdown sanitized before storage (strips scripts, iframes, event handlers). Admin-only RLS with WITH CHECK. UUID validation on all params.
| API Route | Method | Description |
|---|---|---|
/api/admin/changelog | GET | List the newest entries, including drafts, within the configured bound |
/api/admin/changelog | POST | Create entry |
/api/admin/changelog | PATCH | Update entry |
/api/admin/changelog | DELETE | Delete entry |
Documentation release snapshots
The documentation has two layers: content/docs/current/ is the editable preview served at /docs/next, while every validated release is preserved under content/docs/versions/<version>/ and served from /docs/<version>.
A version is validated when changelog.md contains a dated ## [version] - YYYY-MM-DD heading. The same change must run pnpm run docs:version:create -- <version>. CI rejects a release heading without its immutable documentation snapshot.
pnpm run docs:version:create -- 2.0
pnpm run docs:versions:check
The creation command snapshots MDX content, navigation, and onboarding metadata, updates the latest-version registry, and refuses to overwrite an existing release. Former unversioned /docs/... URLs remain temporary aliases to the latest validated version.