judge/src/actions/docker/run.ts

145 lines
3.4 KiB
TypeScript

"use server";
import Dockerode from "dockerode";
import { pullImage } from "@/actions/docker/image";
import {
MessageType,
MessageProps,
} from "@/app/playground/layout/terminal/components/message";
enum ResultType {
CompilationError = "compilation error",
RuntimeError = "runtime error",
Success = "success",
}
interface RunCodeResult {
success: boolean;
output: string;
message: string;
type: ResultType;
}
async function prepareEnvironment(docker: Dockerode, imageGCC: string) {
const images = await docker.listImages();
const imageExists = images.some(
(image) => image.RepoTags && image.RepoTags.includes(imageGCC)
);
if (!imageExists) {
await pullImage(imageGCC);
}
}
async function compileAndRun(
code: string,
docker: Dockerode,
imageGCC: string
): Promise<Dockerode.Container> {
const container = await docker.createContainer({
Image: imageGCC,
Cmd: [
"sh",
"-c",
`export LANG=C.UTF-8 && printf '%s' '${code.replace(
/'/g,
"'\\''"
)}' > main.c && gcc main.c -o main && ./main`,
],
Tty: false,
});
await container.start();
return container;
}
async function getOutputAndResultType(
stream: NodeJS.ReadableStream
): Promise<RunCodeResult> {
return new Promise((resolve, reject) => {
let output = "";
stream.on("data", (chunk) => {
output += chunk.toString("utf8");
});
stream.on("end", () => {
const isSuccessful = !output.includes("error:");
const message = isSuccessful
? "运行成功:编译成功"
: "运行错误:编译失败";
resolve({
success: isSuccessful,
output,
message,
type: isSuccessful ? ResultType.Success : ResultType.CompilationError,
});
});
stream.on("error", (err) => {
reject(err);
});
});
}
async function cleanupContainer(container: Dockerode.Container): Promise<void> {
try {
const containerInfo = await container.inspect();
if (containerInfo.State.Running) {
await container.stop();
}
await container.remove();
} catch (cleanupError) {
console.error("清理容器时出错:", cleanupError);
}
}
function mapResultTypeToMessageType(type: ResultType): MessageType {
switch (type) {
case ResultType.CompilationError:
case ResultType.RuntimeError:
return "error";
case ResultType.Success:
return "success";
default:
return "info";
}
}
export async function runCode(
language: string,
code: string
): Promise<MessageProps> {
const docker = new Dockerode({ socketPath: "/var/run/docker.sock" });
const imageGCC = "gcc:latest";
let container: Dockerode.Container | undefined;
try {
if (language === "c") {
await prepareEnvironment(docker, imageGCC);
container = await compileAndRun(code, docker, imageGCC);
const stream = await container.attach({
stream: true,
stdout: true,
stderr: true,
});
const result = await getOutputAndResultType(stream);
return {
type: mapResultTypeToMessageType(result.type),
message: result.message,
};
} else {
throw new Error(`不支持的语言: ${language}`);
}
} catch (error) {
console.error("运行代码时出错:", error);
return {
type: "error",
message: error instanceof Error ? error.message : "未知错误",
};
} finally {
if (container) {
await cleanupContainer(container);
}
}
}