import type { CollectionAfterChangeHook, CollectionAfterDeleteHook } from 'payload' /** * 记录操作日志的通用函数 */ export async function logAction({ req, action, collection, documentId, documentTitle, changes, }: { req: any action: 'create' | 'update' | 'delete' | 'sync' | 'login' | 'logout' collection: string documentId?: string documentTitle?: string changes?: any }) { try { const { payload, user } = req if (!user) return // 无用户信息则不记录 // 获取 IP 地址 const ip = req.headers?.['x-forwarded-for'] || req.headers?.['x-real-ip'] || req.ip || req.connection?.remoteAddress || 'unknown' // 获取 User Agent const userAgent = req.headers?.['user-agent'] || 'unknown' // 创建日志记录 await payload.create({ collection: 'logs', data: { action, collection, documentId: documentId?.toString(), documentTitle, user: user.id, changes, ip, userAgent, }, // 不触发 hooks,避免递归 context: { skipHooks: true }, }) } catch (error) { // 静默失败,避免影响主要操作 console.error('[Log Hook Error]:', error) } } /** * afterChange 钩子:记录创建和更新操作 */ export const logAfterChange: CollectionAfterChangeHook = async ({ doc, req, operation, collection, }) => { // 跳过日志自身的操作,避免递归 if (req.context?.skipHooks) return doc const collectionSlug = collection?.slug as string // 不记录 logs 和 users-sessions 自身 if (collectionSlug === 'logs' || collectionSlug === 'users-sessions') return doc await logAction({ req, action: operation === 'create' ? 'create' : 'update', collection: collectionSlug, documentId: doc.id, documentTitle: doc.title || doc.name || doc.email || doc.alt || `ID: ${doc.id}`, changes: operation === 'update' ? { updatedFields: Object.keys(doc) } : undefined, }) return doc } /** * afterDelete 钩子:记录删除操作 */ export const logAfterDelete: CollectionAfterDeleteHook = async ({ doc, req, collection }) => { if (req.context?.skipHooks) return doc const collectionSlug = collection?.slug as string if (collectionSlug === 'logs' || collectionSlug === 'users-sessions') return doc await logAction({ req, action: 'delete', collection: collectionSlug, documentId: doc.id, documentTitle: doc.title || doc.name || doc.email || doc.alt || `ID: ${doc.id}`, }) return doc }