Skip to content

Commit

Permalink
update api (#733)
Browse files Browse the repository at this point in the history
* add player code router

* add player code router

* ignore .env.local

* modify code router, add room router

* add manager delete room network

* rom router check manager

* update room router

* modify graphql query

* update code and room for the latest docker image

* update code path

* queue

* add contest router

* add compile status

* add playback

* update server

* add compile logs

* fix some errors

* update cors

Co-authored-by: Campbell He <[email protected]>
  • Loading branch information
BryantSuen and duskmoon314 authored Apr 20, 2021
1 parent 6bb3fd2 commit ea443ad
Show file tree
Hide file tree
Showing 10 changed files with 1,192 additions and 23 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ typings/

# dotenv environment variables file
.env
.env.local

# next.js build output
.next
Expand Down
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@
"typecheck": "tsc --noUnusedLocals"
},
"dependencies": {
"@types/node-cron": "^2.0.3",
"@babel/runtime": "7.13.10",
"ali-oss": "6.15.2",
"bcrypt": "5.0.1",
"cors": "2.8.5",
"debug": "4.3.1",
"dockerode": "3.2.1",
"dotenv": "8.2.0",
"express": "4.17.1",
"graphql": "15.5.0",
Expand All @@ -26,6 +28,8 @@
"jsonwebtoken": "8.5.1",
"mongoose": "5.11.8",
"morgan": "1.10.0",
"multer": "1.4.2",
"node-cron": "^3.0.0",
"node-fetch": "2.6.1",
"nodemailer": "6.5.0"
},
Expand All @@ -45,12 +49,14 @@
"@types/bcrypt": "3.0.1",
"@types/cors": "2.8.10",
"@types/debug": "4.1.5",
"@types/dockerode": "3.2.2",
"@types/express": "4.17.11",
"@types/html-to-text": "6.0.0",
"@types/jsonwebtoken": "8.5.1",
"@types/mongodb": "3.6.12",
"@types/mongoose": "5.10.3",
"@types/morgan": "1.9.2",
"@types/multer": "1.4.5",
"@types/node-fetch": "2.5.10",
"@types/nodemailer": "6.4.1",
"@typescript-eslint/eslint-plugin": "4.12.0",
Expand Down
1 change: 1 addition & 0 deletions queue_data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
35 changes: 21 additions & 14 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,28 @@ import logger from "morgan";
import staticRouter from "./routes/static";
import userRouter from "./routes/users";
import emailRouter from "./routes/emails";
import codeRouter from "./routes/code";
import roomRouter from "./routes/room";
import contestRouter from "./routes/contest";

const app = express();

if (process.env.NODE_ENV === "production") {
const whitelist = ["https://eesast.com", "http://localhost:3000"];
const whitelist =
process.env.NODE_ENV === "production"
? ["https://eesast.com", "http://localhost:3000"]
: ["http://localhost:3000"];

app.use(
cors({
origin: function (origin, callback) {
if (!origin || whitelist.indexOf(origin) !== -1) {
callback(null, true);
} else {
callback(new Error("Not allowed by CORS"));
}
},
})
);
}
app.use(
cors({
origin: function (origin, callback) {
if (!origin || whitelist.indexOf(origin) !== -1) {
callback(null, true);
} else {
callback(new Error("Not allowed by CORS"));
}
},
})
);

app.use(logger(process.env.NODE_ENV === "production" ? "combined" : "dev"));
app.use(express.json());
Expand All @@ -30,5 +34,8 @@ app.use(express.urlencoded({ extended: true }));
app.use("/static", staticRouter);
app.use("/users", userRouter);
app.use("/emails", emailRouter);
app.use("/code", codeRouter);
app.use("/room", roomRouter);
app.use("/contest", contestRouter);

export default app;
28 changes: 28 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import dotenv from "dotenv";
import http from "http";
import mongoose from "mongoose";
import app from "./app";
import { GraphQLClient } from "graphql-request";
import { queue_element } from "./middlewares/docker_queue";
import docker_cron from "./middlewares/docker_queue";
import fs from "fs";

// // Use for dev
// import path from "path";
Expand Down Expand Up @@ -41,6 +45,21 @@ db.once("open", () => {
debug("Database connected");
});

export const client = new GraphQLClient(
`${process.env.HASURA_URL}/v1/graphql`,
{
headers: {
"Content-Type": "application/json",
"x-hasura-admin-secret": process.env.HASURA_GRAPHQL_ADMIN_SECRET!,
},
}
);

export const docker_queue: queue_element[] = JSON.parse(
fs.readFileSync("./queue_data.json").toString()
);
docker_cron();

const port = normalizePort(process.env.PORT || "28888");
app.set("port", port);

Expand Down Expand Up @@ -73,3 +92,12 @@ server.on("listening", () => {
const bind = typeof addr === "string" ? "pipe " + addr : "port " + addr!.port;
debug("Listening on " + bind);
});

