Architecture
Stack
| Layer | Technology |
|---|---|
| Framework | Next.js 15 App Router |
| CMS | Payload CMS v3 (embedded in Next.js) |
| Database | Neon Postgres (via @payloadcms/db-postgres) |
| File storage | Vercel Blob (audio, saint images) |
| Styling | Tailwind CSS v4 |
| PWA | @ducanh2912/next-pwa — service worker + Web Push |
| Hosting | Vercel (single deployment, app + admin + API) |
Payload runs inside the Next.js process — there is no separate CMS server. The admin panel is served at /admin by Payload's Next.js plugin (@payloadcms/next).
Auth model
No login. Every godchild has a private URL:
slug— godchild's name slug, e.g.marietoken— 24-char hex token, generated once by Payload'sbeforeChangehook viacrypto.randomBytes(12).toString('hex')
Two-layer validation:
- Edge middleware (
middleware.ts) — fast format check: token must be ≥ 16 chars. Rejects garbage URLs with a 404 before hitting Node. - Layout (
app/[slug]/[token]/layout.tsx) — full DB lookup vialib/auth.ts → resolveGodchild(). Invalid slug/token → 404. Also checksactive: true.
Middleware runs on the edge runtime; Payload's local API needs Node, so the real DB check cannot happen there.
URL structure
/[slug]/[token] Dashboard
/[slug]/[token]/chapelet Rosary (mystery picker)
/[slug]/[token]/chapelet/[mystery] Mystery detail
/[slug]/[token]/prieres Prayer library
/[slug]/[token]/saint Patron saint
/[slug]/[token]/briques All briques archive
/admin Payload admin panel
/api/push/subscribe POST — save push subscription
Key files
middleware.ts Edge format guard
lib/auth.ts resolveGodchild() — DB token lookup
lib/mystery-of-day.ts Returns today's mystery type from weekday
lib/push.ts Web Push subscription helpers
payload.config.ts Payload configuration (collections, DB, secret)
payload-types.ts Auto-generated TypeScript types (do not edit)
collections/ One file per Payload collection
components/ UI components (BriqueCard, MysteryAccordion, etc.)
app/[slug]/[token]/ Godchild-facing routes
app/(payload)/ Payload admin (auto-managed by @payloadcms/next)
PWA
The service worker is generated by @ducanh2912/next-pwa at build time. Web Push requires VAPID keys (see Getting started). Push subscriptions are stored in the Godchildren collection (push_subscription field).
iOS requires the app to be installed via "Add to Home Screen" before push works (iOS 16.4+).