// 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 | 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, after: Record, ): Record { const changes: Record = {} 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 }