import { buildConfig } from 'payload' import { postgresAdapter } from '@payloadcms/db-postgres' import { lexicalEditor } from '@payloadcms/richtext-lexical' import { multiTenantPlugin } from '@payloadcms/plugin-multi-tenant' import { seoPlugin } from '@payloadcms/plugin-seo' import { nestedDocsPlugin } from '@payloadcms/plugin-nested-docs' import { formBuilderPlugin } from '@payloadcms/plugin-form-builder' import { redirectsPlugin } from '@payloadcms/plugin-redirects' import { openapi, swaggerUI } from 'payload-oapi' import { de } from '@payloadcms/translations/languages/de' import { en } from '@payloadcms/translations/languages/en' import path from 'path' import { fileURLToPath } from 'url' import sharp from 'sharp' // Security: Validate required environment variables at startup import { env } from './lib/envValidation' 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' import { Testimonials } from './collections/Testimonials' import { FAQs } from './collections/FAQs' import { Team } from './collections/Team' import { ServiceCategories } from './collections/ServiceCategories' import { Services } from './collections/Services' import { NewsletterSubscribers } from './collections/NewsletterSubscribers' // Portfolio Collections import { PortfolioCategories } from './collections/PortfolioCategories' import { Portfolios } from './collections/Portfolios' // Video Collections import { VideoCategories } from './collections/VideoCategories' import { Videos } from './collections/Videos' // Product Collections import { ProductCategories } from './collections/ProductCategories' import { Products } from './collections/Products' // Timeline Collection import { Timelines } from './collections/Timelines' // Workflow Collection import { Workflows } from './collections/Workflows' // Blogging Collections import { Tags } from './collections/Tags' import { Authors } from './collections/Authors' // New Feature Collections import { Locations } from './collections/Locations' import { Partners } from './collections/Partners' import { Jobs } from './collections/Jobs' import { Downloads } from './collections/Downloads' import { Events } from './collections/Events' // Tenant-specific Collections import { Bookings } from './collections/Bookings' import { Certifications } from './collections/Certifications' import { Projects } from './collections/Projects' // BlogWoman Collections - ENABLED import { Favorites } from './collections/Favorites' import { Series } from './collections/Series' // YouTube Operations Hub Collections import { YouTubeChannels } from './collections/YouTubeChannels' import { YouTubeContent } from './collections/YouTubeContent' import { YtTasks } from './collections/YtTasks' import { YtNotifications } from './collections/YtNotifications' // YouTube Operations Hub v2 Collections import { YtBatches } from './collections/YtBatches' import { YtMonthlyGoals } from './collections/YtMonthlyGoals' import { YtScriptTemplates } from './collections/YtScriptTemplates' import { YtChecklistTemplates } from './collections/YtChecklistTemplates' import { YtSeries } from './collections/YtSeries' // Community Management Collections import { SocialPlatforms } from './collections/SocialPlatforms' import { SocialAccounts } from './collections/SocialAccounts' import { CommunityInteractions } from './collections/CommunityInteractions' import { CommunityTemplates } from './collections/CommunityTemplates' import { CommunityRules } from './collections/CommunityRules' import { ReportSchedules } from './collections/ReportSchedules' // Debug: Minimal test collection - DISABLED (nur für Tests) // import { TestMinimal } from './collections/TestMinimal' // Consent Management Collections import { CookieConfigurations } from './collections/CookieConfigurations' import { CookieInventory } from './collections/CookieInventory' import { ConsentLogs } from './collections/ConsentLogs' import { PrivacyPolicySettings } from './collections/PrivacyPolicySettings' // Tenant-specific Settings Collections (converted from Globals) import { SiteSettings } from './collections/SiteSettings' import { Navigations } from './collections/Navigations' // Global Settings (system-wide, not tenant-specific) import { SEOSettings } from './globals/SEOSettings' // Hooks import { sendFormNotification } from './hooks/sendFormNotification' import { formSubmissionBeforeChange } from './hooks/formSubmissionHooks' import { setSubmissionTenant } from './hooks/setSubmissionTenant' // Form Submissions Overrides import { formSubmissionOverrides } from './collections/FormSubmissionsOverrides' // Email import { multiTenantEmailAdapter } from './lib/email/payload-email-adapter' // Email Logs import { EmailLogs } from './collections/EmailLogs' // Audit Logs import { AuditLogs } from './collections/AuditLogs' // Monitoring Collections import { MonitoringSnapshots } from './collections/MonitoringSnapshots' import { MonitoringLogs } from './collections/MonitoringLogs' import { MonitoringAlertRules } from './collections/MonitoringAlertRules' import { MonitoringAlertHistory } from './collections/MonitoringAlertHistory' const filename = fileURLToPath(import.meta.url) const dirname = path.dirname(filename) export default buildConfig({ serverURL: process.env.PAYLOAD_PUBLIC_SERVER_URL || 'https://pl.porwoll.tech', admin: { user: Users.slug, components: { afterNavLinks: [ '@/components/admin/CommunityNavLinks#CommunityNavLinks', '@/components/admin/YouTubeAnalyticsNavLinks#YouTubeAnalyticsNavLinks', '@/components/admin/MonitoringNavLinks#MonitoringNavLinks', ], views: { TenantDashboard: { Component: '@/components/admin/TenantDashboardView#TenantDashboardView', path: '/tenant-dashboard', }, YouTubeAnalyticsDashboard: { Component: '@/components/admin/YouTubeAnalyticsDashboardView#YouTubeAnalyticsDashboardView', path: '/youtube-analytics', }, ContentCalendar: { Component: '@/components/admin/ContentCalendarView#ContentCalendarView', path: '/content-calendar', }, MonitoringDashboard: { Component: '@/components/admin/MonitoringDashboardView#MonitoringDashboardView', path: '/monitoring', }, }, }, }, // Multi-Tenant Email Adapter email: multiTenantEmailAdapter, // Admin Panel Internationalization (UI translations) i18n: { supportedLanguages: { de, en }, fallbackLanguage: 'de', }, // Content Localization (multilingual content) localization: { locales: [ { label: 'Deutsch', code: 'de', }, { label: 'English', code: 'en', }, ], defaultLocale: 'de', fallback: true, }, // 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', 'http://10.10.181.104:3000', 'https://dev.zh3.de', 'https://porwoll.de', 'https://www.porwoll.de', 'https://pl.porwoll.tech', 'https://pl.c2sgmbh.de', 'https://cms.c2sgmbh.de', ], // CSRF Protection csrf: [ 'http://localhost:3000', 'http://localhost:3001', 'http://10.10.180.153:3000', 'http://10.10.180.153:3001', 'http://10.10.181.104:3000', 'https://dev.zh3.de', 'https://porwoll.de', 'https://www.porwoll.de', 'https://pl.porwoll.tech', 'https://pl.c2sgmbh.de', 'https://cms.c2sgmbh.de', ], collections: [ Users, Media, Tenants, Pages, Posts, Categories, SocialLinks, Testimonials, FAQs, Team, ServiceCategories, Services, NewsletterSubscribers, // Portfolio PortfolioCategories, Portfolios, // Videos VideoCategories, Videos, // Products ProductCategories, Products, // Timelines & Workflows Timelines, Workflows, // Blogging Tags, Authors, // New Feature Collections Locations, Partners, Jobs, Downloads, Events, // Tenant-specific Collections Bookings, Certifications, Projects, // BlogWoman Collections - ENABLED Favorites, Series, // YouTube Operations Hub YouTubeChannels, YouTubeContent, YtTasks, YtNotifications, // YouTube Operations Hub v2 YtBatches, YtMonthlyGoals, YtScriptTemplates, YtChecklistTemplates, YtSeries, // Community Management SocialPlatforms, SocialAccounts, CommunityInteractions, CommunityTemplates, CommunityRules, ReportSchedules, // Debug: Minimal test collection - DISABLED // TestMinimal, // Consent Management CookieConfigurations, CookieInventory, ConsentLogs, PrivacyPolicySettings, // System EmailLogs, AuditLogs, // Monitoring MonitoringSnapshots, MonitoringLogs, MonitoringAlertRules, MonitoringAlertHistory, // Tenant-specific Settings (converted from Globals) SiteSettings, Navigations, ], globals: [SEOSettings], editor: lexicalEditor(), secret: env.PAYLOAD_SECRET, typescript: { outputFile: path.resolve(dirname, 'payload-types.ts'), }, db: postgresAdapter({ pool: { connectionString: env.DATABASE_URI, }, // push: false - Migrationen für Schema-Änderungen verwenden push: false, }), // Sharp für Bildoptimierung sharp, plugins: [ // formBuilderPlugin MUSS vor multiTenantPlugin stehen, da es die forms/form-submissions // Collections erstellt, die multiTenantPlugin dann mit Tenant-Scoping erweitert. formBuilderPlugin({ fields: { text: true, textarea: true, select: true, email: true, state: false, country: false, checkbox: true, number: true, message: true, payment: false, }, // Fix für TypeScript Types Generation - das Plugin braucht explizite relationTo Angaben redirectRelationships: ['pages'], formOverrides: { admin: { group: 'Formulare', }, labels: { singular: 'Formular', plural: 'Formulare', }, fields: ({ defaultFields }) => [ ...defaultFields, { name: 'tenant', type: 'relationship', relationTo: 'tenants', required: true, admin: { position: 'sidebar' }, }, ], }, formSubmissionOverrides: { ...(formSubmissionOverrides as Record), hooks: { beforeChange: [setSubmissionTenant, formSubmissionBeforeChange], afterChange: [sendFormNotification], }, } as Parameters[0]['formSubmissionOverrides'], }), multiTenantPlugin({ tenantsSlug: 'tenants', collections: { media: {}, pages: {}, posts: {}, categories: {}, 'social-links': {}, // Type assertion für neue Collections bis payload-types.ts regeneriert wird ...({ testimonials: {}, faqs: {}, team: {}, 'service-categories': {}, services: {}, 'newsletter-subscribers': {}, // Portfolio Collections 'portfolio-categories': {}, portfolios: {}, // Video Collections 'video-categories': {}, videos: {}, // Product Collections 'product-categories': {}, products: {}, // Timeline & Workflow Collections timelines: {}, workflows: {}, // Blogging Collections tags: {}, authors: {}, // New Feature Collections locations: {}, partners: {}, jobs: {}, downloads: {}, events: {}, // Tenant-specific Collections bookings: {}, certifications: {}, projects: {}, // BlogWoman Collections - ENABLED favorites: {}, series: {}, // Debug: Minimal test collection - DISABLED // 'test-minimal': {}, // Form Builder Plugin Collections - customTenantField: true weil tenant via formOverrides injiziert wird forms: { customTenantField: true }, 'form-submissions': { customTenantField: true }, // Consent Management Collections - customTenantField: true weil sie bereits ein tenant-Feld haben 'cookie-configurations': { customTenantField: true }, 'cookie-inventory': { customTenantField: true }, 'consent-logs': { customTenantField: true }, 'privacy-policy-settings': { customTenantField: true }, // Tenant-specific Settings (converted from Globals) 'site-settings': {}, navigations: {}, } as Record), }, // Super Admins haben Zugriff auf alle Tenants userHasAccessToAllTenants: (user) => { const result = Boolean(user?.isSuperAdmin) console.log('[DEBUG:MultiTenant] userHasAccessToAllTenants:', { userId: user?.id, email: user?.email, isSuperAdmin: user?.isSuperAdmin, result, tenants: user?.tenants, userKeys: user ? Object.keys(user) : 'no user', }) return result }, debug: true, // Deutsche Übersetzungen für den Tenant-Selector i18n: { translations: { de: { 'nav-tenantSelector-label': 'Nach Tenant filtern', 'assign-tenant-button-label': 'Tenant zuweisen', 'assign-tenant-modal-title': '"{{title}}" zuweisen', 'field-assignedTenant-label': 'Zugewiesener Tenant', }, en: { 'nav-tenantSelector-label': 'Filter by Tenant', 'assign-tenant-button-label': 'Assign Tenant', 'assign-tenant-modal-title': 'Assign "{{title}}"', 'field-assignedTenant-label': 'Assigned Tenant', }, }, }, }), seoPlugin({ collections: [], uploadsCollection: 'media', generateTitle: ({ doc }) => `${doc?.title || ''} | Website`, generateDescription: ({ doc }) => doc?.excerpt || '', }), nestedDocsPlugin({ collections: [], generateLabel: (_, doc) => doc.title as string, generateURL: (docs) => docs.reduce((url, doc) => `${url}/${doc.slug}`, ''), }), redirectsPlugin({ collections: ['pages'], overrides: { admin: { group: 'Einstellungen', }, }, }), // OpenAPI Documentation openapi({ openapiVersion: '3.1', metadata: { title: 'Payload CMS API', version: '1.0.0', description: 'Multi-Tenant CMS API für porwoll.de, complexcaresolutions.de, gunshin.de und zweitmein.ng', }, }), // Swagger UI unter /api/docs swaggerUI({}), ], })