From 569fb04a5bcf6701d5729bb998dfb902f4c9c1f0 Mon Sep 17 00:00:00 2001 From: zero1177 Date: Fri, 3 Apr 2026 05:19:52 +0000 Subject: [PATCH 1/6] feat(be): implement polygon tool upload --- .../admin/src/polygon/file/file.service.ts | 49 +++++++++++++++++++ .../apps/admin/src/polygon/polygon.module.ts | 6 ++- .../admin/src/polygon/polygon.resolver.ts | 28 ++++++++++- .../apps/admin/src/polygon/polygon.service.ts | 35 ++++++++++++- apps/backend/libs/amqp/src/amqp.module.ts | 10 ++-- .../libs/constants/src/rabbitmq.constants.ts | 10 ++++ apps/backend/prisma/schema.prisma | 6 +-- 7 files changed, 133 insertions(+), 11 deletions(-) create mode 100644 apps/backend/apps/admin/src/polygon/file/file.service.ts diff --git a/apps/backend/apps/admin/src/polygon/file/file.service.ts b/apps/backend/apps/admin/src/polygon/file/file.service.ts new file mode 100644 index 0000000000..4195cda191 --- /dev/null +++ b/apps/backend/apps/admin/src/polygon/file/file.service.ts @@ -0,0 +1,49 @@ +import { Injectable } from '@nestjs/common' +import type { ToolType } from '@prisma/client' +import type { FileUpload } from 'graphql-upload/processRequest.mjs' +import { UnprocessableDataException } from '@libs/exception' +import { PrismaService } from '@libs/prisma' + +const MAX_TOOL_FILE_SIZE = 10 * 1024 * 1024 // 10MB + +@Injectable() +export class FileService { + constructor(private readonly prisma: PrismaService) {} + + async uploadPolygonToolFile( + problemId: number, + toolType: ToolType, + file: FileUpload + ) { + const { filename, createReadStream } = file + + //ReadStream → [chunk1, chunk2, chunk3, ...] → Buffer.concat + //→ 최종 Buffer로 변환해 → DB(PostgreSQL)에 저장 + const chunks: Buffer[] = [] + let total = 0 + for await (const chunk of createReadStream()) { + total += chunk.length + if (total > MAX_TOOL_FILE_SIZE) { + throw new UnprocessableDataException('File size exceeds maximum limit') + } + chunks.push(chunk) + } + const fileContent = Buffer.concat(chunks).toString('utf-8') + + // (problemId, toolType) unique — 재업로드 시 갱신 + const tool = await this.prisma.polygonTool.upsert({ + // eslint-disable-next-line @typescript-eslint/naming-convention + where: { problemId_toolType: { problemId, toolType } }, + update: { fileName: filename, fileContent }, + create: { problemId, toolType, fileName: filename, fileContent } + }) + return tool + } + + async deletePolygonFile(problemId: number, toolType: ToolType) { + return await this.prisma.polygonTool.delete({ + // eslint-disable-next-line @typescript-eslint/naming-convention + where: { problemId_toolType: { problemId, toolType } } + }) + } +} diff --git a/apps/backend/apps/admin/src/polygon/polygon.module.ts b/apps/backend/apps/admin/src/polygon/polygon.module.ts index f45388d1c4..4727d5a12b 100644 --- a/apps/backend/apps/admin/src/polygon/polygon.module.ts +++ b/apps/backend/apps/admin/src/polygon/polygon.module.ts @@ -1,10 +1,12 @@ import { Module } from '@nestjs/common' +import { AMQPModule } from '@libs/amqp' import { RolesModule } from '@libs/auth' +import { FileService } from './file/file.service' import { PolygonResolver } from './polygon.resolver' import { PolygonService } from './polygon.service' @Module({ - imports: [RolesModule], - providers: [PolygonResolver, PolygonService] + imports: [RolesModule, AMQPModule], + providers: [PolygonResolver, PolygonService, FileService] }) export class PolygonModule {} diff --git a/apps/backend/apps/admin/src/polygon/polygon.resolver.ts b/apps/backend/apps/admin/src/polygon/polygon.resolver.ts index 442be0c0e6..783c013593 100644 --- a/apps/backend/apps/admin/src/polygon/polygon.resolver.ts +++ b/apps/backend/apps/admin/src/polygon/polygon.resolver.ts @@ -1,10 +1,34 @@ -import { Resolver } from '@nestjs/graphql' +import { Args, Int, Mutation, Resolver } from '@nestjs/graphql' +import { ToolType } from '@prisma/client' +import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs' +import type { FileUpload } from 'graphql-upload/processRequest.mjs' import { UseDisableAdminGuard } from '@libs/auth' -import { PolygonProblem } from '@admin/@generated' +import { PolygonProblem, PolygonTool } from '@admin/@generated' import { PolygonService } from './polygon.service' @Resolver(() => PolygonProblem) @UseDisableAdminGuard() export class PolygonResolver { constructor(private readonly polygonService: PolygonService) {} + + @Mutation(() => PolygonTool) + async uploadPolygonTool( + @Args('problemId', { type: () => Int }) problemId: number, + @Args('toolType', { type: () => ToolType }) toolType: ToolType, + @Args('file', { type: () => GraphQLUpload }) file: Promise + ) { + return this.polygonService.uploadPolygonTool( + problemId, + toolType, + await file + ) + } + + @Mutation(() => PolygonTool) + async deletePolygonTool( + @Args('problemId', { type: () => Int }) problemId: number, + @Args('toolType', { type: () => ToolType }) toolType: ToolType + ) { + return this.polygonService.deletePolygonTool(problemId, toolType) + } } diff --git a/apps/backend/apps/admin/src/polygon/polygon.service.ts b/apps/backend/apps/admin/src/polygon/polygon.service.ts index 861fcb39aa..b4f5d981cf 100644 --- a/apps/backend/apps/admin/src/polygon/polygon.service.ts +++ b/apps/backend/apps/admin/src/polygon/polygon.service.ts @@ -1,7 +1,40 @@ import { Injectable } from '@nestjs/common' +import { ToolType } from '@prisma/client' +import type { FileUpload } from 'graphql-upload/processRequest.mjs' +import { PolygonAMQPService } from '@libs/amqp' import { PrismaService } from '@libs/prisma' +import { FileService } from './file/file.service' @Injectable() export class PolygonService { - constructor(private readonly prisma: PrismaService) {} + constructor( + private readonly prisma: PrismaService, + private readonly fileService: FileService, + private readonly polygonAMQPService: PolygonAMQPService + ) {} + + async uploadPolygonTool( + problemId: number, + toolType: ToolType, + file: FileUpload + ) { + //DB에 파일 저장 + const uploadedTool = await this.fileService.uploadPolygonToolFile( + problemId, + toolType, + file + ) + + //RabbitMQ 메시지 발행 + await this.polygonAMQPService.publishPolygonToolUploadMessage( + uploadedTool.problemId, + uploadedTool.toolType + ) + + return uploadedTool + } + + async deletePolygonTool(problemId: number, toolType: ToolType) { + return this.fileService.deletePolygonFile(problemId, toolType) + } } diff --git a/apps/backend/libs/amqp/src/amqp.module.ts b/apps/backend/libs/amqp/src/amqp.module.ts index 4191698105..dd44c6232d 100644 --- a/apps/backend/libs/amqp/src/amqp.module.ts +++ b/apps/backend/libs/amqp/src/amqp.module.ts @@ -2,7 +2,11 @@ import { Module } from '@nestjs/common' import { ConfigModule, ConfigService } from '@nestjs/config' import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq' import { CONSUME_CHANNEL, PUBLISH_CHANNEL } from '@libs/constants' -import { CheckAMQPService, JudgeAMQPService } from './amqp.service' +import { + CheckAMQPService, + JudgeAMQPService, + PolygonAMQPService +} from './amqp.service' @Module({ imports: [ @@ -41,7 +45,7 @@ import { CheckAMQPService, JudgeAMQPService } from './amqp.service' inject: [ConfigService] }) ], - providers: [JudgeAMQPService, CheckAMQPService], - exports: [JudgeAMQPService, CheckAMQPService] + providers: [JudgeAMQPService, CheckAMQPService, PolygonAMQPService], + exports: [JudgeAMQPService, CheckAMQPService, PolygonAMQPService] }) export class AMQPModule {} diff --git a/apps/backend/libs/constants/src/rabbitmq.constants.ts b/apps/backend/libs/constants/src/rabbitmq.constants.ts index be6a0d68b4..2e629e299c 100644 --- a/apps/backend/libs/constants/src/rabbitmq.constants.ts +++ b/apps/backend/libs/constants/src/rabbitmq.constants.ts @@ -33,3 +33,13 @@ export const CHECK_RESULT_KEY = 'check.result' export const CHECK_RESULT_QUEUE = 'plag.q.check.result' export const CHECK_MESSAGE_TYPE = 'check' + +/** + * Polygon Tool 업로드 + */ + +export const POLYGON_EXCHANGE = 'iris.e.direct.polygon' + +export const POLYGON_TOOL_KEY = 'polygon.tool' + +export const POLYGON_TOOL_MESSAGE_TYPE = 'polygonTool' diff --git a/apps/backend/prisma/schema.prisma b/apps/backend/prisma/schema.prisma index 5ebc8002b5..c9ab0738bf 100644 --- a/apps/backend/prisma/schema.prisma +++ b/apps/backend/prisma/schema.prisma @@ -1136,9 +1136,9 @@ model PolygonTool { problem PolygonProblem @relation(fields: [problemId], references: [id], onDelete: Cascade) problemId Int @map("problem_id") - toolType ToolType @map("tool_type") // 'generator' | 'validator' | 'checker' - fileName String @map("file_name") - filePath String @map("file_path") // S3 key + toolType ToolType @map("tool_type") // 'generator' | 'validator' | 'checker' + fileName String @map("file_name") + fileContent String @map("file_content") createTime DateTime @default(now()) @map("create_time") updateTime DateTime @updatedAt @map("update_time") From 034d3ffcccdb296fcd97cf1ece0edb0b44b8a23d Mon Sep 17 00:00:00 2001 From: zero1177 Date: Thu, 16 Apr 2026 14:59:54 +0000 Subject: [PATCH 2/6] feat(be): implement publication service for polygon to run tool files & update rabbitmq constants --- .../interface/polygonToolRequest.interface.ts | 15 +++++ .../admin/src/polygon/polygon-pub.service.ts | 55 +++++++++++++++++++ .../apps/admin/src/polygon/polygon.service.ts | 37 ++++++++----- .../libs/constants/src/rabbitmq.constants.ts | 17 +++++- 4 files changed, 106 insertions(+), 18 deletions(-) create mode 100644 apps/backend/apps/admin/src/polygon/interface/polygonToolRequest.interface.ts create mode 100644 apps/backend/apps/admin/src/polygon/polygon-pub.service.ts diff --git a/apps/backend/apps/admin/src/polygon/interface/polygonToolRequest.interface.ts b/apps/backend/apps/admin/src/polygon/interface/polygonToolRequest.interface.ts new file mode 100644 index 0000000000..eb75dadca2 --- /dev/null +++ b/apps/backend/apps/admin/src/polygon/interface/polygonToolRequest.interface.ts @@ -0,0 +1,15 @@ +export interface GeneratorRequest { + problemId: number + generatorLanguage: string + generatorCode: string + generatorArgs: string[] + solutionLanguage: string + solutionCode: string + testCaseCount: number +} + +export interface ValidatorRequest { + problemId: number + language: string + validatorCode: string +} diff --git a/apps/backend/apps/admin/src/polygon/polygon-pub.service.ts b/apps/backend/apps/admin/src/polygon/polygon-pub.service.ts new file mode 100644 index 0000000000..412f5b415c --- /dev/null +++ b/apps/backend/apps/admin/src/polygon/polygon-pub.service.ts @@ -0,0 +1,55 @@ +import { Language, ToolType } from '@prisma/client' +import type { PolygonAMQPService } from '@libs/amqp' +import type { PrismaService } from '@libs/prisma' + +export class PolygonPublicationService { + constructor( + private readonly prisma: PrismaService, + private readonly amqpService: PolygonAMQPService + ) {} + + async publishGeneratorMessage( + problemId: number, + generatorArgs: string[], + testCaseCount: number + ) { + //DB에서 generator, solution 조회 + const [generator, solution] = await Promise.all([ + this.prisma.polygonTool.findUniqueOrThrow({ + where: { + // eslint-disable-next-line @typescript-eslint/naming-convention + problemId_toolType: { problemId, toolType: ToolType.Generator } + } + }), + this.prisma.polygonSolution.findUniqueOrThrow({ + where: { problemId } + }) + ]) + + //실행 요청 메시지 publish + await this.amqpService.publishGeneratorMessage({ + problemId, + generatorLanguage: Language.Cpp, + generatorCode: generator.fileContent, + generatorArgs, + solutionLanguage: solution.language, + solutionCode: solution.fileContent, + testCaseCount + }) + } + + async publishValidatorMessage(problemId: number) { + const validator = await this.prisma.polygonTool.findUniqueOrThrow({ + where: { + // eslint-disable-next-line @typescript-eslint/naming-convention + problemId_toolType: { problemId, toolType: ToolType.Validator } + } + }) + + await this.amqpService.publishValidatorMessage({ + problemId, + language: Language.Cpp, + validatorCode: validator.fileContent + }) + } +} diff --git a/apps/backend/apps/admin/src/polygon/polygon.service.ts b/apps/backend/apps/admin/src/polygon/polygon.service.ts index b4f5d981cf..66fc88d5d5 100644 --- a/apps/backend/apps/admin/src/polygon/polygon.service.ts +++ b/apps/backend/apps/admin/src/polygon/polygon.service.ts @@ -1,16 +1,16 @@ import { Injectable } from '@nestjs/common' import { ToolType } from '@prisma/client' import type { FileUpload } from 'graphql-upload/processRequest.mjs' -import { PolygonAMQPService } from '@libs/amqp' import { PrismaService } from '@libs/prisma' import { FileService } from './file/file.service' +import { PolygonPublicationService } from './polygon-pub.service' @Injectable() export class PolygonService { constructor( private readonly prisma: PrismaService, private readonly fileService: FileService, - private readonly polygonAMQPService: PolygonAMQPService + private readonly publicationService: PolygonPublicationService ) {} async uploadPolygonTool( @@ -19,22 +19,29 @@ export class PolygonService { file: FileUpload ) { //DB에 파일 저장 - const uploadedTool = await this.fileService.uploadPolygonToolFile( - problemId, - toolType, - file - ) - - //RabbitMQ 메시지 발행 - await this.polygonAMQPService.publishPolygonToolUploadMessage( - uploadedTool.problemId, - uploadedTool.toolType - ) - - return uploadedTool + await this.fileService.uploadPolygonToolFile(problemId, toolType, file) } async deletePolygonTool(problemId: number, toolType: ToolType) { return this.fileService.deletePolygonFile(problemId, toolType) } + + //파일 실행 + async runGenerator( + problemId: number, + generatorArgs: string[], + testCaseCount: number + ) { + await this.publicationService.publishGeneratorMessage( + problemId, + generatorArgs, + testCaseCount + ) + } + + async runValidator(problemId: number) { + await this.publicationService.publishValidatorMessage(problemId) + } + + //테스트케이스 저장 } diff --git a/apps/backend/libs/constants/src/rabbitmq.constants.ts b/apps/backend/libs/constants/src/rabbitmq.constants.ts index 2e629e299c..6810bbb63d 100644 --- a/apps/backend/libs/constants/src/rabbitmq.constants.ts +++ b/apps/backend/libs/constants/src/rabbitmq.constants.ts @@ -35,11 +35,22 @@ export const CHECK_RESULT_QUEUE = 'plag.q.check.result' export const CHECK_MESSAGE_TYPE = 'check' /** - * Polygon Tool 업로드 + * Polygon Tool 업로드 -> Queue */ export const POLYGON_EXCHANGE = 'iris.e.direct.polygon' -export const POLYGON_TOOL_KEY = 'polygon.tool' +export const POLYGON_GENERATOR_KEY = 'polygon.generator' +export const POLYGON_GENERATOR_MESSAGE_TYPE = 'generate' -export const POLYGON_TOOL_MESSAGE_TYPE = 'polygonTool' +export const POLYGON_VALIDATOR_KEY = 'polygon.validator' +export const POLYGON_VALIDATOR_MESSAGE_TYPE = 'validator' + +export const POLYGON_CHECKER_KEY = 'polygon.checker' +export const POLYGON_CHECKER_MESSAGE_TYPE = 'checker' + +export const POLYGON_GENERATOR_RESULT_KEY = 'polygon.generate.result' +export const POLYGON_GENERATOR_RESULT_QUEUE = 'iris.q.polygon.generate.result' + +export const POLYGON_VALIDATOR_RESULT_KEY = 'polygon.validate.result' +export const POLYGON_VALIDATOR_RESULT_QUEUE = 'iris.q.polygon.validate.result' From 71d4d60b0f77cddcebfa70ab88e306bcd7671293 Mon Sep 17 00:00:00 2001 From: zero1177 Date: Thu, 16 Apr 2026 15:01:45 +0000 Subject: [PATCH 3/6] fix(be): fix fieldName filePath into fileContent --- apps/backend/prisma/schema.prisma | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/backend/prisma/schema.prisma b/apps/backend/prisma/schema.prisma index c9ab0738bf..bcfa38e1fa 100644 --- a/apps/backend/prisma/schema.prisma +++ b/apps/backend/prisma/schema.prisma @@ -1121,9 +1121,9 @@ model PolygonSolution { problem PolygonProblem @relation(fields: [problemId], references: [id], onDelete: Cascade) problemId Int @unique @map("problem_id") - fileName String @map("file_name") // 원본 파일명 (solution.cpp) - filePath String @map("file_path") // S3 key - language Language + fileName String @map("file_name") // 원본 파일명 (solution.cpp) + fileContent String @map("file_content") //DB 저장 + language Language createTime DateTime @default(now()) @map("create_time") updateTime DateTime @updatedAt @map("update_time") @@ -1138,7 +1138,7 @@ model PolygonTool { toolType ToolType @map("tool_type") // 'generator' | 'validator' | 'checker' fileName String @map("file_name") - fileContent String @map("file_content") + fileContent String @map("file_content") //DB 저장 createTime DateTime @default(now()) @map("create_time") updateTime DateTime @updatedAt @map("update_time") From 0c8d86e0965b6e1e35a96e6585584065e8c462a1 Mon Sep 17 00:00:00 2001 From: zero1177 Date: Thu, 16 Apr 2026 15:14:17 +0000 Subject: [PATCH 4/6] feat(be): add polygon amqp service logic --- apps/backend/libs/amqp/src/amqp.service.ts | 134 ++++++++++++++++++++- 1 file changed, 133 insertions(+), 1 deletion(-) diff --git a/apps/backend/libs/amqp/src/amqp.service.ts b/apps/backend/libs/amqp/src/amqp.service.ts index c0a2ccf07d..11287b698f 100644 --- a/apps/backend/libs/amqp/src/amqp.service.ts +++ b/apps/backend/libs/amqp/src/amqp.service.ts @@ -18,8 +18,21 @@ import { MESSAGE_PRIORITY_HIGH, MESSAGE_PRIORITY_MIDDLE, MESSAGE_PRIORITY_LOW, - SUBMISSION_KEY + SUBMISSION_KEY, + POLYGON_EXCHANGE, + POLYGON_GENERATOR_MESSAGE_TYPE, + POLYGON_GENERATOR_KEY, + POLYGON_GENERATOR_RESULT_KEY, + POLYGON_GENERATOR_RESULT_QUEUE, + POLYGON_VALIDATOR_RESULT_KEY, + POLYGON_VALIDATOR_RESULT_QUEUE, + POLYGON_VALIDATOR_KEY, + POLYGON_VALIDATOR_MESSAGE_TYPE } from '@libs/constants' +import type { + GeneratorRequest, + ValidatorRequest +} from '@admin/polygon/interface/polygonToolRequest.interface' @Injectable() export class JudgeAMQPService { @@ -212,3 +225,122 @@ export class CheckAMQPService { onCheckMessage?: (msg: object) => Promise } } + +@Injectable() +export class PolygonAMQPService { + private readonly logger = new Logger(PolygonAMQPService.name) + + constructor( + private readonly amqpConnection: AmqpConnection, + private readonly traceService: TraceService + ) {} + + //1. 큐 구독 + startGeneratorSubscription() { + //결과메시지 도착하면 콜백 실행됨 + this.amqpConnection.createSubscriber( + //@golevelup/nestjs-rabbitmq 버전이 업데이트 되면서 생긴 문제? + async (msg: object | undefined) => { + try { + if (!msg) return //undefined인 경우 메시지 큐에서 제거 + //onGenerateResult 핸들러가 등록되어 있으면 + if (this.messageHandlers?.onGenerateResult) { + await this.messageHandlers.onGenerateResult(msg) //onGenerateResult() 실행 + } + } catch (error) { + this.logger.error( + error, + 'Unexpected error in handling generator result message' + ) + return new Nack() + } + }, + { + exchange: POLYGON_EXCHANGE, + routingKey: POLYGON_GENERATOR_RESULT_KEY, //결과 큐를 분리할건지 통합할건지 조율해야됨. + queue: POLYGON_GENERATOR_RESULT_QUEUE + }, + ORIGIN_HANDLER_NAME + ) + } + + startValidatorSubscription() { + //결과메시지 도착하면 콜백 실행됨 + this.amqpConnection.createSubscriber( + //@golevelup/nestjs-rabbitmq 버전이 업데이트 되면서 생긴 문제? + async (msg: object | undefined) => { + try { + if (!msg) return //undefined인 경우 메시지 큐에서 제거 + //onValidateResult 핸들러가 등록되어 있으면 + if (this.messageHandlers?.onValidateResult) { + await this.messageHandlers.onValidateResult(msg) //onValidateResult() 실행 + } + } catch (error) { + this.logger.error( + error, + 'Unexpected error in handling validator result message' + ) + return new Nack() + } + }, + { + exchange: POLYGON_EXCHANGE, + routingKey: POLYGON_VALIDATOR_RESULT_KEY, //결과 큐를 분리할건지 통합할건지 조율해야됨. + queue: POLYGON_VALIDATOR_RESULT_QUEUE + }, + ORIGIN_HANDLER_NAME + ) + } + + /** + * Generator 실행 요청을 Iris로 publish합니다. + */ + @Span() + async publishGeneratorMessage(request: GeneratorRequest): Promise { + const span = this.traceService.startSpan('publishGeneratorMessage.publish') + await this.amqpConnection.publish( + POLYGON_EXCHANGE, + POLYGON_GENERATOR_KEY, + request, + { + messageId: `Generator-${request.problemId}`, + persistent: true, + type: POLYGON_GENERATOR_MESSAGE_TYPE + } + ) + span.end() + } + + /** + * Validator 실행 요청을 Iris로 publish합니다. + */ + @Span() + async publishValidatorMessage(request: ValidatorRequest): Promise { + const span = this.traceService.startSpan('publishValidatorMessage.publish') + await this.amqpConnection.publish( + POLYGON_EXCHANGE, + POLYGON_VALIDATOR_KEY, + request, + { + messageId: `Validator-${request.problemId}`, + persistent: true, + type: POLYGON_VALIDATOR_MESSAGE_TYPE, + priority: MESSAGE_PRIORITY_MIDDLE + } + ) + span.end() + } + + //handler 설정 + setMessageHandlers(handlers: { + onGenerateResult?: (msg: object) => Promise + onValidateResult?: (msg: object) => Promise + }) { + this.messageHandlers = handlers + } + + private messageHandlers?: { + onGenerateResult?: (msg: object) => Promise + onValidateResult?: (msg: object) => Promise + } +} From 6a1eca4763e6ce163e49519516609e89735bf12f Mon Sep 17 00:00:00 2001 From: zero1177 Date: Thu, 16 Apr 2026 15:20:33 +0000 Subject: [PATCH 5/6] chore(be): commit migration file --- .../migration.sql | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 apps/backend/prisma/migrations/20260416151922_fix_file_path_into_file_content/migration.sql diff --git a/apps/backend/prisma/migrations/20260416151922_fix_file_path_into_file_content/migration.sql b/apps/backend/prisma/migrations/20260416151922_fix_file_path_into_file_content/migration.sql new file mode 100644 index 0000000000..516080f210 --- /dev/null +++ b/apps/backend/prisma/migrations/20260416151922_fix_file_path_into_file_content/migration.sql @@ -0,0 +1,16 @@ +/* + Warnings: + + - You are about to drop the column `file_path` on the `polygon_solution` table. All the data in the column will be lost. + - You are about to drop the column `file_path` on the `polygon_tool` table. All the data in the column will be lost. + - Added the required column `file_content` to the `polygon_solution` table without a default value. This is not possible if the table is not empty. + - Added the required column `file_content` to the `polygon_tool` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "public"."polygon_solution" DROP COLUMN "file_path", +ADD COLUMN "file_content" TEXT NOT NULL; + +-- AlterTable +ALTER TABLE "public"."polygon_tool" DROP COLUMN "file_path", +ADD COLUMN "file_content" TEXT NOT NULL; From 66a23466f9c77da1589e21f00ba8bd50d9001009 Mon Sep 17 00:00:00 2001 From: zero1177 Date: Tue, 21 Apr 2026 05:11:32 +0000 Subject: [PATCH 6/6] feat(be): add message result interface --- .../interface/polygonToolResult.interface.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 apps/backend/apps/admin/src/polygon/interface/polygonToolResult.interface.ts diff --git a/apps/backend/apps/admin/src/polygon/interface/polygonToolResult.interface.ts b/apps/backend/apps/admin/src/polygon/interface/polygonToolResult.interface.ts new file mode 100644 index 0000000000..1079dda9aa --- /dev/null +++ b/apps/backend/apps/admin/src/polygon/interface/polygonToolResult.interface.ts @@ -0,0 +1,23 @@ +export interface GeneratorResultMessage { + submissionId: number + resultCode: number + judgeResult: { + generatedTestCases: number + totalTestCases: number + } + error: string +} + +export interface ValidatorResultMessage { + submissionId: number + resultCode: number + judgeResult: { + isValid: boolean + testcaseCount: number + results: Array<{ + id: number + isValid: boolean + }> + } + error: string +}