diff --git a/src/server/routers/filtering.ts b/src/server/routers/filtering.ts index 43bd179..fdb4ce8 100644 --- a/src/server/routers/filtering.ts +++ b/src/server/routers/filtering.ts @@ -12,6 +12,24 @@ import { NotificationTypes, } from '../services/in-app-notification' +/** + * Verify the current session user exists in the database. + * Guards against stale JWT sessions (e.g., after database reseed). + */ +async function ensureUserExists(db: PrismaClient, userId: string): Promise { + const user = await db.user.findUnique({ + where: { id: userId }, + select: { id: true }, + }) + if (!user) { + throw new TRPCError({ + code: 'UNAUTHORIZED', + message: 'Your session refers to a user that no longer exists. Please log out and log back in.', + }) + } + return user.id +} + /** * Extract a numeric confidence/quality score from aiScreeningJson. * Looks for common keys: overallScore, confidenceScore, score, qualityScore. @@ -908,18 +926,20 @@ export const filteringRouter = router({ }) ) .mutation(async ({ ctx, input }) => { + const verifiedUserId = await ensureUserExists(ctx.prisma, ctx.user.id) + const result = await ctx.prisma.filteringResult.update({ where: { id: input.id }, data: { finalOutcome: input.finalOutcome, - overriddenBy: ctx.user.id, + overriddenByUser: { connect: { id: verifiedUserId } }, overriddenAt: new Date(), overrideReason: input.reason, }, }) await logAudit({ - userId: ctx.user.id, + userId: verifiedUserId, action: 'UPDATE', entityType: 'FilteringResult', entityId: input.id, @@ -946,18 +966,20 @@ export const filteringRouter = router({ }) ) .mutation(async ({ ctx, input }) => { + const verifiedUserId = await ensureUserExists(ctx.prisma, ctx.user.id) + await ctx.prisma.filteringResult.updateMany({ where: { id: { in: input.ids } }, data: { finalOutcome: input.finalOutcome, - overriddenBy: ctx.user.id, + overriddenBy: verifiedUserId, overriddenAt: new Date(), overrideReason: input.reason, }, }) await logAudit({ - userId: ctx.user.id, + userId: verifiedUserId, action: 'BULK_UPDATE_STATUS', entityType: 'FilteringResult', detailsJson: { @@ -983,6 +1005,8 @@ export const filteringRouter = router({ }) ) .mutation(async ({ ctx, input }) => { + const verifiedUserId = await ensureUserExists(ctx.prisma, ctx.user.id) + const currentRound = await ctx.prisma.round.findUniqueOrThrow({ where: { id: input.roundId }, select: { id: true, competitionId: true, sortOrder: true, name: true }, @@ -1101,7 +1125,7 @@ export const filteringRouter = router({ where: { id: { in: demotedIds } }, data: { finalOutcome: 'FLAGGED', - overriddenBy: ctx.user.id, + overriddenBy: verifiedUserId, overriddenAt: new Date(), overrideReason: 'Demoted by category target enforcement', }, @@ -1112,7 +1136,7 @@ export const filteringRouter = router({ await ctx.prisma.$transaction(operations) await logAudit({ - userId: ctx.user.id, + userId: verifiedUserId, action: 'UPDATE', entityType: 'Stage', entityId: input.roundId, @@ -1149,6 +1173,8 @@ export const filteringRouter = router({ }) ) .mutation(async ({ ctx, input }) => { + const verifiedUserId = await ensureUserExists(ctx.prisma, ctx.user.id) + await ctx.prisma.filteringResult.update({ where: { roundId_projectId: { @@ -1158,7 +1184,7 @@ export const filteringRouter = router({ }, data: { finalOutcome: 'PASSED', - overriddenBy: ctx.user.id, + overriddenByUser: { connect: { id: verifiedUserId } }, overriddenAt: new Date(), overrideReason: 'Reinstated by admin', }, @@ -1170,7 +1196,7 @@ export const filteringRouter = router({ }) await logAudit({ - userId: ctx.user.id, + userId: verifiedUserId, action: 'UPDATE', entityType: 'FilteringResult', detailsJson: { @@ -1192,6 +1218,8 @@ export const filteringRouter = router({ }) ) .mutation(async ({ ctx, input }) => { + const verifiedUserId = await ensureUserExists(ctx.prisma, ctx.user.id) + await ctx.prisma.$transaction([ ...input.projectIds.map((projectId) => ctx.prisma.filteringResult.update({ @@ -1203,7 +1231,7 @@ export const filteringRouter = router({ }, data: { finalOutcome: 'PASSED', - overriddenBy: ctx.user.id, + overriddenByUser: { connect: { id: verifiedUserId } }, overriddenAt: new Date(), overrideReason: 'Bulk reinstated by admin', }, @@ -1216,7 +1244,7 @@ export const filteringRouter = router({ ]) await logAudit({ - userId: ctx.user.id, + userId: verifiedUserId, action: 'BULK_UPDATE_STATUS', entityType: 'FilteringResult', detailsJson: {