cms.c2sgmbh/docs/PROMPT_PAYLOAD_API_CONFIG.md
Martin Porwoll a88e4f60d0 test: add E2E and integration tests with documentation
Tests:
- Update frontend.e2e.spec.ts with locale testing
- Add search.e2e.spec.ts for search functionality
- Add i18n.int.spec.ts for localization tests
- Add search.int.spec.ts for search integration
- Update playwright.config.ts

Documentation:
- Add CLAUDE.md with project instructions
- Add docs/ directory with detailed documentation
- Add scripts/ for utility scripts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 08:19:52 +00:00

383 lines
8.6 KiB
Markdown

# Payload API Konfiguration für externe Frontend-Zugriffe
## Kontext
Du arbeitest im Verzeichnis `/home/payload/payload-cms` auf dem Server sv-payload (10.10.181.100).
Das Frontend wird auf einem separaten Development-Server entwickelt:
- IP: 10.10.180.153
- Domain: dev.zh3.de
- Projekt: frontend-porwoll
Payload CMS muss so konfiguriert werden, dass externe Frontends auf die API zugreifen können.
## Aufgabe
1. CORS konfigurieren für Frontend-Zugriff
2. API-Zugriff ohne Authentifizierung für öffentliche Inhalte ermöglichen
3. Optional: GraphQL aktivieren
4. API Key für geschützte Operationen erstellen
## Schritt 1: CORS Konfiguration
Aktualisiere `src/payload.config.ts`:
```typescript
import { buildConfig } from 'payload'
import { postgresAdapter } from '@payloadcms/db-postgres'
import { lexicalEditor } from '@payloadcms/richtext-lexical'
import { multiTenantPlugin } from '@payloadcms/plugin-multi-tenant'
import path from 'path'
import { fileURLToPath } from 'url'
// Collections
import { Users } from './collections/Users'
import { Media } from './collections/Media'
import { Tenants } from './collections/Tenants'
import { Pages } from './collections/Pages'
import { Posts } from './collections/Posts'
import { Categories } from './collections/Categories'
import { SocialLinks } from './collections/SocialLinks'
// Globals
import { SiteSettings } from './globals/SiteSettings'
import { Navigation } from './globals/Navigation'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
export default buildConfig({
admin: {
user: Users.slug,
},
// CORS Konfiguration für externe Frontends
cors: [
'http://localhost:3000',
'http://localhost:3001',
'http://10.10.180.153:3000',
'http://10.10.180.153:3001',
'https://dev.zh3.de',
'https://porwoll.de',
'https://www.porwoll.de',
],
// CSRF Protection - gleiche Origins
csrf: [
'http://localhost:3000',
'http://localhost:3001',
'http://10.10.180.153:3000',
'http://10.10.180.153:3001',
'https://dev.zh3.de',
'https://porwoll.de',
'https://www.porwoll.de',
],
collections: [Users, Media, Tenants, Pages, Posts, Categories, SocialLinks],
globals: [SiteSettings, Navigation],
editor: lexicalEditor(),
secret: process.env.PAYLOAD_SECRET || '',
typescript: {
outputFile: path.resolve(dirname, 'payload-types.ts'),
},
db: postgresAdapter({
pool: {
connectionString: process.env.DATABASE_URI || '',
},
}),
plugins: [
multiTenantPlugin({
tenantsSlug: 'tenants',
collections: {
media: {},
pages: {},
posts: {},
categories: {},
'social-links': {},
},
debug: true,
}),
],
})
```
## Schritt 2: Öffentlichen API-Zugriff konfigurieren
Für öffentliche Inhalte (Pages, Posts) muss der `read`-Zugriff ohne Auth erlaubt werden.
### Pages Collection aktualisieren (`src/collections/Pages.ts`)
Füge Access Control hinzu:
```typescript
import type { CollectionConfig } from 'payload'
export const Pages: CollectionConfig = {
slug: 'pages',
admin: {
useAsTitle: 'title',
defaultColumns: ['title', 'slug', 'status', 'updatedAt'],
},
// Öffentlicher Lesezugriff für veröffentlichte Seiten
access: {
read: ({ req }) => {
// Eingeloggte User sehen alles
if (req.user) return true
// Öffentlich: nur veröffentlichte Seiten
return {
status: {
equals: 'published',
},
}
},
create: ({ req }) => !!req.user,
update: ({ req }) => !!req.user,
delete: ({ req }) => !!req.user,
},
fields: [
// ... bestehende Felder
],
}
```
### Posts Collection aktualisieren (`src/collections/Posts.ts`)
```typescript
import type { CollectionConfig } from 'payload'
export const Posts: CollectionConfig = {
slug: 'posts',
admin: {
useAsTitle: 'title',
defaultColumns: ['title', 'category', 'status', 'publishedAt'],
},
access: {
read: ({ req }) => {
if (req.user) return true
return {
status: {
equals: 'published',
},
}
},
create: ({ req }) => !!req.user,
update: ({ req }) => !!req.user,
delete: ({ req }) => !!req.user,
},
fields: [
// ... bestehende Felder
],
}
```
### Media Collection aktualisieren (`src/collections/Media.ts`)
```typescript
import type { CollectionConfig } from 'payload'
export const Media: CollectionConfig = {
slug: 'media',
admin: {
useAsTitle: 'alt',
},
access: {
// Medien sind öffentlich lesbar
read: () => true,
create: ({ req }) => !!req.user,
update: ({ req }) => !!req.user,
delete: ({ req }) => !!req.user,
},
upload: {
staticDir: 'media',
mimeTypes: ['image/*', 'application/pdf'],
},
fields: [
{
name: 'alt',
type: 'text',
required: true,
},
],
}
```
### Categories Collection (`src/collections/Categories.ts`)
```typescript
access: {
read: () => true, // Kategorien sind öffentlich
create: ({ req }) => !!req.user,
update: ({ req }) => !!req.user,
delete: ({ req }) => !!req.user,
},
```
### SocialLinks Collection (`src/collections/SocialLinks.ts`)
```typescript
access: {
read: () => true, // Social Links sind öffentlich
create: ({ req }) => !!req.user,
update: ({ req }) => !!req.user,
delete: ({ req }) => !!req.user,
},
```
### Globals öffentlich machen
#### SiteSettings (`src/globals/SiteSettings.ts`)
```typescript
import type { GlobalConfig } from 'payload'
export const SiteSettings: GlobalConfig = {
slug: 'site-settings',
access: {
read: () => true, // Öffentlich lesbar
update: ({ req }) => !!req.user,
},
fields: [
// ... bestehende Felder
],
}
```
#### Navigation (`src/globals/Navigation.ts`)
```typescript
import type { GlobalConfig } from 'payload'
export const Navigation: GlobalConfig = {
slug: 'navigation',
access: {
read: () => true, // Öffentlich lesbar
update: ({ req }) => !!req.user,
},
fields: [
// ... bestehende Felder
],
}
```
## Schritt 3: GraphQL aktivieren (Optional)
Falls GraphQL gewünscht ist, installiere das Plugin:
```bash
pnpm add @payloadcms/graphql
```
Dann in `payload.config.ts`:
```typescript
import { buildConfig } from 'payload'
import { graphqlPlugin } from '@payloadcms/graphql'
export default buildConfig({
// ... andere Config
plugins: [
graphqlPlugin({}),
multiTenantPlugin({
// ...
}),
],
})
```
GraphQL Endpoint: `https://pl.c2sgmbh.de/api/graphql`
## Schritt 4: API Key für geschützte Operationen (Optional)
Für Operationen wie Kontaktformular-Submissions kann ein API Key erstellt werden.
### Umgebungsvariable hinzufügen
```bash
# In .env hinzufügen
PAYLOAD_API_KEY=dein-sicherer-api-key-hier-generieren
```
Generiere einen sicheren Key:
```bash
openssl rand -hex 32
```
### API Key Middleware (falls benötigt)
Für spezielle Endpoints kann der API Key geprüft werden. Für die meisten Fälle reicht jedoch die Access Control.
## Schritt 5: Media URL Konfiguration
Stelle sicher, dass Media-URLs korrekt sind:
```typescript
// In payload.config.ts
export default buildConfig({
serverURL: process.env.PAYLOAD_PUBLIC_SERVER_URL || 'https://pl.c2sgmbh.de',
// ... rest
})
```
Die `.env` sollte enthalten:
```env
PAYLOAD_PUBLIC_SERVER_URL=https://pl.c2sgmbh.de
```
## Schritt 6: Build und Neustart
```bash
cd /home/payload/payload-cms
pnpm build
pm2 restart payload
```
## API Endpoints nach Konfiguration
| Endpoint | Methode | Beschreibung |
|----------|---------|--------------|
| `/api/pages` | GET | Alle veröffentlichten Seiten |
| `/api/pages?where[slug][equals]=home` | GET | Seite nach Slug |
| `/api/posts` | GET | Alle veröffentlichten Posts |
| `/api/posts?limit=10&page=1` | GET | Posts paginiert |
| `/api/categories` | GET | Alle Kategorien |
| `/api/media` | GET | Alle Medien |
| `/api/globals/site-settings` | GET | Site Settings |
| `/api/globals/navigation` | GET | Navigation |
## Test der API
Nach dem Neustart testen:
```bash
# Von sv-dev aus
curl https://pl.c2sgmbh.de/api/pages
curl https://pl.c2sgmbh.de/api/globals/site-settings
curl https://pl.c2sgmbh.de/api/globals/navigation
# Oder lokal auf sv-payload
curl http://localhost:3000/api/pages
```
## Erfolgskriterien
1. ✅ API antwortet auf Anfragen von dev.zh3.de ohne CORS-Fehler
2. ✅ Öffentliche Endpoints liefern Daten ohne Auth
3. ✅ Admin-Panel funktioniert weiterhin unter /admin
4. ✅ Media-URLs sind vollständig (mit Domain)
## Sicherheitshinweise
- Nur `read`-Zugriff ist öffentlich
- `create`, `update`, `delete` erfordern Authentifizierung
- Unveröffentlichte Inhalte sind nicht öffentlich sichtbar
- Admin-Panel bleibt geschützt