Fix FK constraint error on filtering override — verify user exists
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
The overriddenBy FK to User was failing when the session contained a stale user ID (e.g. after database reseed). Added ensureUserExists() guard to all override/reinstate mutations and switched single-record updates to use Prisma connect syntax for safer FK resolution. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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<string> {
|
||||
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: {
|
||||
|
||||
Reference in New Issue
Block a user