refactor(config/judge.ts, types/judge.ts): split judge configuration and types

This commit is contained in:
cfngc4594 2025-03-05 09:27:00 +08:00
parent 808dd96a50
commit a33033b48d
3 changed files with 65 additions and 53 deletions

View File

@ -3,7 +3,9 @@
import tar from "tar-stream"; import tar from "tar-stream";
import Docker from "dockerode"; import Docker from "dockerode";
import { Readable, Writable } from "stream"; import { Readable, Writable } from "stream";
import { ExitCode, JudgeResult, LanguageConfigs } from "@/config/judge"; import { JudgeConfig } from "@/config/judge";
import { EditorLanguage } from "@/types/editor-language";
import { ExitCode, JudgeResultMetadata } from "@/types/judge";
// Docker client initialization // Docker client initialization
const docker = new Docker({ socketPath: "/var/run/docker.sock" }); const docker = new Docker({ socketPath: "/var/run/docker.sock" });
@ -41,10 +43,11 @@ function createTarStream(file: string, value: string) {
} }
export async function judge( export async function judge(
language: string, language: EditorLanguage,
value: string value: string
): Promise<JudgeResult> { ): Promise<JudgeResultMetadata> {
const { fileName, fileExtension, image, tag, workingDir, memoryLimit, timeLimit, compileOutputLimit, runOutputLimit } = LanguageConfigs[language]; const { fileName, fileExtension } = JudgeConfig[language].editorLanguageMetadata;
const { image, tag, workingDir, memoryLimit, timeLimit, compileOutputLimit, runOutputLimit } = JudgeConfig[language].dockerMetadata;
const file = `${fileName}.${fileExtension}`; const file = `${fileName}.${fileExtension}`;
let container: Docker.Container | undefined; let container: Docker.Container | undefined;
@ -70,14 +73,14 @@ export async function judge(
} }
} }
async function compile(container: Docker.Container, file: string, fileName: string, maxOutput: number = 1 * 1024 * 1024): Promise<JudgeResult> { async function compile(container: Docker.Container, file: string, fileName: string, maxOutput: number = 1 * 1024 * 1024): Promise<JudgeResultMetadata> {
const compileExec = await container.exec({ const compileExec = await container.exec({
Cmd: ["gcc", "-O2", file, "-o", fileName], Cmd: ["gcc", "-O2", file, "-o", fileName],
AttachStdout: true, AttachStdout: true,
AttachStderr: true, AttachStderr: true,
}); });
return new Promise<JudgeResult>((resolve, reject) => { return new Promise<JudgeResultMetadata>((resolve, reject) => {
compileExec.start({}, (error, stream) => { compileExec.start({}, (error, stream) => {
if (error || !stream) { if (error || !stream) {
return reject({ output: "System Error", exitCode: ExitCode.SE }); return reject({ output: "System Error", exitCode: ExitCode.SE });
@ -126,7 +129,7 @@ async function compile(container: Docker.Container, file: string, fileName: stri
const stderr = stderrChunks.join(""); const stderr = stderrChunks.join("");
const exitCode = (await compileExec.inspect()).ExitCode; const exitCode = (await compileExec.inspect()).ExitCode;
let result: JudgeResult; let result: JudgeResultMetadata;
if (exitCode !== 0 || stderr) { if (exitCode !== 0 || stderr) {
result = { output: stderr || "Compilation Error", exitCode: ExitCode.CE }; result = { output: stderr || "Compilation Error", exitCode: ExitCode.CE };
@ -145,14 +148,14 @@ async function compile(container: Docker.Container, file: string, fileName: stri
} }
// Run code and implement timeout // Run code and implement timeout
async function run(container: Docker.Container, fileName: string, timeLimit?: number, maxOutput: number = 1 * 1024 * 1024): Promise<JudgeResult> { async function run(container: Docker.Container, fileName: string, timeLimit?: number, maxOutput: number = 1 * 1024 * 1024): Promise<JudgeResultMetadata> {
const runExec = await container.exec({ const runExec = await container.exec({
Cmd: [`./${fileName}`], Cmd: [`./${fileName}`],
AttachStdout: true, AttachStdout: true,
AttachStderr: true, AttachStderr: true,
}); });
return new Promise<JudgeResult>((resolve, reject) => { return new Promise<JudgeResultMetadata>((resolve, reject) => {
const stdoutChunks: string[] = []; const stdoutChunks: string[] = [];
let stdoutLength = 0; let stdoutLength = 0;
const stdoutStream = new Writable({ const stdoutStream = new Writable({
@ -211,7 +214,7 @@ async function run(container: Docker.Container, fileName: string, timeLimit?: nu
const stderr = stderrChunks.join(""); const stderr = stderrChunks.join("");
const exitCode = (await runExec.inspect()).ExitCode; const exitCode = (await runExec.inspect()).ExitCode;
let result: JudgeResult; let result: JudgeResultMetadata;
// Exit code 0 means successful execution // Exit code 0 means successful execution
if (exitCode === 0) { if (exitCode === 0) {

View File

@ -1,42 +1,9 @@
// Result type definitions import { EditorLanguage } from "@/types/editor-language";
export enum ExitCode { import { EditorLanguageConfig } from "./editor-language";
SE = 0, // System Error import { DockerMetadata, JudgeMetadata } from "@/types/judge";
CS = 1, // Compilation Success
CE = 2, // Compilation Error
TLE = 3, // Time Limit Exceeded
MLE = 4, // Memory Limit Exceeded
RE = 5, // Runtime Error
AC = 6, // Accepted
WA = 7, // Wrong Answer
}
export type JudgeResult = { export const DockerConfig: Record<EditorLanguage, DockerMetadata> = {
output: string; [EditorLanguage.C]: {
exitCode: ExitCode;
executionTime?: number;
memoryUsage?: number;
};
export interface LanguageConfig {
id: string;
label: string;
fileName: string;
fileExtension: string;
image: string;
tag: string;
workingDir: string;
timeLimit: number;
memoryLimit: number;
compileOutputLimit: number;
runOutputLimit: number;
}
export const LanguageConfigs: Record<string, LanguageConfig> = {
c: {
id: "c",
label: "C",
fileName: "main",
fileExtension: "c",
image: "gcc", image: "gcc",
tag: "latest", tag: "latest",
workingDir: "/src", workingDir: "/src",
@ -45,11 +12,7 @@ export const LanguageConfigs: Record<string, LanguageConfig> = {
compileOutputLimit: 1 * 1024 * 1024, compileOutputLimit: 1 * 1024 * 1024,
runOutputLimit: 1 * 1024 * 1024, runOutputLimit: 1 * 1024 * 1024,
}, },
cpp: { [EditorLanguage.CPP]: {
id: "cpp",
label: "C++",
fileName: "main",
fileExtension: "cpp",
image: "gcc", image: "gcc",
tag: "latest", tag: "latest",
workingDir: "/src", workingDir: "/src",
@ -57,5 +20,16 @@ export const LanguageConfigs: Record<string, LanguageConfig> = {
memoryLimit: 128, memoryLimit: 128,
compileOutputLimit: 1 * 1024 * 1024, compileOutputLimit: 1 * 1024 * 1024,
runOutputLimit: 1 * 1024 * 1024, runOutputLimit: 1 * 1024 * 1024,
}
}
export const JudgeConfig: Record<EditorLanguage, JudgeMetadata> = {
[EditorLanguage.C]: {
editorLanguageMetadata: EditorLanguageConfig[EditorLanguage.C],
dockerMetadata: DockerConfig[EditorLanguage.C],
},
[EditorLanguage.CPP]: {
editorLanguageMetadata: EditorLanguageConfig[EditorLanguage.CPP],
dockerMetadata: DockerConfig[EditorLanguage.CPP],
}, },
}; };

35
src/types/judge.ts Normal file
View File

@ -0,0 +1,35 @@
import { EditorLanguageMetadata } from "./editor-language";
// Result type definitions
export enum ExitCode {
SE = 0, // System Error
CS = 1, // Compilation Success
CE = 2, // Compilation Error
TLE = 3, // Time Limit Exceeded
MLE = 4, // Memory Limit Exceeded
RE = 5, // Runtime Error
AC = 6, // Accepted
WA = 7, // Wrong Answer
}
export type JudgeResultMetadata = {
output: string;
exitCode: ExitCode;
executionTime?: number;
memoryUsage?: number;
};
export type JudgeMetadata = {
editorLanguageMetadata: EditorLanguageMetadata;
dockerMetadata: DockerMetadata;
};
export type DockerMetadata = {
image: string;
tag: string;
workingDir: string;
timeLimit: number;
memoryLimit: number;
compileOutputLimit: number;
runOutputLimit: number;
}