mirror of
https://github.com/complexcaresolutions/cms.c2sgmbh.git
synced 2026-03-17 22:04:10 +00:00
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>
383 lines
8.6 KiB
Markdown
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
|