feat(cms): install Payload and serve the admin panel
Payload 3.88 runs inside the Next app against the existing Postgres, in
its own 'payload' schema so no pipeline operation on public — the app
tables, Airflow's metadata, migrate_csv_to_db.py --drop — can reach blog
content.
Its REST API is mounted at /cms-api. /api is the FastAPI proxy's
catch-all, which would swallow every admin call and forward it to the
backend with no error. The mount points live in lib/payloadRoutes.ts so
there is one definition and a test can assert it without importing
Payload: it is ESM-only, next/jest will not transform it, and appending
transformIgnorePatterns cannot un-ignore a package. Forcing it through
transpilePackages would change how the production build bundles Payload
to serve a test, so the live proof that /api still reaches FastAPI stays
where it belongs — the e2e journeys, which call /api/schools.
The package becomes ESM ("type": "module"), which Payload's CLI requires:
richtext-lexical has top-level await and the config cannot be require()d.
Only two files needed renaming, jest.config.cjs and a build script.
The build is verified to succeed with DATABASE_URL and PAYLOAD_SECRET
both unset, which is how CI builds it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017YmbBhr8s7GusjDE12hrZM
This commit is contained in:
1 parent
2437ffce42
commit
c5a4d106da
19 files changed
+5441
-226
No files matched your search
@@ -0,0 +1,33 @@
|
||||
import type { CollectionConfig } from 'payload';
|
||||
|
||||
/**
|
||||
* The site's only authenticated surface. There is one account and no
|
||||
* registration: `create` is closed to everyone, so the first user is seeded
|
||||
* with `payload create-first-user` and no one can add another through the API.
|
||||
*/
|
||||
export const Users: CollectionConfig = {
|
||||
slug: 'users',
|
||||
auth: {
|
||||
// Slows credential stuffing against a panel that is on the public
|
||||
// internet. Five attempts, then a ten-minute lock.
|
||||
maxLoginAttempts: 5,
|
||||
lockTime: 10 * 60 * 1000,
|
||||
},
|
||||
access: {
|
||||
create: () => false,
|
||||
read: ({ req }) => Boolean(req.user),
|
||||
update: ({ req }) => Boolean(req.user),
|
||||
delete: () => false,
|
||||
},
|
||||
admin: { useAsTitle: 'email' },
|
||||
fields: [
|
||||
{
|
||||
name: 'displayName',
|
||||
type: 'text',
|
||||
required: true,
|
||||
// Rendered as the byline on every post. First name only — the site
|
||||
// publishes no surname and no employer.
|
||||
defaultValue: 'Tudor',
|
||||
},
|
||||
],
|
||||
};
|
||||
Reference in new issue
Block a user