fix: tech debt batch 1 — TS errors, vulnerabilities, dead code

- Fixed 12 TypeScript errors across analytics.ts, observer-project-detail.tsx, bulk-upload/page.tsx, settings/profile/page.tsx
- npm audit: 8 vulnerabilities resolved (1 critical, 4 high, 3 moderate)
- Deleted 3 dead files: live-control.ts (618 lines), feature-flags.ts, file-type-categories.ts
- Removed typescript.ignoreBuildErrors: true — TS errors now block builds

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-07 23:51:44 +01:00
parent 1ebdf5f9c9
commit 1356809cb1
9 changed files with 149 additions and 842 deletions

View File

@@ -1379,11 +1379,10 @@ export const analyticsRouter = router({
bucket: true, objectKey: true, pageCount: true, textPreview: true,
detectedLang: true, langConfidence: true, analyzedAt: true,
roundId: true,
round: { select: { id: true, name: true, roundType: true, sortOrder: true } },
requirementId: true,
requirement: { select: { id: true, name: true, description: true, isRequired: true } },
},
orderBy: [{ round: { sortOrder: 'asc' } }, { createdAt: 'asc' }],
orderBy: [{ createdAt: 'asc' }],
},
teamMembers: {
include: {

View File

@@ -1,618 +0,0 @@
/**
* Live Control Service
*
* Manages real-time control of live final events within a round.
* Handles session management, project cursor navigation, queue reordering,
* pause/resume, and cohort voting windows.
*
* The LiveProgressCursor tracks the current position in a live presentation
* sequence. Cohorts group projects for voting with configurable windows.
*/
import type { PrismaClient } from '@prisma/client'
import { logAudit } from '@/server/utils/audit'
// ─── Types ──────────────────────────────────────────────────────────────────
export interface SessionResult {
success: boolean
sessionId: string | null
cursorId: string | null
errors?: string[]
}
export interface CursorState {
roundId: string
sessionId: string
activeProjectId: string | null
activeOrderIndex: number
isPaused: boolean
totalProjects: number
}
// ─── Helpers ────────────────────────────────────────────────────────────────
function generateSessionId(): string {
const timestamp = Date.now().toString(36)
const random = Math.random().toString(36).substring(2, 8)
return `live-${timestamp}-${random}`
}
// ─── Start Session ──────────────────────────────────────────────────────────
/**
* Create or reset a LiveProgressCursor for a round. If a cursor already exists,
* it is reset to the beginning. A new sessionId is always generated.
*/
export async function startSession(
roundId: string,
actorId: string,
prisma: PrismaClient | any
): Promise<SessionResult> {
try {
// Verify round exists and is a LIVE_FINAL type
const round = await prisma.round.findUnique({
where: { id: roundId },
})
if (!round) {
return {
success: false,
sessionId: null,
cursorId: null,
errors: [`Round ${roundId} not found`],
}
}
if (round.roundType !== 'LIVE_FINAL') {
return {
success: false,
sessionId: null,
cursorId: null,
errors: [
`Round "${round.name}" is type ${round.roundType}, expected LIVE_FINAL`,
],
}
}
// Find the first project in the first cohort
const firstCohortProject = await prisma.cohortProject.findFirst({
where: {
cohort: { roundId },
},
orderBy: { sortOrder: 'asc' as const },
select: { projectId: true },
})
const sessionId = generateSessionId()
// Upsert the cursor (one per round)
const cursor = await prisma.liveProgressCursor.upsert({
where: { roundId },
create: {
roundId,
sessionId,
activeProjectId: firstCohortProject?.projectId ?? null,
activeOrderIndex: 0,
isPaused: false,
},
update: {
sessionId,
activeProjectId: firstCohortProject?.projectId ?? null,
activeOrderIndex: 0,
isPaused: false,
},
})
// Decision audit log
await prisma.decisionAuditLog.create({
data: {
eventType: 'live.session_started',
entityType: 'LiveProgressCursor',
entityId: cursor.id,
actorId,
detailsJson: {
roundId,
sessionId,
firstProjectId: firstCohortProject?.projectId ?? null,
},
},
})
await logAudit({
prisma,
userId: actorId,
action: 'LIVE_SESSION_STARTED',
entityType: 'LiveProgressCursor',
entityId: cursor.id,
detailsJson: { roundId, sessionId },
})
return {
success: true,
sessionId,
cursorId: cursor.id,
}
} catch (error) {
console.error('[LiveControl] Failed to start session:', error)
return {
success: false,
sessionId: null,
cursorId: null,
errors: [
error instanceof Error ? error.message : 'Failed to start live session',
],
}
}
}
// ─── Set Active Project ─────────────────────────────────────────────────────
/**
* Set the currently active project in the live session.
* Validates that the project belongs to a cohort in this round and performs
* a version check on the cursor's sessionId to prevent stale updates.
*/
export async function setActiveProject(
roundId: string,
projectId: string,
actorId: string,
prisma: PrismaClient | any
): Promise<{ success: boolean; errors?: string[] }> {
try {
// Verify cursor exists
const cursor = await prisma.liveProgressCursor.findUnique({
where: { roundId },
})
if (!cursor) {
return {
success: false,
errors: ['No live session found for this round. Start a session first.'],
}
}
// Verify project is in a cohort for this round
const cohortProject = await prisma.cohortProject.findFirst({
where: {
projectId,
cohort: { roundId },
},
select: { id: true, sortOrder: true },
})
if (!cohortProject) {
return {
success: false,
errors: [
`Project ${projectId} is not in any cohort for round ${roundId}`,
],
}
}
// Update cursor
await prisma.liveProgressCursor.update({
where: { roundId },
data: {
activeProjectId: projectId,
activeOrderIndex: cohortProject.sortOrder,
},
})
// Audit
await prisma.decisionAuditLog.create({
data: {
eventType: 'live.cursor_updated',
entityType: 'LiveProgressCursor',
entityId: cursor.id,
actorId,
detailsJson: {
roundId,
projectId,
orderIndex: cohortProject.sortOrder,
action: 'setActiveProject',
},
},
})
await logAudit({
prisma,
userId: actorId,
action: 'LIVE_SET_ACTIVE_PROJECT',
entityType: 'LiveProgressCursor',
entityId: cursor.id,
detailsJson: { projectId, orderIndex: cohortProject.sortOrder },
})
return { success: true }
} catch (error) {
console.error('[LiveControl] Failed to set active project:', error)
return {
success: false,
errors: [
error instanceof Error
? error.message
: 'Failed to set active project',
],
}
}
}
// ─── Jump to Project ────────────────────────────────────────────────────────
/**
* Jump to a project by its order index in the cohort queue.
*/
export async function jumpToProject(
roundId: string,
orderIndex: number,
actorId: string,
prisma: PrismaClient | any
): Promise<{ success: boolean; projectId?: string; errors?: string[] }> {
try {
const cursor = await prisma.liveProgressCursor.findUnique({
where: { roundId },
})
if (!cursor) {
return {
success: false,
errors: ['No live session found for this round'],
}
}
// Find the CohortProject at the given sort order
const cohortProject = await prisma.cohortProject.findFirst({
where: {
cohort: { roundId },
sortOrder: orderIndex,
},
select: { projectId: true, sortOrder: true },
})
if (!cohortProject) {
return {
success: false,
errors: [`No project found at order index ${orderIndex}`],
}
}
// Update cursor
await prisma.liveProgressCursor.update({
where: { roundId },
data: {
activeProjectId: cohortProject.projectId,
activeOrderIndex: orderIndex,
},
})
await prisma.decisionAuditLog.create({
data: {
eventType: 'live.cursor_updated',
entityType: 'LiveProgressCursor',
entityId: cursor.id,
actorId,
detailsJson: {
roundId,
projectId: cohortProject.projectId,
orderIndex,
action: 'jumpToProject',
},
},
})
await logAudit({
prisma,
userId: actorId,
action: 'LIVE_JUMP_TO_PROJECT',
entityType: 'LiveProgressCursor',
entityId: cursor.id,
detailsJson: { orderIndex, projectId: cohortProject.projectId },
})
return { success: true, projectId: cohortProject.projectId }
} catch (error) {
console.error('[LiveControl] Failed to jump to project:', error)
return {
success: false,
errors: [
error instanceof Error ? error.message : 'Failed to jump to project',
],
}
}
}
// ─── Reorder Queue ──────────────────────────────────────────────────────────
/**
* Reorder the presentation queue by updating CohortProject sortOrder values.
* newOrder is an array of cohortProjectIds in the desired order.
*/
export async function reorderQueue(
roundId: string,
newOrder: string[],
actorId: string,
prisma: PrismaClient | any
): Promise<{ success: boolean; errors?: string[] }> {
try {
// Verify all provided IDs belong to cohorts in this round
const cohortProjects = await prisma.cohortProject.findMany({
where: {
id: { in: newOrder },
cohort: { roundId },
},
select: { id: true },
})
const validIds = new Set(cohortProjects.map((cp: any) => cp.id))
const invalidIds = newOrder.filter((id) => !validIds.has(id))
if (invalidIds.length > 0) {
return {
success: false,
errors: [
`CohortProject IDs not found in round ${roundId}: ${invalidIds.join(', ')}`,
],
}
}
// Update sortOrder for each item
await prisma.$transaction(
newOrder.map((cohortProjectId, index) =>
prisma.cohortProject.update({
where: { id: cohortProjectId },
data: { sortOrder: index },
})
)
)
const cursor = await prisma.liveProgressCursor.findUnique({
where: { roundId },
})
if (cursor) {
await prisma.decisionAuditLog.create({
data: {
eventType: 'live.queue_reordered',
entityType: 'LiveProgressCursor',
entityId: cursor.id,
actorId,
detailsJson: {
roundId,
newOrderCount: newOrder.length,
},
},
})
}
await logAudit({
prisma,
userId: actorId,
action: 'LIVE_REORDER_QUEUE',
entityType: 'Round',
entityId: roundId,
detailsJson: { reorderedCount: newOrder.length },
})
return { success: true }
} catch (error) {
console.error('[LiveControl] Failed to reorder queue:', error)
return {
success: false,
errors: [
error instanceof Error ? error.message : 'Failed to reorder queue',
],
}
}
}
// ─── Pause / Resume ─────────────────────────────────────────────────────────
/**
* Toggle the pause state of a live session.
*/
export async function pauseResume(
roundId: string,
isPaused: boolean,
actorId: string,
prisma: PrismaClient | any
): Promise<{ success: boolean; errors?: string[] }> {
try {
const cursor = await prisma.liveProgressCursor.findUnique({
where: { roundId },
})
if (!cursor) {
return {
success: false,
errors: ['No live session found for this round'],
}
}
await prisma.liveProgressCursor.update({
where: { roundId },
data: { isPaused },
})
await prisma.decisionAuditLog.create({
data: {
eventType: isPaused ? 'live.session_paused' : 'live.session_resumed',
entityType: 'LiveProgressCursor',
entityId: cursor.id,
actorId,
detailsJson: {
roundId,
isPaused,
sessionId: cursor.sessionId,
},
},
})
await logAudit({
prisma,
userId: actorId,
action: isPaused ? 'LIVE_SESSION_PAUSED' : 'LIVE_SESSION_RESUMED',
entityType: 'LiveProgressCursor',
entityId: cursor.id,
detailsJson: { roundId, isPaused },
})
return { success: true }
} catch (error) {
console.error('[LiveControl] Failed to pause/resume:', error)
return {
success: false,
errors: [
error instanceof Error
? error.message
: 'Failed to toggle pause state',
],
}
}
}
// ─── Cohort Window Management ───────────────────────────────────────────────
/**
* Open a cohort's voting window. Sets isOpen to true and records the
* window open timestamp.
*/
export async function openCohortWindow(
cohortId: string,
actorId: string,
prisma: PrismaClient | any
): Promise<{ success: boolean; errors?: string[] }> {
try {
const cohort = await prisma.cohort.findUnique({
where: { id: cohortId },
})
if (!cohort) {
return { success: false, errors: [`Cohort ${cohortId} not found`] }
}
if (cohort.isOpen) {
return {
success: false,
errors: [`Cohort "${cohort.name}" is already open`],
}
}
const now = new Date()
await prisma.cohort.update({
where: { id: cohortId },
data: {
isOpen: true,
windowOpenAt: now,
},
})
await prisma.decisionAuditLog.create({
data: {
eventType: 'live.cohort_opened',
entityType: 'Cohort',
entityId: cohortId,
actorId,
detailsJson: {
cohortName: cohort.name,
roundId: cohort.roundId,
openedAt: now.toISOString(),
},
},
})
await logAudit({
prisma,
userId: actorId,
action: 'LIVE_COHORT_OPENED',
entityType: 'Cohort',
entityId: cohortId,
detailsJson: { cohortName: cohort.name, roundId: cohort.roundId },
})
return { success: true }
} catch (error) {
console.error('[LiveControl] Failed to open cohort window:', error)
return {
success: false,
errors: [
error instanceof Error
? error.message
: 'Failed to open cohort window',
],
}
}
}
/**
* Close a cohort's voting window. Sets isOpen to false and records the
* window close timestamp.
*/
export async function closeCohortWindow(
cohortId: string,
actorId: string,
prisma: PrismaClient | any
): Promise<{ success: boolean; errors?: string[] }> {
try {
const cohort = await prisma.cohort.findUnique({
where: { id: cohortId },
})
if (!cohort) {
return { success: false, errors: [`Cohort ${cohortId} not found`] }
}
if (!cohort.isOpen) {
return {
success: false,
errors: [`Cohort "${cohort.name}" is already closed`],
}
}
const now = new Date()
await prisma.cohort.update({
where: { id: cohortId },
data: {
isOpen: false,
windowCloseAt: now,
},
})
await prisma.decisionAuditLog.create({
data: {
eventType: 'live.cohort_closed',
entityType: 'Cohort',
entityId: cohortId,
actorId,
detailsJson: {
cohortName: cohort.name,
roundId: cohort.roundId,
closedAt: now.toISOString(),
},
},
})
await logAudit({
prisma,
userId: actorId,
action: 'LIVE_COHORT_CLOSED',
entityType: 'Cohort',
entityId: cohortId,
detailsJson: { cohortName: cohort.name, roundId: cohort.roundId },
})
return { success: true }
} catch (error) {
console.error('[LiveControl] Failed to close cohort window:', error)
return {
success: false,
errors: [
error instanceof Error
? error.message
: 'Failed to close cohort window',
],
}
}
}