108 lines
2.5 KiB
TypeScript
108 lines
2.5 KiB
TypeScript
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
|
||
}
|