Files
teatea-pension/modules/core/server/audit.ts

69 lines
2.0 KiB
TypeScript

import { desc, eq } from "drizzle-orm";
import { getDatabase } from "@/modules/core/server/db";
import { auditLogs } from "@/modules/core/server/schema";
import type { AuditLog, AuditResult, PublicAccount } from "@/modules/core/types";
type AuditInput = {
actor?: PublicAccount;
organizationId?: string;
action: string;
targetType: string;
targetId?: string;
result: AuditResult;
reason: string;
};
function toAuditLog(row: typeof auditLogs.$inferSelect): AuditLog {
return {
id: row.id,
organizationId: row.organizationId ?? undefined,
timestamp: row.timestamp.toISOString(),
actorAccountId: row.actorAccountId ?? undefined,
actorEmail: row.actorEmail ?? undefined,
action: row.action,
targetType: row.targetType,
targetId: row.targetId ?? undefined,
result: row.result,
reason: row.reason,
};
}
export async function recordAuditLog(input: AuditInput): Promise<AuditLog> {
const database = getDatabase();
const rows = await database
.insert(auditLogs)
.values({
organizationId: input.organizationId,
actorAccountId: input.actor?.id,
actorEmail: input.actor?.email,
action: input.action,
targetType: input.targetType,
targetId: input.targetId,
result: input.result,
reason: input.reason,
})
.returning();
const row = rows[0];
if (!row) {
throw new Error("审计日志写入失败");
}
return toAuditLog(row);
}
export async function listAuditLogs(params: { organizationId?: string; limit?: number }): Promise<AuditLog[]> {
const database = getDatabase();
const limit = params.limit ?? 100;
const rows = params.organizationId
? await database
.select()
.from(auditLogs)
.where(eq(auditLogs.organizationId, params.organizationId))
.orderBy(desc(auditLogs.timestamp))
.limit(limit)
: await database.select().from(auditLogs).orderBy(desc(auditLogs.timestamp)).limit(limit);
return rows.map(toAuditLog);
}