# 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