// server.addListener("close",()=>{
// try{
// fs.writeFileSync('./queue_data.json',docker_queue.toString());
// console.log("stop!");
// }catch(err){
// console.log(err);
// }
// })
177 changes: 177 additions & 0 deletions src/middlewares/docker_queue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import cron from "node-cron";
import { docker_queue } from "..";
import Docker from "dockerode";
import jwt from "jsonwebtoken";
import { JwtServerPayload } from "../routes/contest";

export interface queue_element {
room_id: string;
team_id_1: string;
team_id_2: string;
}

const docker_cron = () => {
// cron.schedule("*/10 * * * * *", async () => {
// const docker =
// process.env.DOCKER === "remote"
// ? new Docker({
// host: process.env.DOCKER_URL,
// port: process.env.DOCKER_PORT,
// })
// : new Docker();
// await docker.pruneNetworks({
// filters:'{"until":"5m"}'
// });
// });
// 上面这个本来打算实现自动删除一定时间的network的,但是不知为何 filter 怎么也用不好,所以目前只能使用 crontab 来做。求大佬来帮忙想办法orz
cron.schedule(`*/${process.env.QUEUE_CHECK_TIME} * * * * *`, async () => {
const max_container_num = parseInt(process.env.MAX_CONTAINERS as string);
const docker =
process.env.DOCKER === "remote"
? new Docker({
host: process.env.DOCKER_URL,
port: process.env.DOCKER_PORT,
})
: new Docker();
try {
const existing_containers = await docker.listContainers({
all: true,
});
if (existing_containers.length > max_container_num) return;
else {
const available_num = Math.min(
(max_container_num - existing_containers.length) / 2,
docker_queue.length
);
console.log("队列容量:", max_container_num);
console.log("等待:", available_num);
if (available_num === 0) return;
for (let i = 0; i < available_num; ++i) {
const queue_front = docker_queue.shift() as queue_element;
try {
const existing_networks = await docker.listNetworks();
if (
!existing_networks.filter(
(elem) => elem.Name === `THUAI4_room_${queue_front.room_id}`
).length
) {
await docker.createNetwork({
Name: `THUAI4_room_${queue_front.room_id}`,
});
}
} catch (err) {
console.log(err);
continue;
}
let containerRunning = false;
const containerList = await docker.listContainers();
containerList.forEach((containerInfo) => {
if (
containerInfo.Names.includes(
`/THUAI4_room_server_${queue_front?.room_id}`
) ||
containerInfo.Names.includes(
`/THUAI4_room_client_${queue_front?.room_id}`
)
) {
containerRunning = true;
}
});
if (!containerRunning) {
try {
const serverToken = jwt.sign(
{
room_id: queue_front.room_id,
teams: [
{
team_alias: 0,
team_id: queue_front.team_id_1,
},
{
team_alias: 1,
team_id: queue_front.team_id_2,
},
],
} as JwtServerPayload,
process.env.SECRET!,
{
expiresIn: "10m",
}
);
const url =
process.env.NODE_ENV == "production"
? "https://api.eesast.com/contest"
: "http:127.0.0.1:28888/contest";
const container_server = await docker.createContainer({
Image: process.env.SERVER_IMAGE,
Tty: true,
AttachStdin: false,
AttachStdout: false,
AttachStderr: false,
name: `THUAI4_room_server_${queue_front.room_id}`,
HostConfig: {
NetworkMode: `THUAI4_room_${queue_front.room_id}`,
Binds: [
`/data/thuai4_playback/${queue_front.room_id}/:/usr/local/mnt`, //TODO:路径待定!
],
AutoRemove: true,
},
Cmd: [
process.env.MAX_SERVER_TIMEOUT as string,
"2",
"4",
`${url}`,
`${serverToken}`,
],
});
await container_server.start();
} catch (err) {
console.log(err);
continue;
}

const network = docker.getNetwork(
`THUAI4_room_${queue_front.room_id}`
);
const netInfo = (await network.inspect()) as Docker.NetworkInspectInfo;
const roomIp = Object.values(
netInfo.Containers!
)[0].IPv4Address.split("/")[0];

try {
const container_client = await docker.createContainer({
Image: process.env.AGENTCLIENT_IMAGE,
AttachStdin: false,
AttachStdout: false,
AttachStderr: false,
name: `THUAI4_room_client_${queue_front.room_id}`,
HostConfig: {
NetworkMode: `THUAI4_room_${queue_front.room_id}`,
AutoRemove: true,
Binds: [
`/data/thuai4/${queue_front.team_id_1}/player:/usr/local/mnt/player1`,
`/data/thuai4/${queue_front.team_id_2}/player:/usr/local/mnt/player2`,
],
},
Cmd: [`${roomIp}`, process.env.MAX_CLIENT_TIMEOUT as string],
});
await container_client.start();
} catch (err) {
console.log(err);
continue;
}

console.log("all ok");
} else {
console.log("running");
continue;
}
}
}
} catch (err) {
console.log(err);
}
});
};

export default docker_cron;
Loading

0 comments on commit ea443ad

Please sign in to comment.