import { initTRPC, TRPCError } from '@trpc/server'; import { z } from 'zod'; import { eq } from 'drizzle-orm'; import { projects } from '../../src/db/schema'; import type { TRPCContext } from './types'; // Initialize tRPC with context const t = initTRPC.context().create(); // Middleware for authentication const isAuthenticated = t.middleware(({ ctx, next }) => { if (!ctx.userId) { throw new TRPCError({ code: 'UNAUTHORIZED', message: 'User not authenticated' }); } return next({ ctx: { ...ctx, userId: ctx.userId } }); }); // Middleware for database access const hasDb = t.middleware(({ ctx, next }) => { if (!ctx.db) { throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Database not available' }); } return next({ ctx: { ...ctx, db: ctx.db } }); }); // Middleware for project ownership verification const hasProjectAccess = t.middleware(async ({ ctx, next }) => { if (!ctx.projectId) { throw new TRPCError({ code: 'FORBIDDEN', message: 'Project access required' }); } if (!ctx.userId) { throw new TRPCError({ code: 'UNAUTHORIZED', message: 'User not authenticated' }); } if (!ctx.db) { throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Database not available' }); } const rows = await ctx.db.select({ id: projects.id, ownerId: projects.ownerId }) .from(projects) .where(eq(projects.id, ctx.projectId)); const project = rows[0]; if (!project) { throw new TRPCError({ code: 'NOT_FOUND', message: `Project ${ctx.projectId} not found` }); } if (project.ownerId !== ctx.userId) { throw new TRPCError({ code: 'FORBIDDEN', message: `You do not have access to project ${ctx.projectId}` }); } return next({ ctx: { ...ctx, projectId: ctx.projectId } }); }); // Base router export const baseRouter = t.router; // Procedure builders export const publicProcedure = t.procedure.use(hasDb); export const protectedProcedure = t.procedure.use(isAuthenticated).use(hasDb); export const projectProcedure = t.procedure .use(isAuthenticated) .use(hasDb) .use(hasProjectAccess); // Validation middleware export const validateInput = (schema: T) => { return t.middleware(({ input, next }) => { const validated = schema.parse(input); return next({ input: validated }); }); }; export { t, TRPCError };