fix: security hardening + performance refactoring (code review batch 1)

- IDOR fix: deliberation vote now verifies juryMemberId === ctx.user.id
- Rate limiting: tRPC middleware (100/min), AI endpoints (5/hr), auth IP-based (10/15min)
- 6 compound indexes added to Prisma schema
- N+1 eliminated in processRoundClose (batch updateMany/createMany)
- N+1 eliminated in batchCheckRequirementsAndTransition (3 batch queries)
- Service extraction: juror-reassignment.ts (578 lines)
- Dead code removed: award.ts, cohort.ts, decision.ts (680 lines)
- 35 bare catch blocks replaced across 16 files
- Fire-and-forget async calls fixed
- Notification false positive bug fixed

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-07 16:18:24 +01:00
parent a8b8643936
commit b85a9b9a7b
32 changed files with 1032 additions and 1355 deletions

View File

@@ -768,6 +768,7 @@ model Assignment {
@@index([isCompleted])
@@index([projectId, userId])
@@index([juryGroupId])
@@index([roundId, isCompleted])
}
model Evaluation {
@@ -964,6 +965,7 @@ model NotificationLog {
@@index([projectId])
@@index([batchId])
@@index([email])
@@index([type, status])
}
// =============================================================================
@@ -1494,6 +1496,7 @@ model RankingSnapshot {
@@index([roundId])
@@index([triggeredById])
@@index([createdAt])
@@index([roundId, createdAt])
}
// Tracks progress of long-running AI tagging jobs
@@ -1740,6 +1743,8 @@ model ConflictOfInterest {
@@index([userId])
@@index([hasConflict])
@@index([projectId])
@@index([userId, hasConflict])
}
// =============================================================================
@@ -2283,6 +2288,7 @@ model ProjectRoundState {
@@index([projectId])
@@index([roundId])
@@index([state])
@@index([roundId, state])
}
model AdvancementRule {