// src/lib/tenantAccess.ts import type { Access, PayloadRequest } from 'payload' /** * Ermittelt die Tenant-ID aus dem Request-Host. * Gleicht die Domain mit der tenants-Collection ab. */ export async function getTenantIdFromHost(req: PayloadRequest): Promise { try { // Host-Header extrahieren (unterstützt verschiedene Formate) const headers = req.headers as Headers | Record const host = typeof headers.get === 'function' ? headers.get('host') : (headers as Record)['host'] if (!host || typeof host !== 'string') { return null } // Domain normalisieren: Port und www entfernen const domain = host.split(':')[0].replace(/^www\./, '').toLowerCase().trim() if (!domain) { return null } // Tenant aus Datenbank suchen (domains ist ein Array mit domain-Subfeld) const result = await req.payload.find({ collection: 'tenants', where: { 'domains.domain': { equals: domain }, }, limit: 1, depth: 0, }) if (result.docs.length > 0 && result.docs[0]?.id) { return Number(result.docs[0].id) } return null } catch (error) { console.error('[TenantAccess] Error resolving tenant from host:', error) return null } } /** * Access-Control für öffentlich lesbare, aber tenant-isolierte Collections. * * - Authentifizierte Admin-User: Voller Lesezugriff * - Anonyme Requests: Nur Daten des eigenen Tenants (basierend auf Domain) */ export const tenantScopedPublicRead: Access = async ({ req }) => { // Authentifizierte Admins dürfen alles lesen if (req.user) { return true } // Anonyme Requests: Tenant aus Domain ermitteln const tenantId = await getTenantIdFromHost(req) if (!tenantId) { // Keine gültige Domain → kein Zugriff return false } // Nur Dokumente des eigenen Tenants zurückgeben return { tenant: { equals: tenantId, }, } } /** * Access-Control: Nur authentifizierte User */ export const authenticatedOnly: Access = ({ req }) => { return !!req.user }