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,
|
NotificationTypes,
|
||||||
} from '../services/in-app-notification'
|
} 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.
|
* Extract a numeric confidence/quality score from aiScreeningJson.
|
||||||
* Looks for common keys: overallScore, confidenceScore, score, qualityScore.
|
* Looks for common keys: overallScore, confidenceScore, score, qualityScore.
|
||||||
@@ -908,18 +926,20 @@ export const filteringRouter = router({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
|
const verifiedUserId = await ensureUserExists(ctx.prisma, ctx.user.id)
|
||||||
|
|
||||||
const result = await ctx.prisma.filteringResult.update({
|
const result = await ctx.prisma.filteringResult.update({
|
||||||
where: { id: input.id },
|
where: { id: input.id },
|
||||||
data: {
|
data: {
|
||||||
finalOutcome: input.finalOutcome,
|
finalOutcome: input.finalOutcome,
|
||||||
overriddenBy: ctx.user.id,
|
overriddenByUser: { connect: { id: verifiedUserId } },
|
||||||
overriddenAt: new Date(),
|
overriddenAt: new Date(),
|
||||||
overrideReason: input.reason,
|
overrideReason: input.reason,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
await logAudit({
|
await logAudit({
|
||||||
userId: ctx.user.id,
|
userId: verifiedUserId,
|
||||||
action: 'UPDATE',
|
action: 'UPDATE',
|
||||||
entityType: 'FilteringResult',
|
entityType: 'FilteringResult',
|
||||||
entityId: input.id,
|
entityId: input.id,
|
||||||
@@ -946,18 +966,20 @@ export const filteringRouter = router({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
|
const verifiedUserId = await ensureUserExists(ctx.prisma, ctx.user.id)
|
||||||
|
|
||||||
await ctx.prisma.filteringResult.updateMany({
|
await ctx.prisma.filteringResult.updateMany({
|
||||||
where: { id: { in: input.ids } },
|
where: { id: { in: input.ids } },
|
||||||
data: {
|
data: {
|
||||||
finalOutcome: input.finalOutcome,
|
finalOutcome: input.finalOutcome,
|
||||||
overriddenBy: ctx.user.id,
|
overriddenBy: verifiedUserId,
|
||||||
overriddenAt: new Date(),
|
overriddenAt: new Date(),
|
||||||
overrideReason: input.reason,
|
overrideReason: input.reason,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
await logAudit({
|
await logAudit({
|
||||||
userId: ctx.user.id,
|
userId: verifiedUserId,
|
||||||
action: 'BULK_UPDATE_STATUS',
|
action: 'BULK_UPDATE_STATUS',
|
||||||
entityType: 'FilteringResult',
|
entityType: 'FilteringResult',
|
||||||
detailsJson: {
|
detailsJson: {
|
||||||
@@ -983,6 +1005,8 @@ export const filteringRouter = router({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
|
const verifiedUserId = await ensureUserExists(ctx.prisma, ctx.user.id)
|
||||||
|
|
||||||
const currentRound = await ctx.prisma.round.findUniqueOrThrow({
|
const currentRound = await ctx.prisma.round.findUniqueOrThrow({
|
||||||
where: { id: input.roundId },
|
where: { id: input.roundId },
|
||||||
select: { id: true, competitionId: true, sortOrder: true, name: true },
|
select: { id: true, competitionId: true, sortOrder: true, name: true },
|
||||||
@@ -1101,7 +1125,7 @@ export const filteringRouter = router({
|
|||||||
where: { id: { in: demotedIds } },
|
where: { id: { in: demotedIds } },
|
||||||
data: {
|
data: {
|
||||||
finalOutcome: 'FLAGGED',
|
finalOutcome: 'FLAGGED',
|
||||||
overriddenBy: ctx.user.id,
|
overriddenBy: verifiedUserId,
|
||||||
overriddenAt: new Date(),
|
overriddenAt: new Date(),
|
||||||
overrideReason: 'Demoted by category target enforcement',
|
overrideReason: 'Demoted by category target enforcement',
|
||||||
},
|
},
|
||||||
@@ -1112,7 +1136,7 @@ export const filteringRouter = router({
|
|||||||
await ctx.prisma.$transaction(operations)
|
await ctx.prisma.$transaction(operations)
|
||||||
|
|
||||||
await logAudit({
|
await logAudit({
|
||||||
userId: ctx.user.id,
|
userId: verifiedUserId,
|
||||||
action: 'UPDATE',
|
action: 'UPDATE',
|
||||||
entityType: 'Stage',
|
entityType: 'Stage',
|
||||||
entityId: input.roundId,
|
entityId: input.roundId,
|
||||||
@@ -1149,6 +1173,8 @@ export const filteringRouter = router({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
|
const verifiedUserId = await ensureUserExists(ctx.prisma, ctx.user.id)
|
||||||
|
|
||||||
await ctx.prisma.filteringResult.update({
|
await ctx.prisma.filteringResult.update({
|
||||||
where: {
|
where: {
|
||||||
roundId_projectId: {
|
roundId_projectId: {
|
||||||
@@ -1158,7 +1184,7 @@ export const filteringRouter = router({
|
|||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
finalOutcome: 'PASSED',
|
finalOutcome: 'PASSED',
|
||||||
overriddenBy: ctx.user.id,
|
overriddenByUser: { connect: { id: verifiedUserId } },
|
||||||
overriddenAt: new Date(),
|
overriddenAt: new Date(),
|
||||||
overrideReason: 'Reinstated by admin',
|
overrideReason: 'Reinstated by admin',
|
||||||
},
|
},
|
||||||
@@ -1170,7 +1196,7 @@ export const filteringRouter = router({
|
|||||||
})
|
})
|
||||||
|
|
||||||
await logAudit({
|
await logAudit({
|
||||||
userId: ctx.user.id,
|
userId: verifiedUserId,
|
||||||
action: 'UPDATE',
|
action: 'UPDATE',
|
||||||
entityType: 'FilteringResult',
|
entityType: 'FilteringResult',
|
||||||
detailsJson: {
|
detailsJson: {
|
||||||
@@ -1192,6 +1218,8 @@ export const filteringRouter = router({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
|
const verifiedUserId = await ensureUserExists(ctx.prisma, ctx.user.id)
|
||||||
|
|
||||||
await ctx.prisma.$transaction([
|
await ctx.prisma.$transaction([
|
||||||
...input.projectIds.map((projectId) =>
|
...input.projectIds.map((projectId) =>
|
||||||
ctx.prisma.filteringResult.update({
|
ctx.prisma.filteringResult.update({
|
||||||
@@ -1203,7 +1231,7 @@ export const filteringRouter = router({
|
|||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
finalOutcome: 'PASSED',
|
finalOutcome: 'PASSED',
|
||||||
overriddenBy: ctx.user.id,
|
overriddenByUser: { connect: { id: verifiedUserId } },
|
||||||
overriddenAt: new Date(),
|
overriddenAt: new Date(),
|
||||||
overrideReason: 'Bulk reinstated by admin',
|
overrideReason: 'Bulk reinstated by admin',
|
||||||
},
|
},
|
||||||
@@ -1216,7 +1244,7 @@ export const filteringRouter = router({
|
|||||||
])
|
])
|
||||||
|
|
||||||
await logAudit({
|
await logAudit({
|
||||||
userId: ctx.user.id,
|
userId: verifiedUserId,
|
||||||
action: 'BULK_UPDATE_STATUS',
|
action: 'BULK_UPDATE_STATUS',
|
||||||
entityType: 'FilteringResult',
|
entityType: 'FilteringResult',
|
||||||
detailsJson: {
|
detailsJson: {
|
||||||
|
|||||||
Reference in New Issue
Block a user