Skip to content

Commit

Permalink
Update code.ts and finish test. (#1308)
Browse files Browse the repository at this point in the history
* Implement a mapping from contest_name to RUNNER_IMAGE & COMPILER_IMAGE

* Optimized the logic of status codes for the user route

* Finish compile-start app

* Finish compile-finish app

* Update compile route

* Prepare test on server

* Finish test for compiler

* Clear code

* Fix some minor bugs

* Uncomment some code

* Some optimizations were implemented

* Update code.ts

* Fix some bugs

* Fix some bugs

* Update code.ts

* Update code.ts
  • Loading branch information
zzdhybthu authored Mar 31, 2024
1 parent af74a5d commit 5b73967
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 29 deletions.
5 changes: 3 additions & 2 deletions src/helpers/docker_queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { JwtServerPayload } from "../routes/contest";
import { gql } from "graphql-request";
import { client } from "..";
import fs from "fs";
import { base_directory, get_contest_name, contest_image_map } from "./utils";
import { get_base_directory, get_contest_name, contest_image_map } from "./utils";

export interface queue_element {
contest_id: string;
Expand Down Expand Up @@ -44,9 +44,10 @@ const get_port = async (room_id: string, exposed_ports: Array<string>, sub_base_
return result;
}

const docker_cron = () => {
const docker_cron = async () => {
const port_num = parseInt(process.env.MAX_CONTAINERS as string);
const exposed_ports = new Array(port_num).fill("");
const base_directory = await get_base_directory();

cron.schedule(`*/${process.env.QUEUE_CHECK_TIME} * * * * *`, async () => {
const max_container_num = parseInt(process.env.MAX_CONTAINERS as string);
Expand Down
36 changes: 35 additions & 1 deletion src/helpers/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { gql } from "graphql-request";
import { client } from "..";

export const base_directory = process.env.NODE_ENV === "production" ? '/data' : process.env.BASE_DIR!;
export const get_base_directory = async () => {
return process.env.NODE_ENV === "production" ? '/data' : process.env.BASE_DIR!;
}

// query contest_name from contest_id
export const get_contest_name: any = async (contest_id: string) => {
Expand Down Expand Up @@ -90,6 +92,38 @@ export const get_maneger_from_user: any = async (user_uuid: string, contest_id:
return query_if_manager.contest_manager[0]?.user_uuid ?? null;
}

// query language and contest_id from code_id
export const query_code: any = async (code_id: string) => {
const query_all_from_code = await client.request(
gql`
query get_all_from_code($code_id: uuid!) {
contest_team_code(where: {code_id: {_eq: $code_id}}) {
team_id
language
compile_status
contest_team {
contest_id
contest {
contest_name
}
}
}
}
`,
{
code_id: code_id,
}
);

return {
contest_id: query_all_from_code.contest_team_code[0]?.contest_team?.contest_id ?? null,
contest_name: query_all_from_code.contest_team_code[0]?.contest_team?.contest?.contest_name ?? null,
team_id: query_all_from_code.contest_team_code[0]?.team_id ?? null,
language: query_all_from_code.contest_team_code[0]?.language ?? null,
compile_status: query_all_from_code.contest_team_code[0]?.compile_status ?? null
};
}


type ContestImages = {
[key: string]: {
Expand Down
48 changes: 28 additions & 20 deletions src/routes/code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,29 +168,29 @@ router.get("/download", async (req, res) => {
return res.status(200).send("200 OK: Download success");
})


/**
* POST compile start
* @param {token}
* @param {string} contest_name
* @param {uuid} code_id
* @param {string} language
* @param {string} path
* @param {string} path (optional, default to contest_name/code/team_id)
**/
router.post("/compile-start", authenticate(), async (req, res) => {
try {
const contest_name = req.body.contest_name;
const code_id = req.body.code_id;
const language = req.body.language;
const user_uuid = req.auth.user.uuid;
if (!contest_name || !code_id || !language || !user_uuid) {
if (!code_id || !user_uuid) {
return res.status(422).send("422 Unprocessable Entity: Missing credentials");
}

const contest_id = await utils.get_contest_id(contest_name);
const team_id = await utils.get_team_from_code(code_id);
if (!team_id) {
const { contest_id, contest_name, team_id, language, compile_status} = await utils.query_code(code_id);
if (!contest_id || !team_id || !language) {
return res.status(404).send("404 Not Found: Code unavailable");
}
if (compile_status === "Completed") {
return res.status(400).send("400 Bad Request: Code already compiled");
}

const is_manager = await utils.get_maneger_from_user(user_uuid, contest_id);
if (!is_manager) {
const user_team_id = await utils.get_team_from_user(user_uuid, contest_id);
Expand All @@ -211,6 +211,7 @@ router.post("/compile-start", authenticate(), async (req, res) => {

console.log("start to get sts")
const cosPath = req.body.path ? req.body.path : `${contest_name}/code/${team_id}`;
const base_directory = await utils.get_base_directory();

try {
const cos = await initCOS();
Expand Down Expand Up @@ -246,7 +247,10 @@ router.post("/compile-start", authenticate(), async (req, res) => {
console.log("start to download files")

const key = `${cosPath}/${code_id}.${language}`;
const outputPath = `${utils.base_directory}/${contest_name}/code/${team_id}/${code_id}.${language}`;
console.log(base_directory)
const outputPath = `${base_directory}/${contest_name}/code/${team_id}/${code_id}/source/${code_id}.${language}`;

await fs.mkdir(`${base_directory}/${contest_name}/code/${team_id}/${code_id}/source`, { recursive: true });
await downloadObject(key, outputPath);

} catch (err) {
Expand Down Expand Up @@ -301,7 +305,8 @@ router.post("/compile-start", authenticate(), async (req, res) => {
],
HostConfig: {
Binds: [
`${utils.base_directory}/${contest_name}/code/${team_id}:/usr/local/code`,
`${base_directory}/${contest_name}/code/${team_id}/${code_id}/source:/usr/local/code`,
`${base_directory}/${contest_name}/code/${team_id}/${code_id}/output:/usr/local/output`
],
AutoRemove: true,
NetworkMode: "host"
Expand Down Expand Up @@ -351,7 +356,7 @@ router.post("/compile-start", authenticate(), async (req, res) => {

/**
* POST compile finish
* @param {string} token
* @param {token}
* @param {string} compile_status
*/
router.post("/compile-finish", async (req, res) => {
Expand All @@ -371,6 +376,7 @@ router.post("/compile-finish", async (req, res) => {
const contest_name = payload.contest_name;
const compile_status = req.body.compile_status;
const cosPath = payload.cos_path;
const base_directory = await utils.get_base_directory();
if (!compile_status || !team_id || !code_id || !contest_name) {
return res.status(422).send("422 Unprocessable Entity: Missing credentials");
}
Expand Down Expand Up @@ -408,13 +414,15 @@ router.post("/compile-finish", async (req, res) => {
};
if (compile_status === "Success") {
const key = `${cosPath}/${code_id}`;
const localFilePath = `${utils.base_directory}/${contest_name}/code/${team_id}/${code_id}`;
const localFilePath = `${base_directory}/${contest_name}/code/${team_id}/${code_id}/output/${code_id}`;
await uploadObject(localFilePath, key);
}
const key = `${cosPath}/${code_id}.log`;
const localFilePath = `${utils.base_directory}/${contest_name}/code/${team_id}/${code_id}.log`;
let key = `${cosPath}/${code_id}.log`;
let localFilePath = `${base_directory}/${contest_name}/code/${team_id}/${code_id}/output/${code_id}.log`;
await uploadObject(localFilePath, key);
key = `${cosPath}/${code_id}.curl.log`;
localFilePath = `${base_directory}/${contest_name}/code/${team_id}/${code_id}/output/${code_id}.curl.log`;
await uploadObject(localFilePath, key);

} catch (err) {
return res.status(500).send("500 Internal Server Error: Upload files failed. " + err);
}
Expand Down Expand Up @@ -447,14 +455,14 @@ router.post("/compile-finish", async (req, res) => {
const stats = await fs.stat(filePath);
if (stats.isDirectory()) {
await deleteAllFilesInDir(filePath);
await fs.rmdir(filePath);
} else {
await fs.unlink(filePath);
}
}));
}
));
await fs.rmdir(directoryPath);
}

await deleteFile(`${utils.base_directory}/${contest_name}/code/${team_id}`);
await deleteFile(`${base_directory}/${contest_name}/code/${team_id}/${code_id}`);

} catch (err) {
return res.status(500).send("500 Internal Server Error: Delete files failed. " + err);
Expand Down
11 changes: 6 additions & 5 deletions src/routes/contest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ router.put("/", async (req, res) => {


import * as fs from 'fs';
import { base_directory, get_contest_name } from "../helpers/utils"
import { get_base_directory, get_contest_name } from "../helpers/utils"
/**
* POST launch contest
* @param token
Expand Down Expand Up @@ -287,9 +287,9 @@ router.post("/", async (req, res) => {
}
}
`,
{
contest_id: contest_id,
user_uuid: user_uuid
{
contest_id: contest_id,
user_uuid: user_uuid
}
);
const is_manager = query_if_manager.contest_manager.length != 0;
Expand All @@ -306,13 +306,14 @@ router.post("/", async (req, res) => {
}
}
`,
{
{
contest_id: contest_id,
}
);
const valid_team_ids = query_valid_teams.contest_team;

const contest_name = await get_contest_name(contest_id);
const base_directory = await get_base_directory();
switch (req.body.mode) {
case 0: {
for (let i = 0; i < valid_team_ids.length; i++) {
Expand Down
4 changes: 3 additions & 1 deletion src/routes/room.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { docker_queue } from "..";
import jwt from "jsonwebtoken";
import { JwtUserPayload } from "../middlewares/authenticate";
import * as fs from "fs/promises";
import { base_directory, get_contest_name } from "../helpers/utils";
import { get_base_directory, get_contest_name } from "../helpers/utils";

const router = express.Router();

Expand All @@ -25,6 +25,7 @@ router.post("/", async (req, res) => {
const team_seq = req.body.team_seq as boolean;
const map = req.body.map as number;
const exposed = req.body.exposed as number;
const base_directory = await get_base_directory();
const authHeader = req.get("Authorization");
if (!authHeader) {
return res.status(401).send("401 Unauthorized: Missing token");
Expand Down Expand Up @@ -213,6 +214,7 @@ router.get("/:room_id", async (req, res) => {
// }

const contest_name = await get_contest_name(req.body.contest_id);
const base_directory = await get_base_directory();
const videoPath = req.body.arenic === 1 ? `${base_directory}/${contest_name}/arena/${room_id}/video.thuaipb` : `${base_directory}/${contest_name}/competition/${room_id}/video.thuaipb`;

try {
Expand Down

0 comments on commit 5b73967

Please sign in to comment.