import { eq, desc, and, inArray, sql } from "drizzle-orm"; import { database } from "~/database/context"; import * as schema from "~/database/schema"; import { findUserById, getUserDisplayName } from "~/models/user"; export type AuditLogEntry = typeof schema.commissionerAuditLog.$inferSelect; export type NewAuditLogEntry = typeof schema.commissionerAuditLog.$inferInsert; export type AuditAction = typeof schema.auditActionEnum.enumValues[number]; export interface AuditLogPage { entries: AuditLogEntry[]; total: number; hasMore: boolean; } export async function createAuditLogEntry( data: NewAuditLogEntry ): Promise { const db = database(); const [entry] = await db .insert(schema.commissionerAuditLog) .values(data) .returning(); return entry; } export async function getAuditLogForSeason( seasonId: string, options?: { limit?: number; offset?: number; actions?: AuditAction[] } ): Promise { const db = database(); const limit = options?.limit ?? 50; const offset = options?.offset ?? 0; const whereClause = options?.actions && options.actions.length > 0 ? and( eq(schema.commissionerAuditLog.seasonId, seasonId), inArray(schema.commissionerAuditLog.action, options.actions) ) : eq(schema.commissionerAuditLog.seasonId, seasonId); const [entries, countRows] = await Promise.all([ db .select() .from(schema.commissionerAuditLog) .where(whereClause) .orderBy(desc(schema.commissionerAuditLog.createdAt)) .limit(limit) .offset(offset), db .select({ count: sql`count(*)::int` }) .from(schema.commissionerAuditLog) .where(whereClause), ]); const total = countRows[0]?.count ?? 0; return { entries, total, hasMore: offset + entries.length < total }; } /** * Convenience wrapper that resolves the actor display name from the users table * and writes a single audit log record. Call this after the main action succeeds. */ export async function logCommissionerAction(params: { seasonId: string; leagueId: string; actorUserId: string; action: AuditAction; affectedTeamIds?: string[]; details?: Record; }): Promise { const user = await findUserById(params.actorUserId); const actorDisplayName = user ? (getUserDisplayName(user) ?? params.actorUserId) : params.actorUserId; await createAuditLogEntry({ seasonId: params.seasonId, leagueId: params.leagueId, actorUserId: params.actorUserId, actorDisplayName, action: params.action, affectedTeamIds: params.affectedTeamIds ?? [], details: params.details ?? {}, }); }