shared/lib/audit/write-audit-log.ts

67 lines
2.3 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// shared/lib/audit/write-audit-log.ts — 通用审计日志写入函数
export interface AuditLogEntry {
userId?: number | null
username?: string | null
apiKeyId?: number | null
action: string
entityType: string
entityId?: number | null
details?: Record<string, unknown> | null
ipAddress?: string | null
}
export interface AuditStore {
exec(sql: string): void
}
// 写入审计日志(通用版本,调用方传入 store 执行 SQL
export function writeAuditLog(store: AuditStore, entry: AuditLogEntry): void {
const userId = entry.userId ?? 'NULL'
const username = entry.username ? `'${entry.username.replace(/'/g, "''")}'` : 'NULL'
const apiKeyId = entry.apiKeyId ?? 'NULL'
const action = `'${entry.action.replace(/'/g, "''")}'`
const entityType = `'${entry.entityType.replace(/'/g, "''")}'`
const entityId = entry.entityId ?? 'NULL'
const details = entry.details ? `'${JSON.stringify(entry.details).replace(/'/g, "''")}'` : 'NULL'
const ipAddress = entry.ipAddress ? `'${entry.ipAddress.replace(/'/g, "''")}'` : 'NULL'
const sql = `INSERT INTO audit_logs (user_id, username, api_key_id, action, entity_type, entity_id, details, ip_address, created_at)
VALUES (${userId}, ${username}, ${apiKeyId}, ${action}, ${entityType}, ${entityId}, ${details}, ${ipAddress}, datetime('now', '+8 hours'))`
try {
store.exec(sql)
} catch (e) {
// 审计写入失败不阻断主操作
console.error('审计日志写入失败:', e)
}
}
// 对象差异对比(用于审计详情)
export function diffObjects(
before: Record<string, unknown>,
after: Record<string, unknown>,
): Record<string, { from: unknown; to: unknown }> {
const changes: Record<string, { from: unknown; to: unknown }> = {}
const keys = new Set([...Object.keys(before), ...Object.keys(after)])
for (const key of keys) {
// 跳过系统字段
if (['created_at', 'updated_at'].includes(key)) continue
const from = before[key]
const to = after[key]
if (JSON.stringify(from) !== JSON.stringify(to)) {
changes[key] = { from, to }
}
}
return changes
}
// 获取客户端 IP
export function getClientIP(request: Request): string | null {
const forwarded = request.headers.get('x-forwarded-for')
if (forwarded) return forwarded.split(',')[0].trim()
return request.headers.get('x-real-ip') ?? null
}