import { initTRPC, TRPCError } from '@trpc/server' import superjson from 'superjson' import { ZodError } from 'zod' import { type Context } from './context' const t = initTRPC.context().create({ transformer: superjson, errorFormatter({ shape, error }) { return { ...shape, data: { ...shape.data, zodError: error.cause instanceof ZodError ? error.cause.flatten() : null, }, } }, }) export const router = t.router export const publicProcedure = t.procedure export const createCallerFactory = t.createCallerFactory /** * Protected: user must be authenticated */ export const protectedProcedure = t.procedure.use(({ ctx, next }) => { if (!ctx.session?.user) { throw new TRPCError({ code: 'UNAUTHORIZED' }) } return next({ ctx: { ...ctx, session: { ...ctx.session, user: ctx.session.user }, }, }) }) /** * Member: user must belong to an organization * Adds orgId + role to context */ export const memberProcedure = protectedProcedure.use(async ({ ctx, next }) => { const userRole = await ctx.prisma.userRole.findFirst({ where: { userId: ctx.session.user.id }, }) if (!userRole) { throw new TRPCError({ code: 'FORBIDDEN', message: 'You are not a member of any organization.', }) } return next({ ctx: { ...ctx, orgId: userRole.orgId, role: userRole.role, }, }) }) /** * Admin: user must be an admin of their organization */ export const adminProcedure = protectedProcedure.use(async ({ ctx, next }) => { const userRole = await ctx.prisma.userRole.findFirst({ where: { userId: ctx.session.user.id, role: 'admin' }, }) if (!userRole) { throw new TRPCError({ code: 'FORBIDDEN', message: 'Admin access required.', }) } return next({ ctx: { ...ctx, orgId: userRole.orgId, role: 'admin' as const, }, }) })