/** * Activity log utility — records staff actions for audit trail (H10) * * Uses Prisma's AnalyticsEvent table as a lightweight activity store. * Each entry records WHO did WHAT to WHICH entity and WHEN. */ import prisma from "@/lib/prisma" import type { ActivityAction } from "@/lib/ai" interface LogActivityInput { action: ActivityAction entityType: string entityId?: string orgId: string userId?: string userName?: string metadata?: Record } /** * Log an activity entry. Non-blocking — errors are silently swallowed. */ export async function logActivity(input: LogActivityInput): Promise { if (!prisma) return try { await prisma.analyticsEvent.create({ data: { eventType: `activity.${input.action}`, eventId: input.entityType === "event" ? input.entityId : undefined, pledgeId: input.entityType === "pledge" ? input.entityId : undefined, metadata: { action: input.action, entityType: input.entityType, entityId: input.entityId, orgId: input.orgId, userId: input.userId, userName: input.userName, ...input.metadata, }, }, }) } catch (err) { // Activity logging should never break the main flow console.error("[activity-log] Failed to write:", err) } } /** * Query activity log for an org. Returns most recent entries. */ export async function getActivityLog( orgId: string, options: { limit?: number; entityType?: string; entityId?: string } = {} ) { if (!prisma) return [] const { limit = 50, entityType, entityId } = options // eslint-disable-next-line @typescript-eslint/no-explicit-any const where: any = { eventType: { startsWith: "activity." }, } if (entityId) { where.OR = [ { eventId: entityId }, { pledgeId: entityId }, ] } if (entityType) { where.eventType = { startsWith: `activity.${entityType}.` } } const entries = await prisma.analyticsEvent.findMany({ where, orderBy: { createdAt: "desc" }, take: limit, }) return entries.map(e => { const meta = e.metadata as Record || {} return { id: e.id, action: meta.action as string, entityType: meta.entityType as string, entityId: meta.entityId as string, userId: meta.userId as string, userName: meta.userName as string, metadata: meta, timestamp: e.createdAt, } }) }