From 254cb982b38a39ebcc2792043ee5bedd00a1f8af Mon Sep 17 00:00:00 2001 From: HKLeeeee Date: Tue, 28 Nov 2023 13:52:55 +0900 Subject: [PATCH 1/6] =?UTF-8?q?feature:=20[BE]=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=EC=8B=9C=20=EC=96=B8=EC=96=B4=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=ED=95=A8=EA=BB=98=20=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 다른 언어 실행 제공을 위한 정보 저장 --- backEnd/api/src/auth/google/google.service.ts | 2 +- backEnd/api/src/codes/codes.controller.ts | 6 +++++- backEnd/api/src/codes/codes.module.ts | 3 ++- backEnd/api/src/codes/dto/getCode.dto.ts | 4 ++++ backEnd/api/src/codes/dto/saveCode.dto.ts | 4 ++++ backEnd/api/src/codes/schemas/code.schemas.ts | 4 ++++ backEnd/api/src/common/type.ts | 1 + 7 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 backEnd/api/src/common/type.ts diff --git a/backEnd/api/src/auth/google/google.service.ts b/backEnd/api/src/auth/google/google.service.ts index 509d4ff..6983cbc 100644 --- a/backEnd/api/src/auth/google/google.service.ts +++ b/backEnd/api/src/auth/google/google.service.ts @@ -20,7 +20,7 @@ export class GoogleService { this.oauth2Client = new google.auth.OAuth2( this.client_id, this.configService.get('CLIENT_SECRET_GOOGLE'), - `${this.configService.get('API_DOMAIN')}/google-callback`, + `${this.configService.get('API_DOMAIN')}/auth/google-callback`, ); this.authorizationUrl = this.oauth2Client.generateAuthUrl({ diff --git a/backEnd/api/src/codes/codes.controller.ts b/backEnd/api/src/codes/codes.controller.ts index 686ceed..f84f823 100644 --- a/backEnd/api/src/codes/codes.controller.ts +++ b/backEnd/api/src/codes/codes.controller.ts @@ -19,6 +19,7 @@ import { ResourceNotFound } from '../common/exception/exception'; import { Serialize } from '../common/interceptor/serialize.interceptor'; import { SaveResultDto } from './dto/saveResult.dto'; import { GetCodeDto } from './dto/getCode.dto'; +import { SaveCodePipe } from './pipes/saveCode.pipe'; @UseGuards(JwtAuthGuard) @Controller('codes') @@ -28,7 +29,10 @@ export class CodesController { @Post() @Serialize(SaveResultDto) - async save(@Body() saveCodeDto: SaveCodeDto, @Req() req: Request) { + async save( + @Body(SaveCodePipe) saveCodeDto: SaveCodeDto, + @Req() req: Request, + ) { const user: UserInfoDto = req.user as UserInfoDto; saveCodeDto.userID = user.id; return await this.codesService.save(saveCodeDto); diff --git a/backEnd/api/src/codes/codes.module.ts b/backEnd/api/src/codes/codes.module.ts index 63bda42..56005ac 100644 --- a/backEnd/api/src/codes/codes.module.ts +++ b/backEnd/api/src/codes/codes.module.ts @@ -5,6 +5,7 @@ import { MongooseModule } from '@nestjs/mongoose'; import { ConfigModule, ConfigService } from '@nestjs/config'; import { Code, CodeSchema } from './schemas/code.schemas'; import { AuthModule } from '../auth/auth.module'; +import { SaveCodePipe } from './pipes/saveCode.pipe'; @Module({ imports: [ @@ -22,6 +23,6 @@ import { AuthModule } from '../auth/auth.module'; AuthModule, ], controllers: [CodesController], - providers: [CodesService], + providers: [CodesService, SaveCodePipe], }) export class CodesModule {} diff --git a/backEnd/api/src/codes/dto/getCode.dto.ts b/backEnd/api/src/codes/dto/getCode.dto.ts index 4722f7b..4337570 100644 --- a/backEnd/api/src/codes/dto/getCode.dto.ts +++ b/backEnd/api/src/codes/dto/getCode.dto.ts @@ -1,4 +1,5 @@ import { Expose } from 'class-transformer'; +import { supportLang } from '../../common/type'; export class GetCodeDto { @Expose() @@ -9,4 +10,7 @@ export class GetCodeDto { @Expose() content: string; + + @Expose() + language: supportLang; } diff --git a/backEnd/api/src/codes/dto/saveCode.dto.ts b/backEnd/api/src/codes/dto/saveCode.dto.ts index 41525f2..1570f28 100644 --- a/backEnd/api/src/codes/dto/saveCode.dto.ts +++ b/backEnd/api/src/codes/dto/saveCode.dto.ts @@ -1,4 +1,5 @@ import { IsString } from 'class-validator'; +import { supportLang } from '../../common/type'; export class SaveCodeDto { userID?: number; @@ -8,4 +9,7 @@ export class SaveCodeDto { @IsString() content: string; + + @IsString() + language: supportLang; } diff --git a/backEnd/api/src/codes/schemas/code.schemas.ts b/backEnd/api/src/codes/schemas/code.schemas.ts index 19224c7..b95dbf1 100644 --- a/backEnd/api/src/codes/schemas/code.schemas.ts +++ b/backEnd/api/src/codes/schemas/code.schemas.ts @@ -1,5 +1,6 @@ import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; import { HydratedDocument } from 'mongoose'; +import { supportLang } from '../../common/type'; export type CodeDocument = HydratedDocument; @@ -13,6 +14,9 @@ export class Code { @Prop({ require: true }) content: string; + + @Prop({ require: true, default: 'python' }) + language: supportLang; } export const CodeSchema = SchemaFactory.createForClass(Code); diff --git a/backEnd/api/src/common/type.ts b/backEnd/api/src/common/type.ts new file mode 100644 index 0000000..0a1d7f9 --- /dev/null +++ b/backEnd/api/src/common/type.ts @@ -0,0 +1 @@ +export type supportLang = 'python' | 'javascript'; From 01a0a61792a6e035336f8d96b0f0d8bbe7f4643d Mon Sep 17 00:00:00 2001 From: HKLeeeee Date: Tue, 28 Nov 2023 13:53:45 +0900 Subject: [PATCH 2/6] =?UTF-8?q?feature:=20[BE]=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=B4=ED=94=84=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 코드 저장 시 파일명 확장자와 지원 언어 타입이 일치하지 않으면 에러 반환 --- backEnd/api/src/codes/codes.controller.ts | 2 +- backEnd/api/src/codes/pipes/saveCode.pipe.ts | 19 +++++++++++++++++++ backEnd/api/src/common/exception/exception.ts | 6 ++++++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 backEnd/api/src/codes/pipes/saveCode.pipe.ts diff --git a/backEnd/api/src/codes/codes.controller.ts b/backEnd/api/src/codes/codes.controller.ts index f84f823..b7be8a6 100644 --- a/backEnd/api/src/codes/codes.controller.ts +++ b/backEnd/api/src/codes/codes.controller.ts @@ -62,7 +62,7 @@ export class CodesController { async update( @Req() req: Request, @Param('id') id: string, - @Body() saveCodeDto: SaveCodeDto, + @Body(SaveCodePipe) saveCodeDto: SaveCodeDto, ) { const user: UserInfoDto = req.user as UserInfoDto; const userID = user.id; diff --git a/backEnd/api/src/codes/pipes/saveCode.pipe.ts b/backEnd/api/src/codes/pipes/saveCode.pipe.ts new file mode 100644 index 0000000..0b89e23 --- /dev/null +++ b/backEnd/api/src/codes/pipes/saveCode.pipe.ts @@ -0,0 +1,19 @@ +import { PipeTransform, Injectable } from '@nestjs/common'; +import * as path from 'path'; +import { SaveCodeDto } from '../dto/saveCode.dto'; +import { ExtNameException } from '../../common/exception/exception'; + +const languageExtName = { + '.js': 'javascript', + '.py': 'python', +}; + +@Injectable() +export class SaveCodePipe implements PipeTransform { + transform(value: SaveCodeDto) { + if (languageExtName[path.extname(value.title)] !== value.language) { + throw new ExtNameException(); + } + return value; + } +} diff --git a/backEnd/api/src/common/exception/exception.ts b/backEnd/api/src/common/exception/exception.ts index 7552a03..02fc3b7 100644 --- a/backEnd/api/src/common/exception/exception.ts +++ b/backEnd/api/src/common/exception/exception.ts @@ -35,3 +35,9 @@ export class ResourceNotFound extends HttpException { super(message, HttpStatus.BAD_REQUEST); } } + +export class ExtNameException extends HttpException { + constructor(message = '저장 파일의 확장자와 설정 언어가 일치하지 않습니다.') { + super(message, HttpStatus.BAD_REQUEST); + } +} From c463b867ff293e45df7cab4ea4b0f967111bb41b Mon Sep 17 00:00:00 2001 From: HKLeeeee Date: Tue, 28 Nov 2023 14:22:34 +0900 Subject: [PATCH 3/6] =?UTF-8?q?feature:=20[BE]=20api=EC=84=9C=EB=B2=84=20d?= =?UTF-8?q?ata=20=EC=A0=84=EB=8B=AC=20=ED=83=80=EC=9E=85=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backEnd/api/src/common/utils.ts | 10 ++++-- .../api/src/run/dto/request-codeblock.dto.ts | 4 +++ backEnd/api/src/run/run.controller.ts | 35 ++++++++----------- backEnd/api/src/run/run.service.ts | 25 +++++++++---- 4 files changed, 46 insertions(+), 28 deletions(-) diff --git a/backEnd/api/src/common/utils.ts b/backEnd/api/src/common/utils.ts index 1c1a78a..018bb47 100644 --- a/backEnd/api/src/common/utils.ts +++ b/backEnd/api/src/common/utils.ts @@ -1,7 +1,13 @@ import * as process from 'process'; +import { supportLang } from './type'; +export const supportLangEnum = { + PYTHON: 'python', + JAVASCRIPT: 'javascript', +}; -export const requestPath = { - RUN_PYTHON: '/codes/python', +export const requestPath: Record = { + python: '/codes/python', + javascript: '/codes/js', }; const timeUnit = { diff --git a/backEnd/api/src/run/dto/request-codeblock.dto.ts b/backEnd/api/src/run/dto/request-codeblock.dto.ts index 7d8a8f3..bf977c6 100644 --- a/backEnd/api/src/run/dto/request-codeblock.dto.ts +++ b/backEnd/api/src/run/dto/request-codeblock.dto.ts @@ -1,6 +1,10 @@ import { IsString } from 'class-validator'; +import { supportLang } from '../../common/type'; export class RequestCodeblockDto { @IsString() code: string; + + @IsString() + language: supportLang; } diff --git a/backEnd/api/src/run/run.controller.ts b/backEnd/api/src/run/run.controller.ts index 61f7135..852fb60 100644 --- a/backEnd/api/src/run/run.controller.ts +++ b/backEnd/api/src/run/run.controller.ts @@ -17,12 +17,7 @@ export class RunController { @HttpCode(200) @Post('v1') async requestRunCode(@Body() codeBlock: RequestCodeblockDto) { - const { code } = codeBlock; - const securityCheck = this.runService.securityCheck(code); - if (securityCheck === returnCode['vulnerable']) { - // fail - throw new VulnerableException(); - } + this.securityCheck(codeBlock); const responseCodeBlockDto = await this.runService.requestRunningApi(codeBlock); @@ -32,12 +27,7 @@ export class RunController { @HttpCode(200) @Post('v2') async requestRunCodeV2(@Body() codeBlock: RequestCodeblockDto) { - const { code } = codeBlock; - const securityCheck = this.runService.securityCheck(code); - if (securityCheck === returnCode['vulnerable']) { - // fail - throw new VulnerableException(); - } + this.securityCheck(codeBlock); const responseCodeBlockDto = await this.runService.requestRunningMQ(codeBlock); @@ -45,24 +35,29 @@ export class RunController { return responseCodeBlockDto; } - @Get('avgTime') - showAvgTrialTime() { - return this.redisService.showTrialTimeAvg(); - } - @HttpCode(202) @Post('v3') async requestRunCodeV3( @Query('id') socketID: string, @Body() codeBlock: RequestCodeblockDto, ): Promise { - const { code } = codeBlock; - const securityCheck = this.runService.securityCheck(code); + this.securityCheck(codeBlock); + + await this.runService.requestRunningMQPubSub(codeBlock, socketID); + } + + securityCheck(codeBlock: RequestCodeblockDto) { + const { code, language } = codeBlock; + const securityCheck = this.runService.securityCheck(code, language); + if (securityCheck === returnCode['vulnerable']) { // fail throw new VulnerableException(); } + } - await this.runService.requestRunningMQPubSub(codeBlock, socketID); + @Get('avgTime') + showAvgTrialTime() { + return this.redisService.showTrialTimeAvg(); } } diff --git a/backEnd/api/src/run/run.service.ts b/backEnd/api/src/run/run.service.ts index 724c6eb..75036a4 100644 --- a/backEnd/api/src/run/run.service.ts +++ b/backEnd/api/src/run/run.service.ts @@ -14,6 +14,7 @@ import { MqService } from '../mq/mq.service'; import { RedisService } from '../redis/redis.service'; import { TimeoutCodeRunning } from '../common/exception/exception'; import { ResponseCodeBlockDto } from './dto/response-codeblock.dto'; +import { supportLang } from '../common/type'; @Injectable() export class RunService { @@ -24,7 +25,16 @@ export class RunService { private mqService: MqService, private redisService: RedisService, ) {} - securityCheck(data): number { + securityCheck(code: string, language: supportLang): number { + switch (language) { + case 'python': + return this.pythonCheck(code); + case 'javascript': + return this.javascriptCheck(code); + } + } + + pythonCheck(code: string) { // 모듈 제한 const blockedModules = [ 'os', @@ -51,8 +61,8 @@ export class RunService { // check for (const pattern of [...blockModulesPattern, ...inputPattern]) { - if (pattern.test(data)) { - this.logger.warn(`⚠️Invalid Code Requested⚠️\n${data}`); + if (pattern.test(code)) { + this.logger.warn(`⚠️Invalid Code Requested⚠️\n${code}`); return returnCode['vulnerable']; } } @@ -60,6 +70,10 @@ export class RunService { return returnCode['safe']; } + javascriptCheck(code: string) { + return returnCode['vulnerable']; + } + async requestRunningApi( codeBlock: RequestCodeblockDto, ): Promise { @@ -67,15 +81,14 @@ export class RunService { 'http://' + path.join( this.configService.get('RUNNING_SERVER'), - requestPath.RUN_PYTHON, + requestPath[codeBlock.language], ); - // console.log(url); try { const result = await axios.post(url, codeBlock); return new ResponseCodeBlockDto( result.status, result.data.output, - 'Running Python Code Success', + 'Running Code Success', ); } catch (e) { this.logger.error(e.message); From 8a840acc9c072b1495dae6a8bfa3ea181679d3a2 Mon Sep 17 00:00:00 2001 From: HKLeeeee Date: Tue, 28 Nov 2023 16:18:07 +0900 Subject: [PATCH 4/6] =?UTF-8?q?feature:=20[BE]=20running=20=EC=84=9C?= =?UTF-8?q?=EB=B2=84=20api=20=ED=98=B8=EC=B6=9C=20=EB=B0=A9=EC=8B=9D=20?= =?UTF-8?q?=EB=8B=A4=EB=A5=B8=20=EC=96=B8=EC=96=B4=20=EC=A7=80=EC=9B=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backEnd/api/src/run/run.controller.ts | 1 - backEnd/api/src/run/run.service.ts | 7 +- backEnd/running/src/codes/codes.controller.ts | 12 ++-- backEnd/running/src/codes/codes.service.ts | 71 ++++++++----------- .../running/src/codes/dto/request-code.dto.ts | 7 ++ .../src/codes/dto/response-code.dto .ts | 2 +- backEnd/running/src/common/type.ts | 2 + backEnd/running/src/common/utils.ts | 11 +++ backEnd/running/src/mq/mq.consumer.ts | 2 +- 9 files changed, 65 insertions(+), 50 deletions(-) create mode 100644 backEnd/running/src/common/type.ts diff --git a/backEnd/api/src/run/run.controller.ts b/backEnd/api/src/run/run.controller.ts index 852fb60..0d09e2c 100644 --- a/backEnd/api/src/run/run.controller.ts +++ b/backEnd/api/src/run/run.controller.ts @@ -18,7 +18,6 @@ export class RunController { @Post('v1') async requestRunCode(@Body() codeBlock: RequestCodeblockDto) { this.securityCheck(codeBlock); - const responseCodeBlockDto = await this.runService.requestRunningApi(codeBlock); return responseCodeBlockDto; diff --git a/backEnd/api/src/run/run.service.ts b/backEnd/api/src/run/run.service.ts index 75036a4..3c07d12 100644 --- a/backEnd/api/src/run/run.service.ts +++ b/backEnd/api/src/run/run.service.ts @@ -71,18 +71,23 @@ export class RunService { } javascriptCheck(code: string) { - return returnCode['vulnerable']; + return returnCode['safe']; } async requestRunningApi( codeBlock: RequestCodeblockDto, ): Promise { + console.log( + this.configService.get('RUNNING_SERVER'), + requestPath[codeBlock.language], + ); const url = 'http://' + path.join( this.configService.get('RUNNING_SERVER'), requestPath[codeBlock.language], ); + try { const result = await axios.post(url, codeBlock); return new ResponseCodeBlockDto( diff --git a/backEnd/running/src/codes/codes.controller.ts b/backEnd/running/src/codes/codes.controller.ts index 1f3ab4b..22c9bf2 100644 --- a/backEnd/running/src/codes/codes.controller.ts +++ b/backEnd/running/src/codes/codes.controller.ts @@ -7,13 +7,13 @@ export class CodesController { constructor(private readonly codesService: CodesService) {} @Post('/python') - async runPython(@Body() data: RequestCodeDto) { - return await this.codesService.testCode(data.code); + async runPython(@Body() codeBlock: RequestCodeDto) { + console.log(codeBlock); + return await this.codesService.runCode(codeBlock); } - @Post('/test') - async test() { - const code = 'N,M = 5,6; print(N+M)'; - return await this.codesService.testCode(code); + @Post('/js') + async runJavascript(@Body() codeBlock: RequestCodeDto) { + return await this.codesService.runCode(codeBlock); } } diff --git a/backEnd/running/src/codes/codes.service.ts b/backEnd/running/src/codes/codes.service.ts index a3a202c..21eccf9 100644 --- a/backEnd/running/src/codes/codes.service.ts +++ b/backEnd/running/src/codes/codes.service.ts @@ -8,13 +8,13 @@ import * as path from 'path'; import { ResponseCodeDto } from './dto/response-code.dto '; import { RunningException } from 'src/common/exception/exception'; import { exec } from 'child_process'; -type runCommandResult = { stdout: string; stderr: string }; +import { LanguageCommand, Messages } from '../common/utils'; +import { supportLang, runCommandResult } from '../common/type'; +import { RequestCodeDto } from './dto/request-code.dto'; @Injectable() export class CodesService { private logger = new Logger(CodesService.name); private tempDir = path.join(__dirname, '..', 'tmp'); - private timeOutMessage = '코드가 실행되는데 너무 오래 걸립니다.'; - private unKnownMessage = '알 수 없는 에러가 발생했습니다.'; private killSignal: NodeJS.Signals = 'SIGINT'; private readonly timeOut = 5000; @@ -24,50 +24,37 @@ export class CodesService { } } - async testCode(code: string): Promise { + async runCode(codeBlock: RequestCodeDto): Promise { this.logger.debug('function[testCode] Called'); + const { code, language } = codeBlock; + const filePath = this.getFilePath(language); - const filePath = this.getFilePath(); - - try { - fs.writeFileSync(filePath, code); - const { stdout, stderr } = await this.runCommand(filePath, this.timeOut); - if (stderr) { - const errorMessage = this.getErrorMessage(stderr); - throw new RunningException(errorMessage); - } - - return this.getOutput(stdout); - } catch (error) { - this.logger.error(error.message); - const errorMessage = this.getErrorMessage(error.message); - - if (errorMessage === this.unKnownMessage) { + fs.writeFileSync(filePath, code); + const { stdout, stderr } = await this.runCommand(filePath, language); + if (stderr) { + if (stderr === Messages.UNKNOWN) { throw new InternalServerErrorException(); } + const errorMessage = this.getErrorMessage(stderr); throw new RunningException(errorMessage); - } finally { - fs.unlinkSync(filePath); } + fs.unlinkSync(filePath); + return this.getOutput(stdout); } - getFilePath() { - const fileName = `python-${Date.now()}.py`; - return path.join(this.tempDir, fileName); - } - - runCommand(filePath, timeout): Promise { + runCommand(filePath, language: supportLang): Promise { + const command = LanguageCommand[language]; return new Promise((resolve) => { // eslint-disable-next-line let timer; const childProcess = exec( - `python3 ${filePath}`, + `${command} ${filePath}`, (error, stdout, stderr) => { if (error) { this.logger.error(`failed to run requested code ${error.message}`); if (error.signal === this.killSignal) { - stderr = this.timeOutMessage; + stderr = Messages.TIMEOUT; } resolve({ stdout, stderr }); } else { @@ -77,23 +64,19 @@ export class CodesService { }, ); - // 일정 시간 후 자식 프로세스 강제 종료 timer = setTimeout(() => { this.logger.log('timeout!'); childProcess.kill(this.killSignal); - }, timeout); + }, this.timeOut); }); } getOutput(stdout: string): ResponseCodeDto { - const output = stdout.split('\n').map((line) => line.replace('\r', '')); - output.pop(); - const result: ResponseCodeDto = { output: output }; - return result; + return { output: stdout.trim() }; } getErrorMessage(stderr: string): string { - if (stderr === this.timeOutMessage) return stderr; + if (stderr === Messages.TIMEOUT) return stderr; const errorLines = stderr.split('\n'); @@ -111,10 +94,18 @@ export class CodesService { ); if (errorLineIndex !== -1) { - const errorMessage = errorLines[errorLineIndex].trim(); - return errorMessage; + return errorLines[errorLineIndex].trim(); } - return this.unKnownMessage; + return Messages.UNKNOWN; + } + + getFilePath(language: supportLang) { + switch (language) { + case 'python': + return path.join(this.tempDir, `python-${Date.now()}.py`); + case 'javascript': + return path.join(this.tempDir, `javascript-${Date.now()}.js`); + } } } diff --git a/backEnd/running/src/codes/dto/request-code.dto.ts b/backEnd/running/src/codes/dto/request-code.dto.ts index 2da8c91..9fb4684 100644 --- a/backEnd/running/src/codes/dto/request-code.dto.ts +++ b/backEnd/running/src/codes/dto/request-code.dto.ts @@ -1,3 +1,10 @@ +import { IsString } from 'class-validator'; +import { supportLang } from '../../common/type'; + export class RequestCodeDto { + @IsString() code: string; + + @IsString() + language: supportLang; } diff --git a/backEnd/running/src/codes/dto/response-code.dto .ts b/backEnd/running/src/codes/dto/response-code.dto .ts index f579d9b..a1f3c55 100644 --- a/backEnd/running/src/codes/dto/response-code.dto .ts +++ b/backEnd/running/src/codes/dto/response-code.dto .ts @@ -1,3 +1,3 @@ export class ResponseCodeDto { - output: string[]; + output: string; } diff --git a/backEnd/running/src/common/type.ts b/backEnd/running/src/common/type.ts new file mode 100644 index 0000000..f7b3d18 --- /dev/null +++ b/backEnd/running/src/common/type.ts @@ -0,0 +1,2 @@ +export type supportLang = 'python' | 'javascript'; +export type runCommandResult = { stdout: string; stderr: string }; diff --git a/backEnd/running/src/common/utils.ts b/backEnd/running/src/common/utils.ts index eb7f89a..bca381c 100644 --- a/backEnd/running/src/common/utils.ts +++ b/backEnd/running/src/common/utils.ts @@ -2,6 +2,7 @@ import { HttpStatus } from '@nestjs/common'; import { exec } from 'child_process'; import { promisify } from 'util'; import * as process from 'process'; +import { supportLang } from './type'; export const ERRORS = { REQUEST_INVALID: { @@ -16,3 +17,13 @@ export const REDIS = { CHANNEL: 'completed', QUEUE: process.env.NODE_ENV === 'dev' ? 'task-dev' : 'task', }; + +export const Messages = { + TIMEOUT: '코드가 실행되는데 너무 오래 걸립니다.', + UNKNOWN: '알 수 없는 에러가 발생했습니다.', +}; + +export const LanguageCommand: Record = { + python: 'python3', + javascript: 'node', +}; diff --git a/backEnd/running/src/mq/mq.consumer.ts b/backEnd/running/src/mq/mq.consumer.ts index 3443688..2a265c6 100644 --- a/backEnd/running/src/mq/mq.consumer.ts +++ b/backEnd/running/src/mq/mq.consumer.ts @@ -25,7 +25,7 @@ export class MqConsumer { const responseCodeBlockDTO = new ResponseCodeBlockDto(); try { - result = await this.codesService.testCode(job.data); + result = await this.codesService.runCode(job.data); const output: string | string[] = typeof result === 'string' ? result : result.output; this.logger.debug(JSON.stringify(result)); From d2eeeff1df830bc0a3c6a3f6c275867c963ebeda Mon Sep 17 00:00:00 2001 From: HKLeeeee Date: Tue, 28 Nov 2023 16:45:11 +0900 Subject: [PATCH 5/6] =?UTF-8?q?feature:=20[BE]=20redis=20=ED=99=9C?= =?UTF-8?q?=EC=9A=A9=20=EC=BD=94=EB=93=9C=20=EC=8B=A4=ED=96=89=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=20=EA=B8=B0=EB=8A=A5=20=EC=9D=91=EB=8B=B5=20=ED=98=95?= =?UTF-8?q?=EC=8B=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backEnd/api/src/mq/mq.service.ts | 2 +- backEnd/api/src/run/run.service.ts | 6 +----- backEnd/running/src/codes/codes.controller.ts | 1 - backEnd/running/src/codes/codes.service.ts | 4 +++- backEnd/running/src/mq/dto/response-codeblock.dto.ts | 2 +- backEnd/running/src/mq/mq.consumer.ts | 8 ++++---- 6 files changed, 10 insertions(+), 13 deletions(-) diff --git a/backEnd/api/src/mq/mq.service.ts b/backEnd/api/src/mq/mq.service.ts index a6e9568..6d86c94 100644 --- a/backEnd/api/src/mq/mq.service.ts +++ b/backEnd/api/src/mq/mq.service.ts @@ -48,7 +48,7 @@ export class MqService { } async addMessage(data: RequestCodeblockDto) { - const job = await this.queue.add(REDIS.QUEUE, data.code, { + const job = await this.queue.add(REDIS.QUEUE, data, { removeOnComplete: true, }); this.logger.log(`push to task Queue ${job.id}`); diff --git a/backEnd/api/src/run/run.service.ts b/backEnd/api/src/run/run.service.ts index 3c07d12..7924793 100644 --- a/backEnd/api/src/run/run.service.ts +++ b/backEnd/api/src/run/run.service.ts @@ -77,10 +77,6 @@ export class RunService { async requestRunningApi( codeBlock: RequestCodeblockDto, ): Promise { - console.log( - this.configService.get('RUNNING_SERVER'), - requestPath[codeBlock.language], - ); const url = 'http://' + path.join( @@ -120,7 +116,7 @@ export class RunService { this.logger.error(`${job.id} failed to find completed job : ${result}`); throw new TimeoutCodeRunning(); } - this.logger.log(`get completed result ${result}`); + this.logger.log(`get completed result ${JSON.stringify(result)}`); return result; } async requestRunningMQPubSub( diff --git a/backEnd/running/src/codes/codes.controller.ts b/backEnd/running/src/codes/codes.controller.ts index 22c9bf2..066c3f5 100644 --- a/backEnd/running/src/codes/codes.controller.ts +++ b/backEnd/running/src/codes/codes.controller.ts @@ -8,7 +8,6 @@ export class CodesController { @Post('/python') async runPython(@Body() codeBlock: RequestCodeDto) { - console.log(codeBlock); return await this.codesService.runCode(codeBlock); } diff --git a/backEnd/running/src/codes/codes.service.ts b/backEnd/running/src/codes/codes.service.ts index 21eccf9..8a2ab72 100644 --- a/backEnd/running/src/codes/codes.service.ts +++ b/backEnd/running/src/codes/codes.service.ts @@ -11,10 +11,12 @@ import { exec } from 'child_process'; import { LanguageCommand, Messages } from '../common/utils'; import { supportLang, runCommandResult } from '../common/type'; import { RequestCodeDto } from './dto/request-code.dto'; +import * as process from 'process'; @Injectable() export class CodesService { private logger = new Logger(CodesService.name); - private tempDir = path.join(__dirname, '..', 'tmp'); + private tempDir = + process.env.NODE_ENV === 'dev' ? path.join(__dirname, '..', 'tmp') : '/'; private killSignal: NodeJS.Signals = 'SIGINT'; private readonly timeOut = 5000; diff --git a/backEnd/running/src/mq/dto/response-codeblock.dto.ts b/backEnd/running/src/mq/dto/response-codeblock.dto.ts index 9bfe3a5..b064f2d 100644 --- a/backEnd/running/src/mq/dto/response-codeblock.dto.ts +++ b/backEnd/running/src/mq/dto/response-codeblock.dto.ts @@ -1,7 +1,7 @@ export class ResponseCodeBlockDto { jobID?: string | number; statusCode: number; - result: string | string[]; + result: string; message: string; timestamp: string; constructor() { diff --git a/backEnd/running/src/mq/mq.consumer.ts b/backEnd/running/src/mq/mq.consumer.ts index 2a265c6..61fcaee 100644 --- a/backEnd/running/src/mq/mq.consumer.ts +++ b/backEnd/running/src/mq/mq.consumer.ts @@ -12,7 +12,7 @@ export class MqConsumer { private logger = new Logger(MqConsumer.name); private errorMessage = { 400: 'Failed to Run Code', - 201: 'Running Python Code Success', + 201: 'Running Code Success', }; constructor( private redisService: RedisService, @@ -20,15 +20,15 @@ export class MqConsumer { ) {} @Process(REDIS.QUEUE) async getMessageQueue(job: Job) { - this.logger.debug(`getMessageQueue ${job.id}, ${job.data}`); + this.logger.debug(`getMessageQueue ${job.id}, ${job.data.code}`); let result: ResponseCodeDto | string; const responseCodeBlockDTO = new ResponseCodeBlockDto(); try { result = await this.codesService.runCode(job.data); - const output: string | string[] = + const output: string = typeof result === 'string' ? result : result.output; - this.logger.debug(JSON.stringify(result)); + this.logger.debug(result); responseCodeBlockDTO.statusCode = HttpStatus.CREATED; responseCodeBlockDTO.result = output; From 44210af7c9462ea725274eee44b473560c8789ac Mon Sep 17 00:00:00 2001 From: HKLeeeee Date: Tue, 28 Nov 2023 17:00:07 +0900 Subject: [PATCH 6/6] =?UTF-8?q?feature:=20[BE]=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=8B=A4=ED=96=89=20=EC=9A=94=EC=B2=AD=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 언어 타입에 오타나 지원하지 않는 언어가 들어오면 에러 반환 --- backEnd/api/src/common/utils.ts | 1 + backEnd/api/src/mq/mq.service.ts | 4 ++-- backEnd/api/src/run/dto/request-codeblock.dto.ts | 2 +- backEnd/api/src/run/pipes/saveCode.pipe.ts | 14 ++++++++++++++ backEnd/api/src/run/run.controller.ts | 11 ++++++----- backEnd/api/src/run/run.service.ts | 8 ++++---- 6 files changed, 28 insertions(+), 12 deletions(-) create mode 100644 backEnd/api/src/run/pipes/saveCode.pipe.ts diff --git a/backEnd/api/src/common/utils.ts b/backEnd/api/src/common/utils.ts index 018bb47..799a146 100644 --- a/backEnd/api/src/common/utils.ts +++ b/backEnd/api/src/common/utils.ts @@ -59,6 +59,7 @@ export const jwtError = { export const ResponseMessage = { NEED_LOGIN: '로그인이 필요합니다.', INTERNAL_SERVER_ERROR: 'Internal server error', + NOT_SUPPORT: '지원하지 않는 언어 타입입니다.', }; export const SOCKET_EVENT = { diff --git a/backEnd/api/src/mq/mq.service.ts b/backEnd/api/src/mq/mq.service.ts index 6d86c94..a841700 100644 --- a/backEnd/api/src/mq/mq.service.ts +++ b/backEnd/api/src/mq/mq.service.ts @@ -2,7 +2,7 @@ import { Injectable, Logger } from '@nestjs/common'; import { InjectQueue } from '@nestjs/bull'; import { Queue } from 'bull'; import { ConfigService } from '@nestjs/config'; -import { RequestCodeblockDto } from '../run/dto/request-codeblock.dto'; +import { RequestCodeBlockDto } from '../run/dto/request-codeblock.dto'; import Redis from 'ioredis'; import { EVENT, REDIS } from '../common/utils'; import { EventEmitter2 } from '@nestjs/event-emitter'; @@ -47,7 +47,7 @@ export class MqService { }); } - async addMessage(data: RequestCodeblockDto) { + async addMessage(data: RequestCodeBlockDto) { const job = await this.queue.add(REDIS.QUEUE, data, { removeOnComplete: true, }); diff --git a/backEnd/api/src/run/dto/request-codeblock.dto.ts b/backEnd/api/src/run/dto/request-codeblock.dto.ts index bf977c6..b07dba0 100644 --- a/backEnd/api/src/run/dto/request-codeblock.dto.ts +++ b/backEnd/api/src/run/dto/request-codeblock.dto.ts @@ -1,7 +1,7 @@ import { IsString } from 'class-validator'; import { supportLang } from '../../common/type'; -export class RequestCodeblockDto { +export class RequestCodeBlockDto { @IsString() code: string; diff --git a/backEnd/api/src/run/pipes/saveCode.pipe.ts b/backEnd/api/src/run/pipes/saveCode.pipe.ts new file mode 100644 index 0000000..ee8270e --- /dev/null +++ b/backEnd/api/src/run/pipes/saveCode.pipe.ts @@ -0,0 +1,14 @@ +import { PipeTransform, Injectable } from '@nestjs/common'; +import { ExtNameException } from '../../common/exception/exception'; +import { RequestCodeBlockDto } from '../dto/request-codeblock.dto'; +import { ResponseMessage } from '../../common/utils'; + +@Injectable() +export class RequestRunPipe implements PipeTransform { + transform(value: RequestCodeBlockDto) { + if (!['python', 'javascript'].includes(value.language)) { + throw new ExtNameException(ResponseMessage.NOT_SUPPORT); + } + return value; + } +} diff --git a/backEnd/api/src/run/run.controller.ts b/backEnd/api/src/run/run.controller.ts index 0d09e2c..d78f527 100644 --- a/backEnd/api/src/run/run.controller.ts +++ b/backEnd/api/src/run/run.controller.ts @@ -1,10 +1,11 @@ import { Body, Controller, Get, HttpCode, Post, Query } from '@nestjs/common'; import { RunService } from './run.service'; -import { RequestCodeblockDto } from './dto/request-codeblock.dto'; +import { RequestCodeBlockDto } from './dto/request-codeblock.dto'; import { returnCode } from '../common/returnCode'; import { VulnerableException } from '../common/exception/exception'; import { MqService } from '../mq/mq.service'; import { RedisService } from '../redis/redis.service'; +import { RequestRunPipe } from './pipes/saveCode.pipe'; @Controller('run') export class RunController { @@ -16,7 +17,7 @@ export class RunController { ) {} @HttpCode(200) @Post('v1') - async requestRunCode(@Body() codeBlock: RequestCodeblockDto) { + async requestRunCode(@Body(RequestRunPipe) codeBlock: RequestCodeBlockDto) { this.securityCheck(codeBlock); const responseCodeBlockDto = await this.runService.requestRunningApi(codeBlock); @@ -25,7 +26,7 @@ export class RunController { @HttpCode(200) @Post('v2') - async requestRunCodeV2(@Body() codeBlock: RequestCodeblockDto) { + async requestRunCodeV2(@Body(RequestRunPipe) codeBlock: RequestCodeBlockDto) { this.securityCheck(codeBlock); const responseCodeBlockDto = @@ -38,14 +39,14 @@ export class RunController { @Post('v3') async requestRunCodeV3( @Query('id') socketID: string, - @Body() codeBlock: RequestCodeblockDto, + @Body(RequestRunPipe) codeBlock: RequestCodeBlockDto, ): Promise { this.securityCheck(codeBlock); await this.runService.requestRunningMQPubSub(codeBlock, socketID); } - securityCheck(codeBlock: RequestCodeblockDto) { + securityCheck(codeBlock: RequestCodeBlockDto) { const { code, language } = codeBlock; const securityCheck = this.runService.securityCheck(code, language); diff --git a/backEnd/api/src/run/run.service.ts b/backEnd/api/src/run/run.service.ts index 7924793..566a31d 100644 --- a/backEnd/api/src/run/run.service.ts +++ b/backEnd/api/src/run/run.service.ts @@ -5,7 +5,7 @@ import { Logger, } from '@nestjs/common'; import { returnCode } from '../common/returnCode'; -import { RequestCodeblockDto } from './dto/request-codeblock.dto'; +import { RequestCodeBlockDto } from './dto/request-codeblock.dto'; import axios from 'axios'; import { ConfigService } from '@nestjs/config'; import { requestPath } from '../common/utils'; @@ -75,7 +75,7 @@ export class RunService { } async requestRunningApi( - codeBlock: RequestCodeblockDto, + codeBlock: RequestCodeBlockDto, ): Promise { const url = 'http://' + @@ -105,7 +105,7 @@ export class RunService { } async requestRunningMQ( - codeBlock: RequestCodeblockDto, + codeBlock: RequestCodeBlockDto, ): Promise { const job = await this.mqService.addMessage(codeBlock); this.logger.log(`added message queue job#${job.id}`); @@ -120,7 +120,7 @@ export class RunService { return result; } async requestRunningMQPubSub( - codeBlock: RequestCodeblockDto, + codeBlock: RequestCodeBlockDto, socketID: string, ): Promise { const job = await this.mqService.addMessage(codeBlock);