Competition/Round architecture: full platform rewrite (Phases 1-9)
All checks were successful
Build and Push Docker Image / build (push) Successful in 7m45s
All checks were successful
Build and Push Docker Image / build (push) Successful in 7m45s
Replace Pipeline/Stage system with Competition/Round architecture. New schema: Competition, Round (7 types), JuryGroup, AssignmentPolicy, ProjectRoundState, DeliberationSession, ResultLock, SubmissionWindow. New services: round-engine, round-assignment, deliberation, result-lock, submission-manager, competition-context, ai-prompt-guard. Full admin/jury/applicant/mentor UI rewrite. AI prompt hardening with structured prompts, retry logic, and injection detection. All legacy pipeline/stage code removed. 4 new migrations + seed aligned. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { z } from 'zod'
|
||||
import { TRPCError } from '@trpc/server'
|
||||
import { router, mentorProcedure, adminProcedure } from '../trpc'
|
||||
import { router, mentorProcedure, adminProcedure, protectedProcedure } from '../trpc'
|
||||
import { MentorAssignmentMethod } from '@prisma/client'
|
||||
import {
|
||||
getAIMentorSuggestions,
|
||||
@@ -12,6 +12,15 @@ import {
|
||||
NotificationTypes,
|
||||
} from '../services/in-app-notification'
|
||||
import { logAudit } from '@/server/utils/audit'
|
||||
import {
|
||||
activateWorkspace,
|
||||
sendMessage as workspaceSendMessage,
|
||||
getMessages as workspaceGetMessages,
|
||||
markRead as workspaceMarkRead,
|
||||
uploadFile as workspaceUploadFile,
|
||||
addFileComment as workspaceAddFileComment,
|
||||
promoteFile as workspacePromoteFile,
|
||||
} from '../services/mentor-workspace'
|
||||
|
||||
export const mentorRouter = router({
|
||||
/**
|
||||
@@ -1284,4 +1293,150 @@ export const mentorRouter = router({
|
||||
|
||||
return Array.from(mentorStats.values())
|
||||
}),
|
||||
|
||||
// =========================================================================
|
||||
// Workspace Procedures (Phase 4)
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Activate a mentor workspace
|
||||
*/
|
||||
activateWorkspace: adminProcedure
|
||||
.input(z.object({ mentorAssignmentId: z.string() }))
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const result = await activateWorkspace(input.mentorAssignmentId, ctx.user.id, ctx.prisma)
|
||||
if (!result.success) {
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: result.errors?.join('; ') ?? 'Failed to activate workspace',
|
||||
})
|
||||
}
|
||||
return result
|
||||
}),
|
||||
|
||||
/**
|
||||
* Send a message in a mentor workspace
|
||||
*/
|
||||
workspaceSendMessage: mentorProcedure
|
||||
.input(
|
||||
z.object({
|
||||
mentorAssignmentId: z.string(),
|
||||
message: z.string().min(1).max(5000),
|
||||
role: z.enum(['MENTOR_ROLE', 'APPLICANT_ROLE', 'ADMIN_ROLE']),
|
||||
})
|
||||
)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
return workspaceSendMessage(
|
||||
{
|
||||
mentorAssignmentId: input.mentorAssignmentId,
|
||||
senderId: ctx.user.id,
|
||||
message: input.message,
|
||||
role: input.role,
|
||||
},
|
||||
ctx.prisma,
|
||||
)
|
||||
}),
|
||||
|
||||
/**
|
||||
* Get workspace messages
|
||||
*/
|
||||
workspaceGetMessages: mentorProcedure
|
||||
.input(z.object({ mentorAssignmentId: z.string() }))
|
||||
.query(async ({ ctx, input }) => {
|
||||
return workspaceGetMessages(input.mentorAssignmentId, ctx.prisma)
|
||||
}),
|
||||
|
||||
/**
|
||||
* Mark a workspace message as read
|
||||
*/
|
||||
workspaceMarkRead: mentorProcedure
|
||||
.input(z.object({ messageId: z.string() }))
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
await workspaceMarkRead(input.messageId, ctx.prisma)
|
||||
return { success: true }
|
||||
}),
|
||||
|
||||
/**
|
||||
* Upload a file to a workspace
|
||||
*/
|
||||
workspaceUploadFile: mentorProcedure
|
||||
.input(
|
||||
z.object({
|
||||
mentorAssignmentId: z.string(),
|
||||
fileName: z.string().min(1).max(255),
|
||||
mimeType: z.string(),
|
||||
size: z.number().int().min(0),
|
||||
bucket: z.string(),
|
||||
objectKey: z.string(),
|
||||
description: z.string().max(2000).optional(),
|
||||
})
|
||||
)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
return workspaceUploadFile(
|
||||
{
|
||||
mentorAssignmentId: input.mentorAssignmentId,
|
||||
uploadedByUserId: ctx.user.id,
|
||||
fileName: input.fileName,
|
||||
mimeType: input.mimeType,
|
||||
size: input.size,
|
||||
bucket: input.bucket,
|
||||
objectKey: input.objectKey,
|
||||
description: input.description,
|
||||
},
|
||||
ctx.prisma,
|
||||
)
|
||||
}),
|
||||
|
||||
/**
|
||||
* Add a comment to a workspace file
|
||||
*/
|
||||
workspaceAddFileComment: mentorProcedure
|
||||
.input(
|
||||
z.object({
|
||||
mentorFileId: z.string(),
|
||||
content: z.string().min(1).max(5000),
|
||||
parentCommentId: z.string().optional(),
|
||||
})
|
||||
)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
return workspaceAddFileComment(
|
||||
{
|
||||
mentorFileId: input.mentorFileId,
|
||||
authorId: ctx.user.id,
|
||||
content: input.content,
|
||||
parentCommentId: input.parentCommentId,
|
||||
},
|
||||
ctx.prisma,
|
||||
)
|
||||
}),
|
||||
|
||||
/**
|
||||
* Promote a workspace file to official submission
|
||||
*/
|
||||
workspacePromoteFile: adminProcedure
|
||||
.input(
|
||||
z.object({
|
||||
mentorFileId: z.string(),
|
||||
roundId: z.string(),
|
||||
slotKey: z.string(),
|
||||
})
|
||||
)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const result = await workspacePromoteFile(
|
||||
{
|
||||
mentorFileId: input.mentorFileId,
|
||||
roundId: input.roundId,
|
||||
slotKey: input.slotKey,
|
||||
promotedById: ctx.user.id,
|
||||
},
|
||||
ctx.prisma,
|
||||
)
|
||||
if (!result.success) {
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: result.errors?.join('; ') ?? 'Failed to promote file',
|
||||
})
|
||||
}
|
||||
return result
|
||||
}),
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user