import type { CollectionConfig } from "payload"; /** * Append-only Payload collection for audit entries. Mounted by core-cms * when this package is scaffolded (manual wiring step printed by generator). * * Access rules: * - read: admins only * - create: any authenticated context (filtered upstream by PayloadAuditLog) * - update: NEVER (compliance requires append-only) * - delete: admins only (used by the GDPR erasure path with overrideAccess) * * The `update: () => false` rule is the compliance backbone. The erasure * path uses `overrideAccess: true` to bypass for pseudonymization — that's * Payload's documented escape hatch for privileged operations. */ export const auditLogsCollection: CollectionConfig = { slug: "audit-logs", access: { read: ({ req }) => { const user = req.user as { roles?: string[] } | null | undefined; return Array.isArray(user?.roles) && user.roles.includes("admin"); }, create: () => true, update: () => false, delete: ({ req }) => { const user = req.user as { roles?: string[] } | null | undefined; return Array.isArray(user?.roles) && user.roles.includes("admin"); }, }, timestamps: true, fields: [ // WHO { name: "actorId", type: "text", required: true, index: true }, { name: "actorType", type: "select", options: ["user", "system", "service"], required: true, }, { name: "actorRoles", type: "json", required: true }, // WHAT { name: "action", type: "select", options: ["VIEW", "CREATE", "UPDATE", "DELETE", "EXPORT", "PERMISSION_CHANGE"], required: true, index: true, }, { name: "resourceType", type: "text", required: true, index: true }, { name: "resourceId", type: "text" }, { name: "changedFields", type: "json" }, // SCOPE { name: "scopeFeature", type: "text", required: true, index: true }, { name: "scopeEnvironment", type: "text", required: true }, { name: "scopeTenant", type: "text", required: true, index: true }, // WHY { name: "reason", type: "text" }, { name: "correlationId", type: "text", index: true }, { name: "requestId", type: "text" }, // FROM { name: "ipTruncated", type: "text", required: true }, { name: "userAgent", type: "text", required: true }, // PII { name: "containsPii", type: "checkbox", required: true }, { name: "piiCategories", type: "json" }, // OUTCOME { name: "outcome", type: "select", options: ["success", "denied", "error"], required: true, }, { name: "errorCode", type: "text" }, ], };