diff --git a/packages/api/src/router/audit-log-procedure-support.ts b/packages/api/src/router/audit-log-procedure-support.ts new file mode 100644 index 0000000..b935618 --- /dev/null +++ b/packages/api/src/router/audit-log-procedure-support.ts @@ -0,0 +1,127 @@ +import { z } from "zod"; +import type { TRPCContext } from "../trpc.js"; +import { + auditLogByEntityInputSchema, + auditLogListInputSchema, + auditLogTimelineInputSchema, +} from "./audit-log-inputs.js"; +import { + formatAuditDetailEntry, + formatAuditListEntry, + getAuditActivitySummary, + getAuditEntriesByEntity, + getAuditEntryById, + getAuditTimeline, + listAuditEntries, + toAuditListInput, + toAuditTimelineInput, +} from "./audit-log-support.js"; + +export { auditLogByEntityInputSchema, auditLogListInputSchema, auditLogTimelineInputSchema }; + +export const auditLogEntryByIdInputSchema = z.object({ + id: z.string(), +}); + +export const auditLogActivitySummaryInputSchema = z.object({ + startDate: z.date().optional(), + endDate: z.date().optional(), +}); + +type AuditLogContext = Pick; + +export async function listAuditLogEntries( + ctx: AuditLogContext, + input: z.infer, +) { + return listAuditEntries(ctx.db, toAuditListInput({ + entityType: input.entityType, + entityId: input.entityId, + userId: input.userId, + action: input.action, + source: input.source, + startDate: input.startDate, + endDate: input.endDate, + search: input.search, + limit: input.limit, + cursor: input.cursor, + })); +} + +export async function listAuditLogEntriesDetail( + ctx: AuditLogContext, + input: z.infer, +) { + const result = await listAuditLogEntries(ctx, input); + return { + items: result.items.map(formatAuditListEntry), + nextCursor: result.nextCursor ?? null, + }; +} + +export async function getAuditLogEntryById( + ctx: AuditLogContext, + input: z.infer, +) { + return getAuditEntryById(ctx.db, input.id); +} + +export async function getAuditLogEntryByIdDetail( + ctx: AuditLogContext, + input: z.infer, +) { + const entry = await getAuditEntryById(ctx.db, input.id); + return formatAuditDetailEntry(entry); +} + +export async function getAuditLogEntriesByEntity( + ctx: AuditLogContext, + input: z.infer, +) { + return getAuditEntriesByEntity(ctx.db, input); +} + +export async function getAuditLogEntriesByEntityDetail( + ctx: AuditLogContext, + input: z.infer, +) { + const entries = await getAuditEntriesByEntity(ctx.db, input); + return { + entityType: input.entityType, + entityId: input.entityId, + entityName: entries[0]?.entityName ?? null, + itemCount: entries.length, + items: entries.map(formatAuditDetailEntry), + }; +} + +export async function getAuditLogTimeline( + ctx: AuditLogContext, + input: z.infer, +) { + return getAuditTimeline(ctx.db, toAuditTimelineInput({ + startDate: input.startDate, + endDate: input.endDate, + limit: input.limit, + })); +} + +export async function getAuditLogTimelineDetail( + ctx: AuditLogContext, + input: z.infer, +) { + const timeline = await getAuditLogTimeline(ctx, input); + return Object.fromEntries( + Object.entries(timeline).map(([dateKey, entries]) => [ + dateKey, + entries.map(formatAuditDetailEntry), + ]), + ); +} + +export async function getAuditLogActivitySummary( + ctx: AuditLogContext, + input: z.infer, +) { + return getAuditActivitySummary(ctx.db, input); +} diff --git a/packages/api/src/router/audit-log.ts b/packages/api/src/router/audit-log.ts index 4810064..d440808 100644 --- a/packages/api/src/router/audit-log.ts +++ b/packages/api/src/router/audit-log.ts @@ -1,145 +1,55 @@ -import { z } from "zod"; import { createTRPCRouter, controllerProcedure } from "../trpc.js"; import { + auditLogActivitySummaryInputSchema, auditLogByEntityInputSchema, + auditLogEntryByIdInputSchema, auditLogListInputSchema, auditLogTimelineInputSchema, -} from "./audit-log-inputs.js"; -import { - formatAuditDetailEntry, - formatAuditListEntry, - getAuditActivitySummary, - getAuditEntriesByEntity, - getAuditEntryById, - getAuditTimeline, - listAuditEntries, - toAuditListInput, - toAuditTimelineInput, -} from "./audit-log-support.js"; - -// ─── Router ─────────────────────────────────────────────────────────────────── + getAuditLogActivitySummary, + getAuditLogEntriesByEntity, + getAuditLogEntriesByEntityDetail, + getAuditLogEntryById, + getAuditLogEntryByIdDetail, + getAuditLogTimeline, + getAuditLogTimelineDetail, + listAuditLogEntries, + listAuditLogEntriesDetail, +} from "./audit-log-procedure-support.js"; export const auditLogRouter = createTRPCRouter({ - /** - * Paginated, filterable list of audit log entries. - * Cursor-based pagination using createdAt + id. - */ list: controllerProcedure .input(auditLogListInputSchema) - .query(async ({ ctx, input }) => { - return listAuditEntries(ctx.db, toAuditListInput({ - entityType: input.entityType, - entityId: input.entityId, - userId: input.userId, - action: input.action, - source: input.source, - startDate: input.startDate, - endDate: input.endDate, - search: input.search, - limit: input.limit, - cursor: input.cursor, - })); - }), + .query(({ ctx, input }) => listAuditLogEntries(ctx, input)), listDetail: controllerProcedure .input(auditLogListInputSchema) - .query(async ({ ctx, input }) => { - const result = await listAuditEntries(ctx.db, toAuditListInput({ - entityType: input.entityType, - entityId: input.entityId, - userId: input.userId, - action: input.action, - source: input.source, - startDate: input.startDate, - endDate: input.endDate, - search: input.search, - limit: input.limit, - cursor: input.cursor, - })); - return { - items: result.items.map(formatAuditListEntry), - nextCursor: result.nextCursor ?? null, - }; - }), + .query(({ ctx, input }) => listAuditLogEntriesDetail(ctx, input)), - /** - * Get a single audit entry with full changes JSONB (for expand/detail view). - */ getById: controllerProcedure - .input(z.object({ id: z.string() })) - .query(async ({ ctx, input }) => { - return getAuditEntryById(ctx.db, input.id); - }), + .input(auditLogEntryByIdInputSchema) + .query(({ ctx, input }) => getAuditLogEntryById(ctx, input)), getByIdDetail: controllerProcedure - .input(z.object({ id: z.string() })) - .query(async ({ ctx, input }) => { - const entry = await getAuditEntryById(ctx.db, input.id); - return formatAuditDetailEntry(entry); - }), + .input(auditLogEntryByIdInputSchema) + .query(({ ctx, input }) => getAuditLogEntryByIdDetail(ctx, input)), - /** - * Get all audit entries for a specific entity (e.g. a project or resource). - */ getByEntity: controllerProcedure .input(auditLogByEntityInputSchema) - .query(async ({ ctx, input }) => { - return getAuditEntriesByEntity(ctx.db, input); - }), + .query(({ ctx, input }) => getAuditLogEntriesByEntity(ctx, input)), getByEntityDetail: controllerProcedure .input(auditLogByEntityInputSchema) - .query(async ({ ctx, input }) => { - const entries = await getAuditEntriesByEntity(ctx.db, input); - return { - entityType: input.entityType, - entityId: input.entityId, - entityName: entries[0]?.entityName ?? null, - itemCount: entries.length, - items: entries.map(formatAuditDetailEntry), - }; - }), + .query(({ ctx, input }) => getAuditLogEntriesByEntityDetail(ctx, input)), - /** - * Timeline view: entries grouped by date (YYYY-MM-DD). - */ getTimeline: controllerProcedure .input(auditLogTimelineInputSchema) - .query(async ({ ctx, input }) => { - return getAuditTimeline(ctx.db, toAuditTimelineInput({ - startDate: input.startDate, - endDate: input.endDate, - limit: input.limit, - })); - }), + .query(({ ctx, input }) => getAuditLogTimeline(ctx, input)), getTimelineDetail: controllerProcedure .input(auditLogTimelineInputSchema) - .query(async ({ ctx, input }) => { - const timeline = await getAuditTimeline(ctx.db, toAuditTimelineInput({ - startDate: input.startDate, - endDate: input.endDate, - limit: input.limit, - })); - return Object.fromEntries( - Object.entries(timeline).map(([dateKey, entries]) => [ - dateKey, - entries.map(formatAuditDetailEntry), - ]), - ); - }), + .query(({ ctx, input }) => getAuditLogTimelineDetail(ctx, input)), - /** - * Activity summary: counts by entity type, action, and user for a date range. - */ getActivitySummary: controllerProcedure - .input( - z.object({ - startDate: z.date().optional(), - endDate: z.date().optional(), - }), - ) - .query(async ({ ctx, input }) => { - return getAuditActivitySummary(ctx.db, input); - }), + .input(auditLogActivitySummaryInputSchema) + .query(({ ctx, input }) => getAuditLogActivitySummary(ctx, input)), });