import { TRPCError } from "@trpc/server"; import { t } from "@repo/core-shared/trpc/init"; import { defineErrorMiddleware } from "@repo/core-shared/trpc/define-error-middleware"; /** * The minimum user shape that the adminOnly middleware expects to find on `ctx`. * Apps must include this field when creating their tRPC context for requests * that may reach admin procedures. Unauthenticated requests leave `user` * undefined, which the middleware treats as non-admin. */ export type AdminTrpcUser = { roles: string[]; }; /** * Middleware that blocks non-admin callers. * * Reads `ctx.user?.roles` from the tRPC context. Throws FORBIDDEN if the * user is absent or lacks the "admin" role. Apps that mount the auditRouter * must set `ctx.user` with the authenticated user's roles. */ const adminOnly = t.middleware(({ ctx, next }) => { const user = (ctx as { user?: AdminTrpcUser }).user; if (!user?.roles.includes("admin")) { throw new TRPCError({ code: "FORBIDDEN", message: "Admin role required", }); } return next({ ctx: { ...ctx, user } }); }); /** * Base procedure for all audit admin routes. * * - `adminOnly` middleware gates every mutation/query. * - `defineErrorMiddleware([])` — no audit-specific domain errors need tRPC * mapping; the FORBIDDEN thrown by `adminOnly` is a plain TRPCError and * propagates unchanged. */ export const auditProcedure = t.procedure .use(adminOnly) .use(defineErrorMiddleware([]));