From e36c8a569582c3bd211450610405399d844f13ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Wed, 27 Feb 2019 20:44:27 +0700 Subject: [PATCH 01/52] Add time change menu --- src/prompt/contestConfig/time.js | 45 ++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/prompt/contestConfig/time.js diff --git a/src/prompt/contestConfig/time.js b/src/prompt/contestConfig/time.js new file mode 100644 index 00000000..e09f06a8 --- /dev/null +++ b/src/prompt/contestConfig/time.js @@ -0,0 +1,45 @@ +"use strict"; + +const Enquirer = require("enquirer"); + +const enquirer = new Enquirer(); + +const validateTime = (timeStr) => { + return new Date(timeStr).toJSON() === timeStr; +}; + +const getDate = (() => { + const now = new Date(); + now.setMilliseconds(0); + now.setSeconds(0); + now.setMinutes((Math.ceil(now.getMinutes() / 15) + 1) * 15); + return now; +})(); + +const prompt = enquirer.prompt({ + type: "form", + name: "contest", + message: "Please provide the following information:", + choices: [ + { name: "name", message: "Contest name:" }, + { + name: "startTime", + message: "Start time:", + initial: getDate.toJSON(), + validate: validateTime + }, + { + name: "endTime", + message: "End time:", + initial: getDate.toJSON(), + validate: validateTime + } + ] +}); + +prompt + .then((value) => { + return value; + }) + .then(console.log) + .catch(console.error); From 494d33ac5c87394871bce2158ef32cdd999f015f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Thu, 28 Feb 2019 20:18:47 +0700 Subject: [PATCH 02/52] Skip building tag --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index 594f6367..6c7ae1af 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,5 @@ version: 1.{build} +skip_tags: true pull_requests: do_not_increment_build_number: true branches: From 55fd534045b3bc899f1118f4f199b7a294aa7a2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Fri, 1 Mar 2019 18:05:12 +0700 Subject: [PATCH 03/52] Remove duplicate code --- src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 1aa96105..6cb9f5cc 100644 --- a/src/index.js +++ b/src/index.js @@ -29,6 +29,7 @@ async function main() { let res = {}; Console.log("MIRAI Wafter 1.0.0"); Console.log("Copyright (c) 2018 Vườn ươm A0. MIT License."); + try { while (res.main !== "Exit") { res = await mainPrompt(); @@ -41,7 +42,6 @@ async function main() { } } catch (err) { Console.log(err.message); - process.exit(); } process.exit(); } From df2861ee41f2519d968ddcc6d93b5999520a189c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Fri, 1 Mar 2019 19:33:38 +0700 Subject: [PATCH 04/52] Wrap server as module so index.js can call it properly --- src/index.js | 3 +- src/server.js | 151 +++++++++++++++++++++++++++----------------------- 2 files changed, 83 insertions(+), 71 deletions(-) diff --git a/src/index.js b/src/index.js index 6cb9f5cc..9b40c0fd 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,6 @@ const Enquirer = require("enquirer"); const Console = require("console"); +const server = require("./server"); const addUser = require("./prompt/addUser"); const enquirer = new Enquirer(); @@ -36,7 +37,7 @@ async function main() { await mainChoices[res.main](); if (res.main === "Start server") { - require("./server"); + server(); return; } } diff --git a/src/server.js b/src/server.js index be2030e8..209c01e1 100644 --- a/src/server.js +++ b/src/server.js @@ -8,75 +8,86 @@ const session = require("express-session"); const bodyParser = require("body-parser"); const ip = require("ip"); -const server = require("./config/server"); -const passportConfig = require("./controller/passportConfig"); -const initJudger = require("./controller/initJudger"); -const { logToConsole, logToFile } = require("./middleware/log"); - -const info = require("./routes/info"); -const subs = require("./routes/subs"); -const users = require("./routes/users"); -const score = require("./routes/score"); - -passportConfig(passport); -initJudger(); - -const app = express(); - -const PORT = server.port; - -app.use(helmet()); -app.use(helmet.noCache()); -app.use(logToConsole); -app.use(logToFile); - -app.use(bodyParser.urlencoded({ extended: true })); -app.use( - session({ - resave: false, - saveUninitialized: true, - secret: server.secret - }) -); -app.use(passport.initialize()); -app.use(passport.session()); - -// API -app.all("/api", (req, res) => { - res.sendStatus(204); -}); -app.use("/api/info", info); -app.use("/api/subs", subs); -app.use("/api/users", users); -app.use("/api/score", score); - -app.post("/api/login", passport.authenticate("local"), (req, res) => { - res.sendStatus(200); -}); - -app.get("/api/logout", (req, res) => { - req.logout(); - res.sendStatus(200); -}); - -app.all("/api/*", (req, res) => { - res.sendStatus(404); -}); - -app.use("/", express.static(server.staticFolder)); -// Temp solution ? -app.use("/*", (req, res) => { - res.sendFile(server.staticFolder + "/index.html"); -}); - -let serv = app.listen(PORT, () => { - Console.log( - `Wafter is serving at http://${ip.address()}:${serv.address().port}` +/** + * Main server function + */ +function main() { + // These module call config module which read and export itself, + // thus they cannot be called outside main() + const server = require("./config/server"); + const passportConfig = require("./controller/passportConfig"); + const initJudger = require("./controller/initJudger"); + const { logToConsole, logToFile } = require("./middleware/log"); + + const info = require("./routes/info"); + const subs = require("./routes/subs"); + const users = require("./routes/users"); + const score = require("./routes/score"); + + passportConfig(passport); + initJudger(); + + const app = express(); + + const PORT = server.port; + + app.use(helmet()); + app.use(helmet.noCache()); + app.use(logToConsole); + app.use(logToFile); + + app.use(bodyParser.urlencoded({ extended: true })); + app.use( + session({ + resave: false, + saveUninitialized: true, + secret: server.secret + }) ); -}); -process.on("exit", () => { - serv.close(() => { - Console.log("Closing server"); - process.exit(0); + app.use(passport.initialize()); + app.use(passport.session()); + + // API + app.all("/api", (req, res) => { + res.sendStatus(204); + }); + app.use("/api/info", info); + app.use("/api/subs", subs); + app.use("/api/users", users); + app.use("/api/score", score); + + app.post("/api/login", passport.authenticate("local"), (req, res) => { + res.sendStatus(200); + }); + + app.get("/api/logout", (req, res) => { + req.logout(); + res.sendStatus(200); }); -}); + + app.all("/api/*", (req, res) => { + res.sendStatus(404); + }); + + app.use("/", express.static(server.staticFolder)); + // Temp solution ? + app.use("/*", (req, res) => { + res.sendFile(server.staticFolder + "/index.html"); + }); + + let serv = app.listen(PORT, () => { + Console.log( + `Wafter is serving at http://${ip.address()}:${serv.address().port}` + ); + }); + + process.on("exit", () => { + Console.log("Shutting down Wafter"); + // No need to callback because it's time consuming + serv.close(); + }); + + return serv; +} + +module.exports = main; From 97d3cc8f6c22f84d5813f7082dc2b9f9f9853e3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Fri, 1 Mar 2019 22:07:10 +0700 Subject: [PATCH 05/52] Rewrite time interface Phew, that's a lot of work --- src/prompt/contestConfig/time.js | 80 +++++++++++++++++++------------- 1 file changed, 49 insertions(+), 31 deletions(-) diff --git a/src/prompt/contestConfig/time.js b/src/prompt/contestConfig/time.js index e09f06a8..ce1164be 100644 --- a/src/prompt/contestConfig/time.js +++ b/src/prompt/contestConfig/time.js @@ -4,42 +4,60 @@ const Enquirer = require("enquirer"); const enquirer = new Enquirer(); -const validateTime = (timeStr) => { - return new Date(timeStr).toJSON() === timeStr; -}; - -const getDate = (() => { +const getNow = () => { const now = new Date(); now.setMilliseconds(0); now.setSeconds(0); now.setMinutes((Math.ceil(now.getMinutes() / 15) + 1) * 15); return now; +}; + +const timeList = (() => { + let now = getNow(); + const res = {}; + for (let i = 0; i < 40; ++i) { + res[now.toLocaleString()] = now; + now.setMinutes(now.getMinutes() + 15); + } + return res; })(); -const prompt = enquirer.prompt({ - type: "form", - name: "contest", - message: "Please provide the following information:", - choices: [ - { name: "name", message: "Contest name:" }, - { - name: "startTime", - message: "Start time:", - initial: getDate.toJSON(), - validate: validateTime - }, - { - name: "endTime", - message: "End time:", - initial: getDate.toJSON(), - validate: validateTime - } - ] -}); +const offSetList = (() => { + let now = new Date(0); + const res = {}; + for (let i = 1; i <= 20; ++i) { + now.setMinutes(now.getMinutes() + 15); + let str = ""; + if (now.getHours() > 7) + str += (now.getHours() - 7).toString() + " hour(s) "; + if (now.getMinutes() !== 0) + str += now.getMinutes().toString() + " minute(s)"; + res[str.trim()] = i * 15 * 60 * 1000; + } + return res; +})(); -prompt - .then((value) => { - return value; - }) - .then(console.log) - .catch(console.error); +const timePrompt = enquirer.prompt([ + { + type: "select", + name: "startTime", + message: "Start time:", + choices: Object.keys(timeList), + limit: 5, + result: (val) => timeList[val] + }, + { + type: "select", + name: "offset", + message: "Duration:", + choices: Object.keys(offSetList), + limit: 5, + initial: "1 hour(s)", + result: (val) => offSetList[val] + } +]); + +module.exports = timePrompt.then((value) => { + value.endTime = new Date(value.startTime.getTime() + value.offset); + delete value.offset; +}); From 76df3c177c3bb7c7b0a177be36ac2d11d9bec279 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sat, 2 Mar 2019 06:54:08 +0700 Subject: [PATCH 06/52] Improve UX --- src/prompt/contestConfig/time.js | 60 +++++++++++++++++--------------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/src/prompt/contestConfig/time.js b/src/prompt/contestConfig/time.js index ce1164be..7d137a55 100644 --- a/src/prompt/contestConfig/time.js +++ b/src/prompt/contestConfig/time.js @@ -25,39 +25,43 @@ const timeList = (() => { const offSetList = (() => { let now = new Date(0); const res = {}; - for (let i = 1; i <= 20; ++i) { + for (let i = 1; i <= 40; ++i) { now.setMinutes(now.getMinutes() + 15); let str = ""; - if (now.getHours() > 7) - str += (now.getHours() - 7).toString() + " hour(s) "; - if (now.getMinutes() !== 0) - str += now.getMinutes().toString() + " minute(s)"; + if (now.getHours() > 7) str += (now.getHours() - 7).toString() + " hr "; + if (now.getMinutes() !== 0) str += now.getMinutes().toString() + " min"; res[str.trim()] = i * 15 * 60 * 1000; } return res; })(); -const timePrompt = enquirer.prompt([ - { - type: "select", - name: "startTime", - message: "Start time:", - choices: Object.keys(timeList), - limit: 5, - result: (val) => timeList[val] - }, - { - type: "select", - name: "offset", - message: "Duration:", - choices: Object.keys(offSetList), - limit: 5, - initial: "1 hour(s)", - result: (val) => offSetList[val] - } -]); +/** + * Change time prompt + */ +async function timePrompt() { + const { startTime, endTime } = enquirer + .prompt([ + { + type: "select", + name: "startTime", + message: "Start time:", + choices: Object.keys(timeList), + limit: 5, + result: (val) => timeList[val] + }, + { + type: "select", + name: "offset", + message: "Duration:", + choices: Object.keys(offSetList), + limit: 5, + initial: "1 hr", + result: (val) => offSetList[val] + } + ]) + .then((value) => { + value.endTime = new Date(value.startTime.getTime() + value.offset); + }); +} -module.exports = timePrompt.then((value) => { - value.endTime = new Date(value.startTime.getTime() + value.offset); - delete value.offset; -}); +module.exports = timePrompt; From cf14aa9743e64acd8545f709a6197951488fdfe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sat, 2 Mar 2019 09:00:23 +0700 Subject: [PATCH 07/52] Rewrite config system to match new spec --- src/config/contest.js | 4 +- src/config/kon.js | 5 ++- src/config/server.js | 4 +- src/util/config.js | 42 +++++++++++++++++++ .../config/{readKonConfig.js => KonConfig.js} | 30 +++---------- .../{readCtConfig.js => contestConfig.js} | 31 ++++++++------ .../{readServerConfig.js => serverConfig.js} | 2 +- src/util/getNow.js | 12 ++++++ src/util/readConfig.js | 17 -------- 9 files changed, 86 insertions(+), 61 deletions(-) create mode 100644 src/util/config.js rename src/util/config/{readKonConfig.js => KonConfig.js} (53%) rename src/util/config/{readCtConfig.js => contestConfig.js} (73%) rename src/util/config/{readServerConfig.js => serverConfig.js} (92%) create mode 100644 src/util/getNow.js delete mode 100644 src/util/readConfig.js diff --git a/src/config/contest.js b/src/config/contest.js index f56e2336..e4bf4586 100644 --- a/src/config/contest.js +++ b/src/config/contest.js @@ -1,5 +1,5 @@ "use strict"; -const contestConfig = require("../util/config/readCtConfig"); +const contestConfig = require("../util/config/contestConfig"); -module.exports = contestConfig.config(); +module.exports = contestConfig.read(); diff --git a/src/config/kon.js b/src/config/kon.js index 41242a80..ff9aec80 100644 --- a/src/config/kon.js +++ b/src/config/kon.js @@ -2,7 +2,8 @@ const { existsSync, mkdirSync } = require("fs"); -const KonConfig = require("../util/config/readKonConfig"); +const Judger = require("../driver/kon"); +const KonConfig = require("../util/config/KonConfig"); // Require valid folder to work const taskFolder = "Tasks"; @@ -10,6 +11,6 @@ const taskFolder = "Tasks"; if (!existsSync(taskFolder)) mkdirSync(taskFolder); module.exports = { - judgers: KonConfig.config(), + judgers: KonConfig.read().map((kon) => new Judger(kon.url, kon.prob)), tasks: taskFolder }; diff --git a/src/config/server.js b/src/config/server.js index 8b1e9e0d..607922be 100644 --- a/src/config/server.js +++ b/src/config/server.js @@ -3,7 +3,7 @@ const path = require("path"); const { existsSync, mkdirSync, writeFileSync } = require("fs"); -const serverConfig = require("../util/config/readServerConfig"); +const serverConfig = require("../util/config/serverConfig"); const staticFolder = path.join(__dirname, "../../public"); if (!existsSync(staticFolder)) mkdirSync(staticFolder); @@ -12,7 +12,7 @@ const sampleHTML = if (!existsSync(staticFolder + "/index.html")) writeFileSync(staticFolder + "/index.html", sampleHTML); -const serverCfg = Object.assign(serverConfig.config(), { +const serverCfg = Object.assign(serverConfig(), { staticFolder: staticFolder }); diff --git a/src/util/config.js b/src/util/config.js new file mode 100644 index 00000000..d03dee46 --- /dev/null +++ b/src/util/config.js @@ -0,0 +1,42 @@ +"use strict"; + +const { readFileSync } = require("fs"); +const { writeFileSync } = require("fs"); + +class ConfigObject { + constructor(file, parse = (x) => x, sample = () => {}) { + this.file = file; + this.parse = parse; + this.sample = sample; + } + genIfNotExist() { + this.write(this.sample()); + } + read() { + try { + const rawData = readFileSync(this.file, "utf8"); + return this.parse(JSON.parse(rawData)); + } catch (err) { + throw new Error( + `Cannot read config file: "${this.file}": ${err.message}` + ); + } + } + write(data) { + try { + const parsedData = JSON.stringify(this.parse(data), null, 4); + return writeFileSync(this.file, parsedData); + } catch (err) { + throw new Error( + `Cannot write config file: "${this.file}": ${err.message}` + ); + } + } + update(data) { + const store = this.read(); + const updated = Object.assign(store, data); + this.write(updated); + } +} + +module.exports = ConfigObject; diff --git a/src/util/config/readKonConfig.js b/src/util/config/KonConfig.js similarity index 53% rename from src/util/config/readKonConfig.js rename to src/util/config/KonConfig.js index 85ee0e2d..3da52709 100644 --- a/src/util/config/readKonConfig.js +++ b/src/util/config/KonConfig.js @@ -1,12 +1,9 @@ "use strict"; -const { existsSync, writeFileSync } = require("fs"); - const validUrl = require("valid-url"); -const Judger = require("../../driver/kon"); const parseProbList = require("../parseProbList"); -const readConfig = require("../readConfig"); +const Config = require("../config"); /** * Parse & validate Kon config data into formatted one @@ -27,25 +24,10 @@ function parseKonConfig(configData) { return arr; }, []) .filter((kon) => validUrl.isWebUri(kon.url)) - .map((kon) => new Judger(kon.url, parseProbList(kon.prob))); -} - -/** - * Read kon config - */ -function KonConfig() { - const konListFile = "kon.json"; - try { - // Pre-create konList - if (!existsSync(konListFile)) writeFileSync(konListFile, "[]"); - - return parseKonConfig(readConfig(konListFile)); - } catch (err) { - throw new Error(`Failed to read config for Kon: ${err.message}`); - } + .map((kon) => ({ + url: kon.url, + prob: parseProbList(kon.prob) + })); } -module.exports = { - config: KonConfig, - parse: parseKonConfig -}; +module.exports = new Config("kon.json", parseKonConfig, () => "[]"); diff --git a/src/util/config/readCtConfig.js b/src/util/config/contestConfig.js similarity index 73% rename from src/util/config/readCtConfig.js rename to src/util/config/contestConfig.js index 05b82ee7..3d200f71 100644 --- a/src/util/config/readCtConfig.js +++ b/src/util/config/contestConfig.js @@ -1,7 +1,8 @@ "use strict"; const score = require("../score"); -const readConfig = require("../readConfig"); +const Config = require("../config"); +const getNow = require("../getNow"); /** * Parse ISO 8601 into Date object @@ -41,6 +42,7 @@ function parseCtCfg(configData) { name = String(name); mode = String(mode); + if (!score.hasOwnProperty(mode)) throw new Error("Invalid mode"); startTime = parseTime(startTime); @@ -59,18 +61,21 @@ function parseCtCfg(configData) { } /** - * Read Config from contest.json + * Create sample of contest config */ -function contestConfig() { - const ctCfgFile = "contest.json"; - try { - return parseCtCfg(readConfig(ctCfgFile)); - } catch (err) { - throw new Error(`Invalid contest file (${ctCfgFile}): ${err.message}`); - } +function getSample() { + const now = getNow(); + const start = now.toJSON(); + now.setHours(now.getHours() + 1); + const end = now.toJSON(); + return { + name: "Sample Contest", + mode: "OI", + startTime: start, + endTime: end, + probList: [], + allowedCodeExt: [".CPP", ".C"] + }; } -module.exports = { - config: contestConfig, - parse: parseCtCfg -}; +module.exports = new Config("contest.json", parseCtCfg, getSample); diff --git a/src/util/config/readServerConfig.js b/src/util/config/serverConfig.js similarity index 92% rename from src/util/config/readServerConfig.js rename to src/util/config/serverConfig.js index 71be3114..85d5c5b6 100644 --- a/src/util/config/readServerConfig.js +++ b/src/util/config/serverConfig.js @@ -19,4 +19,4 @@ function serverConfig() { }; } -module.exports.config = serverConfig; +module.exports = serverConfig; diff --git a/src/util/getNow.js b/src/util/getNow.js new file mode 100644 index 00000000..c40dd874 --- /dev/null +++ b/src/util/getNow.js @@ -0,0 +1,12 @@ +/** + * Get nearest '15 time + */ +const getNow = () => { + const now = new Date(); + now.setMilliseconds(0); + now.setSeconds(0); + now.setMinutes((Math.ceil(now.getMinutes() / 15) + 1) * 15); + return now; +}; + +module.exports = getNow; diff --git a/src/util/readConfig.js b/src/util/readConfig.js deleted file mode 100644 index 674402ac..00000000 --- a/src/util/readConfig.js +++ /dev/null @@ -1,17 +0,0 @@ -"use strict"; - -const { readFileSync } = require("fs"); - -/** - * Read Config File - * @param {String} configFile File name - */ -function readConfig(configFile) { - try { - return JSON.parse(readFileSync(configFile, "utf8")); - } catch (err) { - throw new Error(`Cannot read config file: "${configFile}."`); - } -} - -module.exports = readConfig; From bc55648fd30df2a1aa35ae2bf2222f701488397a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sat, 2 Mar 2019 09:00:48 +0700 Subject: [PATCH 08/52] Reflect new config system --- src/prompt/contestConfig/time.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/prompt/contestConfig/time.js b/src/prompt/contestConfig/time.js index 7d137a55..584ce69f 100644 --- a/src/prompt/contestConfig/time.js +++ b/src/prompt/contestConfig/time.js @@ -1,17 +1,11 @@ "use strict"; const Enquirer = require("enquirer"); +const getNow = require("../../util/getNow"); +const contestConfig = require("../../util/config/contestConfig"); const enquirer = new Enquirer(); -const getNow = () => { - const now = new Date(); - now.setMilliseconds(0); - now.setSeconds(0); - now.setMinutes((Math.ceil(now.getMinutes() / 15) + 1) * 15); - return now; -}; - const timeList = (() => { let now = getNow(); const res = {}; @@ -39,7 +33,8 @@ const offSetList = (() => { * Change time prompt */ async function timePrompt() { - const { startTime, endTime } = enquirer + contestConfig.genIfNotExist(); + const { startTime, endTime } = await enquirer .prompt([ { type: "select", @@ -61,7 +56,12 @@ async function timePrompt() { ]) .then((value) => { value.endTime = new Date(value.startTime.getTime() + value.offset); + value.startTime = value.startTime.toJSON(); + value.endTime = value.endTime.toJSON(); + return value; }); + + contestConfig.update({ startTime, endTime }); } module.exports = timePrompt; From 52c7e5be18e83f91268abaebe7f79d2e0b4663fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sat, 2 Mar 2019 09:21:45 +0700 Subject: [PATCH 09/52] Fix genIfNotExist() --- src/util/config.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/util/config.js b/src/util/config.js index d03dee46..28a1d03a 100644 --- a/src/util/config.js +++ b/src/util/config.js @@ -1,7 +1,6 @@ "use strict"; -const { readFileSync } = require("fs"); -const { writeFileSync } = require("fs"); +const { readFileSync, writeFileSync, existsSync } = require("fs"); class ConfigObject { constructor(file, parse = (x) => x, sample = () => {}) { @@ -10,7 +9,7 @@ class ConfigObject { this.sample = sample; } genIfNotExist() { - this.write(this.sample()); + if (!existsSync(this.file)) this.write(this.sample()); } read() { try { From 78b25e599665ca35ac7e89546a8e4900d6d6e573 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sat, 2 Mar 2019 09:22:17 +0700 Subject: [PATCH 10/52] Add contestOptions menu --- src/index.js | 4 +++- src/prompt/contestOptions.js | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 src/prompt/contestOptions.js diff --git a/src/index.js b/src/index.js index 1aa96105..881a5ee6 100644 --- a/src/index.js +++ b/src/index.js @@ -1,12 +1,14 @@ const Enquirer = require("enquirer"); const Console = require("console"); const addUser = require("./prompt/addUser"); +const contestOptions = require("./prompt/contestOptions"); const enquirer = new Enquirer(); const mainChoices = { "Start server": () => {}, - "Add user": () => addUser(), + "Add user": addUser, + "Contest options": contestOptions, Exit: () => {} }; diff --git a/src/prompt/contestOptions.js b/src/prompt/contestOptions.js new file mode 100644 index 00000000..8859b4ac --- /dev/null +++ b/src/prompt/contestOptions.js @@ -0,0 +1,33 @@ +const Console = require("console"); +const Enquirer = require("enquirer"); + +const time = require("./contestConfig/time"); + +const enquirer = new Enquirer(); + +/** + * Prompt for contest's option + */ +async function contestOptionsPrompt() { + let contestOptionsContainer = { + "Change time": time + }; + + const { choice } = await enquirer.prompt({ + type: "select", + name: "choice", + message: "Contest Options", + choices: Object.keys(contestOptionsContainer) + }); + + return contestOptionsContainer[choice]().then( + () => { + Console.log("Contest's configuration saved"); + }, + () => { + Console.log("Contest's configuration cannot be saved :("); + } + ); +} + +module.exports = contestOptionsPrompt; From 587dacae1f5ac191896e125ea5d79994045892d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sat, 2 Mar 2019 09:22:34 +0700 Subject: [PATCH 11/52] Improve UI of timePrompt --- src/prompt/contestConfig/time.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/prompt/contestConfig/time.js b/src/prompt/contestConfig/time.js index 584ce69f..f4f6e279 100644 --- a/src/prompt/contestConfig/time.js +++ b/src/prompt/contestConfig/time.js @@ -41,7 +41,7 @@ async function timePrompt() { name: "startTime", message: "Start time:", choices: Object.keys(timeList), - limit: 5, + limit: 1, result: (val) => timeList[val] }, { @@ -49,8 +49,7 @@ async function timePrompt() { name: "offset", message: "Duration:", choices: Object.keys(offSetList), - limit: 5, - initial: "1 hr", + limit: 1, result: (val) => offSetList[val] } ]) From 421356df092efeda0730a756ffc425dc74c44f94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sat, 2 Mar 2019 09:23:49 +0700 Subject: [PATCH 12/52] Edit text on reject --- src/prompt/contestOptions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/prompt/contestOptions.js b/src/prompt/contestOptions.js index 8859b4ac..e94a93bb 100644 --- a/src/prompt/contestOptions.js +++ b/src/prompt/contestOptions.js @@ -25,7 +25,7 @@ async function contestOptionsPrompt() { Console.log("Contest's configuration saved"); }, () => { - Console.log("Contest's configuration cannot be saved :("); + Console.log("Contest's configuration was not saved :("); } ); } From 5a2873e9feaf2cf4ca6b8844f740a85fce8d344d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sat, 2 Mar 2019 10:16:50 +0700 Subject: [PATCH 13/52] FIx incorrect date configured --- src/prompt/contestConfig/time.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/prompt/contestConfig/time.js b/src/prompt/contestConfig/time.js index f4f6e279..7407b567 100644 --- a/src/prompt/contestConfig/time.js +++ b/src/prompt/contestConfig/time.js @@ -10,7 +10,7 @@ const timeList = (() => { let now = getNow(); const res = {}; for (let i = 0; i < 40; ++i) { - res[now.toLocaleString()] = now; + res[now.toLocaleString()] = new Date(now); now.setMinutes(now.getMinutes() + 15); } return res; From 1c612ef4c978b74085b24318ed738dae4be81e36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sat, 2 Mar 2019 10:28:25 +0700 Subject: [PATCH 14/52] Fix return --- src/util/config/KonConfig.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/config/KonConfig.js b/src/util/config/KonConfig.js index 3da52709..72118651 100644 --- a/src/util/config/KonConfig.js +++ b/src/util/config/KonConfig.js @@ -30,4 +30,4 @@ function parseKonConfig(configData) { })); } -module.exports = new Config("kon.json", parseKonConfig, () => "[]"); +module.exports = new Config("kon.json", parseKonConfig, () => []); From 0a2f4386ad47eeb4fb193cd040ce8260b828b78e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sat, 2 Mar 2019 10:28:45 +0700 Subject: [PATCH 15/52] Generate config file from menu --- src/index.js | 5 +++++ src/prompt/contestConfig/time.js | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 881a5ee6..bbe58f94 100644 --- a/src/index.js +++ b/src/index.js @@ -1,8 +1,12 @@ const Enquirer = require("enquirer"); const Console = require("console"); + const addUser = require("./prompt/addUser"); const contestOptions = require("./prompt/contestOptions"); +require("./util/config/contestConfig").genIfNotExist(); +require("./util/config/KonConfig").genIfNotExist(); + const enquirer = new Enquirer(); const mainChoices = { @@ -29,6 +33,7 @@ async function mainPrompt() { */ async function main() { let res = {}; + Console.log("MIRAI Wafter 1.0.0"); Console.log("Copyright (c) 2018 Vườn ươm A0. MIT License."); try { diff --git a/src/prompt/contestConfig/time.js b/src/prompt/contestConfig/time.js index 7407b567..7f543556 100644 --- a/src/prompt/contestConfig/time.js +++ b/src/prompt/contestConfig/time.js @@ -33,7 +33,6 @@ const offSetList = (() => { * Change time prompt */ async function timePrompt() { - contestConfig.genIfNotExist(); const { startTime, endTime } = await enquirer .prompt([ { From d361871af01e3d0129b93aafd1114122a4dcb0f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sat, 2 Mar 2019 11:00:58 +0700 Subject: [PATCH 16/52] Patch conversation between ISO 8601 and Date() --- src/config/contest.js | 6 +++++- src/util/config/contestConfig.js | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/config/contest.js b/src/config/contest.js index e4bf4586..80cb3969 100644 --- a/src/config/contest.js +++ b/src/config/contest.js @@ -2,4 +2,8 @@ const contestConfig = require("../util/config/contestConfig"); -module.exports = contestConfig.read(); +const contest = contestConfig.read(); +contest.startTime = new Date(contest.startTime); +contest.endTime = new Date(contest.endTime); + +module.exports = contest; diff --git a/src/util/config/contestConfig.js b/src/util/config/contestConfig.js index 3d200f71..d4c970e7 100644 --- a/src/util/config/contestConfig.js +++ b/src/util/config/contestConfig.js @@ -45,8 +45,8 @@ function parseCtCfg(configData) { if (!score.hasOwnProperty(mode)) throw new Error("Invalid mode"); - startTime = parseTime(startTime); - endTime = parseTime(endTime); + startTime = parseTime(startTime).toJSON(); + endTime = parseTime(endTime).toJSON(); if (startTime >= endTime) throw new Error("Start time is after end time"); From 7fa036368e0f93a449735a7dd568924cd77aa736 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sat, 2 Mar 2019 11:01:12 +0700 Subject: [PATCH 17/52] Add mode and name prompt --- src/prompt/contestConfig/mode.js | 23 +++++++++++++++++++++++ src/prompt/contestConfig/name.js | 21 +++++++++++++++++++++ src/prompt/contestOptions.js | 10 +++++++--- 3 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 src/prompt/contestConfig/mode.js create mode 100644 src/prompt/contestConfig/name.js diff --git a/src/prompt/contestConfig/mode.js b/src/prompt/contestConfig/mode.js new file mode 100644 index 00000000..2e2875d3 --- /dev/null +++ b/src/prompt/contestConfig/mode.js @@ -0,0 +1,23 @@ +"use strict"; + +const Enquirer = require("enquirer"); +const contestConfig = require("../../util/config/contestConfig"); +const score = require("../../util/score"); + +const enquirer = new Enquirer(); + +/** + * Contest mode prompt + */ +async function modePrompt() { + const { mode } = await enquirer.prompt({ + type: "select", + name: "mode", + message: "Contest mode:", + choices: Object.keys(score) + }); + + contestConfig.update({ mode }); +} + +module.exports = modePrompt; diff --git a/src/prompt/contestConfig/name.js b/src/prompt/contestConfig/name.js new file mode 100644 index 00000000..42aa7b2a --- /dev/null +++ b/src/prompt/contestConfig/name.js @@ -0,0 +1,21 @@ +"use strict"; + +const Enquirer = require("enquirer"); +const contestConfig = require("../../util/config/contestConfig"); + +const enquirer = new Enquirer(); + +/** + * Contest name prompt + */ +async function namePrompt() { + const { name } = await enquirer.prompt({ + type: "input", + name: "name", + message: "Contest name:" + }); + + contestConfig.update({ name }); +} + +module.exports = namePrompt; diff --git a/src/prompt/contestOptions.js b/src/prompt/contestOptions.js index e94a93bb..3fd27767 100644 --- a/src/prompt/contestOptions.js +++ b/src/prompt/contestOptions.js @@ -2,6 +2,8 @@ const Console = require("console"); const Enquirer = require("enquirer"); const time = require("./contestConfig/time"); +const mode = require("./contestConfig/mode"); +const name = require("./contestConfig/name"); const enquirer = new Enquirer(); @@ -10,7 +12,9 @@ const enquirer = new Enquirer(); */ async function contestOptionsPrompt() { let contestOptionsContainer = { - "Change time": time + "Change name": name, + "Change time": time, + "Change mode": mode }; const { choice } = await enquirer.prompt({ @@ -24,8 +28,8 @@ async function contestOptionsPrompt() { () => { Console.log("Contest's configuration saved"); }, - () => { - Console.log("Contest's configuration was not saved :("); + (err) => { + Console.log("Contest's configuration was not saved: ", err.message); } ); } From 66b27a27b17471bc5c98ee9020a191563f7995a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sat, 2 Mar 2019 11:35:35 +0700 Subject: [PATCH 18/52] Revert conversation change and remove parsing when writing --- src/config/contest.js | 6 +----- src/util/config.js | 2 +- src/util/config/contestConfig.js | 4 ++-- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/config/contest.js b/src/config/contest.js index 80cb3969..e4bf4586 100644 --- a/src/config/contest.js +++ b/src/config/contest.js @@ -2,8 +2,4 @@ const contestConfig = require("../util/config/contestConfig"); -const contest = contestConfig.read(); -contest.startTime = new Date(contest.startTime); -contest.endTime = new Date(contest.endTime); - -module.exports = contest; +module.exports = contestConfig.read(); diff --git a/src/util/config.js b/src/util/config.js index 28a1d03a..2dedf0fa 100644 --- a/src/util/config.js +++ b/src/util/config.js @@ -23,7 +23,7 @@ class ConfigObject { } write(data) { try { - const parsedData = JSON.stringify(this.parse(data), null, 4); + const parsedData = JSON.stringify(data, null, 4); return writeFileSync(this.file, parsedData); } catch (err) { throw new Error( diff --git a/src/util/config/contestConfig.js b/src/util/config/contestConfig.js index d4c970e7..3d200f71 100644 --- a/src/util/config/contestConfig.js +++ b/src/util/config/contestConfig.js @@ -45,8 +45,8 @@ function parseCtCfg(configData) { if (!score.hasOwnProperty(mode)) throw new Error("Invalid mode"); - startTime = parseTime(startTime).toJSON(); - endTime = parseTime(endTime).toJSON(); + startTime = parseTime(startTime); + endTime = parseTime(endTime); if (startTime >= endTime) throw new Error("Start time is after end time"); From 42845435d97b5aada6d882e388657a0ddc861dac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sun, 3 Mar 2019 07:41:41 +0700 Subject: [PATCH 19/52] Revert change in result of konConfig after parsing --- src/config/kon.js | 3 +-- src/util/config/KonConfig.js | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/config/kon.js b/src/config/kon.js index ff9aec80..d5b6b8b0 100644 --- a/src/config/kon.js +++ b/src/config/kon.js @@ -2,7 +2,6 @@ const { existsSync, mkdirSync } = require("fs"); -const Judger = require("../driver/kon"); const KonConfig = require("../util/config/KonConfig"); // Require valid folder to work @@ -11,6 +10,6 @@ const taskFolder = "Tasks"; if (!existsSync(taskFolder)) mkdirSync(taskFolder); module.exports = { - judgers: KonConfig.read().map((kon) => new Judger(kon.url, kon.prob)), + judgers: KonConfig.read(), tasks: taskFolder }; diff --git a/src/util/config/KonConfig.js b/src/util/config/KonConfig.js index 72118651..3bca07a2 100644 --- a/src/util/config/KonConfig.js +++ b/src/util/config/KonConfig.js @@ -2,6 +2,7 @@ const validUrl = require("valid-url"); +const Judger = require("../../driver/kon"); const parseProbList = require("../parseProbList"); const Config = require("../config"); @@ -24,10 +25,7 @@ function parseKonConfig(configData) { return arr; }, []) .filter((kon) => validUrl.isWebUri(kon.url)) - .map((kon) => ({ - url: kon.url, - prob: parseProbList(kon.prob) - })); + .map((kon) => new Judger(kon.url, parseProbList(kon.prob))); } module.exports = new Config("kon.json", parseKonConfig, () => []); From e55433633a2fb2ba7a7bd0af934f9f85eb264cef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sun, 3 Mar 2019 07:43:05 +0700 Subject: [PATCH 20/52] Use toJSON to print Kon Judger --- src/driver/kon.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/driver/kon.js b/src/driver/kon.js index 9e49bdcd..5e31ea32 100644 --- a/src/driver/kon.js +++ b/src/driver/kon.js @@ -11,6 +11,12 @@ class Judger { this.serverAddress = server_address; this.probList = prob_list; } + toJSON() { + return { + url: this.serverAddress, + prob: this.probList + }; + } /** * Part 1: checking status of availability of judger * @return {Promise} true if judger is available, false otherwise From 0ffc9ee71b05a67345cc33b206f5428489f9078b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sun, 3 Mar 2019 14:26:16 +0700 Subject: [PATCH 21/52] Fix Error when Wafter doesn't throw error on pairing with Kon --- src/controller/initJudger.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/controller/initJudger.js b/src/controller/initJudger.js index 49f89b95..f06e5286 100644 --- a/src/controller/initJudger.js +++ b/src/controller/initJudger.js @@ -17,9 +17,13 @@ function initJudger() { judger .clone(arcPath) .then((res) => { - if (!res.status === 200) - throw Error("Kon isn't return 200"); - Console.log(`Sucessfully cloned ${judger.serverAddress}`); + if (res.status === 400) + Console.log(`Kon was in used. ${judger.serverAddress}`); + else if (res.status === 200) + Console.log( + `Sucessfully cloned ${judger.serverAddress}` + ); + else throw Error("Cannot verified Kon"); }) .catch((err) => { Console.log( From 4f1673ffee262f1bbb1ba88fe3e3a6e7cb477294 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sun, 3 Mar 2019 20:07:54 +0700 Subject: [PATCH 22/52] Improve container filtering duplication, non-string value, spaced, ... --- src/util/config/contestConfig.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/util/config/contestConfig.js b/src/util/config/contestConfig.js index 3d200f71..84e8efba 100644 --- a/src/util/config/contestConfig.js +++ b/src/util/config/contestConfig.js @@ -21,9 +21,11 @@ function parseTime(timeData) { */ function parseContainer(container) { if (!Array.isArray(container)) throw new Error("Invalid Container"); - return container - .map((x) => String(x).toUpperCase()) + let parsedContainer = container + .filter((x) => typeof x === "string") + .map((x) => x.trim().toUpperCase()) .sort((a, b) => a.localeCompare(b)); + return [...new Set(parsedContainer)]; } /** From faf0b60143f639cf6dc4c27ce82324912fac742d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sun, 3 Mar 2019 20:08:09 +0700 Subject: [PATCH 23/52] Add ace prompt --- src/prompt/contestConfig/ace.js | 38 +++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/prompt/contestConfig/ace.js diff --git a/src/prompt/contestConfig/ace.js b/src/prompt/contestConfig/ace.js new file mode 100644 index 00000000..3e0080eb --- /dev/null +++ b/src/prompt/contestConfig/ace.js @@ -0,0 +1,38 @@ +"use strict"; + +const Enquirer = require("enquirer"); +const contestConfig = require("../../util/config/contestConfig"); + +const enquirer = new Enquirer(); + +/** + * Contest name prompt + */ +async function acePrompt() { + let _ace = contestConfig.read().allowedCodeExt; + + // Temporary solution: create list of language, then filter non-included + const langList = [".C", ".CPP", ".JAVA", ".KT", ".PAS", ".PY"]; + const initAce = langList.filter((x) => _ace.indexOf(x) > -1); + const leftAce = _ace.filter((x) => langList.indexOf(x) === -1); + const { ace } = await enquirer.prompt({ + type: "multiselect", + name: "ace", + message: "Set allowed code extension", + hint: "Use to select, to submit", + initial: initAce, + choices: [ + { name: ".C", hint: "- C" }, + { name: ".CPP", hint: "- C++" }, + { name: ".JAVA", hint: "- Java" }, + { name: ".KT", hint: "- Kotlin" }, + { name: ".PAS", hint: "- Pascal" }, + { name: ".PY", hint: "- Python" } + ] + }); + contestConfig.update({ allowedCodeExt: ace.concat(leftAce) }); +} + +module.exports = acePrompt; + +acePrompt(); From 7085a28514ae239b0d86a2726ab35f280eb0271f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sun, 3 Mar 2019 20:08:17 +0700 Subject: [PATCH 24/52] add probList prompt --- src/prompt/contestConfig/probList.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/prompt/contestConfig/probList.js diff --git a/src/prompt/contestConfig/probList.js b/src/prompt/contestConfig/probList.js new file mode 100644 index 00000000..313ee07f --- /dev/null +++ b/src/prompt/contestConfig/probList.js @@ -0,0 +1,22 @@ +"use strict"; + +const Enquirer = require("enquirer"); +const contestConfig = require("../../util/config/contestConfig"); + +const enquirer = new Enquirer(); + +/** + * Contest name prompt + */ +async function probListPrompt() { + let _probList = contestConfig.read().probList; + const { probList } = await enquirer.prompt({ + type: "list", + name: "probList", + message: "Add problem to contest:" + }); + _probList = _probList.concat(probList); + contestConfig.update({ probList: _probList }); +} + +module.exports = probListPrompt; From 4c037337474116dc1efa7cf8256ebaea7c3852f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sun, 3 Mar 2019 20:21:05 +0700 Subject: [PATCH 25/52] Do parsing when writing data --- src/util/config.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/util/config.js b/src/util/config.js index 2dedf0fa..5475b37a 100644 --- a/src/util/config.js +++ b/src/util/config.js @@ -22,9 +22,21 @@ class ConfigObject { } } write(data) { + let exportData = null; + + try { + // Paraphrase data in JSON to strict type + const jsonized = JSON.parse(JSON.stringify(data)); + const parsedData = this.parse(jsonized); + exportData = JSON.stringify(parsedData, null, 4); + } catch (err) { + throw new Error( + `Failed to parse config file "${this.file}": ${err.message}` + ); + } + try { - const parsedData = JSON.stringify(data, null, 4); - return writeFileSync(this.file, parsedData); + return writeFileSync(this.file, exportData); } catch (err) { throw new Error( `Cannot write config file: "${this.file}": ${err.message}` From 170e39f71f07c850f87ec5b151380a077edb6d73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sun, 3 Mar 2019 20:25:06 +0700 Subject: [PATCH 26/52] Better error report message --- src/util/config.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/util/config.js b/src/util/config.js index 5475b37a..7ef57c54 100644 --- a/src/util/config.js +++ b/src/util/config.js @@ -17,7 +17,7 @@ class ConfigObject { return this.parse(JSON.parse(rawData)); } catch (err) { throw new Error( - `Cannot read config file: "${this.file}": ${err.message}` + `[${this.file}] Cannot read config file: ${err.message}` ); } } @@ -30,16 +30,14 @@ class ConfigObject { const parsedData = this.parse(jsonized); exportData = JSON.stringify(parsedData, null, 4); } catch (err) { - throw new Error( - `Failed to parse config file "${this.file}": ${err.message}` - ); + throw new Error(`Failed to parse data to write: ${err.message}`); } try { return writeFileSync(this.file, exportData); } catch (err) { throw new Error( - `Cannot write config file: "${this.file}": ${err.message}` + `[${this.file}] Cannot write config file: ${err.message}` ); } } From 6a87d9859b98ea2cc751755ed74f7fb9fcf9db1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sun, 3 Mar 2019 20:38:32 +0700 Subject: [PATCH 27/52] Remove temporary call in prompt --- src/prompt/contestConfig/ace.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/prompt/contestConfig/ace.js b/src/prompt/contestConfig/ace.js index 3e0080eb..5f08c4ad 100644 --- a/src/prompt/contestConfig/ace.js +++ b/src/prompt/contestConfig/ace.js @@ -34,5 +34,3 @@ async function acePrompt() { } module.exports = acePrompt; - -acePrompt(); From 5b19d2204e2f1af1b3754186d4d2a995f3bb1f12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sun, 3 Mar 2019 20:44:21 +0700 Subject: [PATCH 28/52] Add probList and ace to the list --- src/prompt/contestOptions.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/prompt/contestOptions.js b/src/prompt/contestOptions.js index 3fd27767..c7a41241 100644 --- a/src/prompt/contestOptions.js +++ b/src/prompt/contestOptions.js @@ -4,6 +4,8 @@ const Enquirer = require("enquirer"); const time = require("./contestConfig/time"); const mode = require("./contestConfig/mode"); const name = require("./contestConfig/name"); +const ace = require("./contestConfig/ace"); +const probList = require("./contestConfig/probList"); const enquirer = new Enquirer(); @@ -14,7 +16,9 @@ async function contestOptionsPrompt() { let contestOptionsContainer = { "Change name": name, "Change time": time, - "Change mode": mode + "Change mode": mode, + "Add problem": probList, + "Change allowed code languages": ace }; const { choice } = await enquirer.prompt({ From e045a8b26401f205c29765f2267a3f883813dc63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sun, 3 Mar 2019 20:58:11 +0700 Subject: [PATCH 29/52] Improvise control over async flow --- src/prompt/contestConfig/ace.js | 7 +++---- src/prompt/contestConfig/mode.js | 8 ++++---- src/prompt/contestConfig/name.js | 7 ++++--- src/prompt/contestConfig/probList.js | 7 +++---- src/prompt/contestConfig/time.js | 10 +++++----- src/prompt/contestOptions.js | 14 ++++++++------ 6 files changed, 27 insertions(+), 26 deletions(-) diff --git a/src/prompt/contestConfig/ace.js b/src/prompt/contestConfig/ace.js index 5f08c4ad..6e9a4cce 100644 --- a/src/prompt/contestConfig/ace.js +++ b/src/prompt/contestConfig/ace.js @@ -1,15 +1,14 @@ "use strict"; const Enquirer = require("enquirer"); -const contestConfig = require("../../util/config/contestConfig"); const enquirer = new Enquirer(); /** * Contest name prompt */ -async function acePrompt() { - let _ace = contestConfig.read().allowedCodeExt; +async function acePrompt(init = {}) { + let _ace = init.allowedCodeExt; // Temporary solution: create list of language, then filter non-included const langList = [".C", ".CPP", ".JAVA", ".KT", ".PAS", ".PY"]; @@ -30,7 +29,7 @@ async function acePrompt() { { name: ".PY", hint: "- Python" } ] }); - contestConfig.update({ allowedCodeExt: ace.concat(leftAce) }); + return { allowedCodeExt: ace.concat(leftAce) }; } module.exports = acePrompt; diff --git a/src/prompt/contestConfig/mode.js b/src/prompt/contestConfig/mode.js index 2e2875d3..0bc28ab6 100644 --- a/src/prompt/contestConfig/mode.js +++ b/src/prompt/contestConfig/mode.js @@ -1,7 +1,6 @@ "use strict"; const Enquirer = require("enquirer"); -const contestConfig = require("../../util/config/contestConfig"); const score = require("../../util/score"); const enquirer = new Enquirer(); @@ -9,15 +8,16 @@ const enquirer = new Enquirer(); /** * Contest mode prompt */ -async function modePrompt() { +async function modePrompt(init = {}) { + const _mode = score.hasOwnProperty(init.mode) ? init.mode : null; const { mode } = await enquirer.prompt({ type: "select", name: "mode", message: "Contest mode:", + initial: _mode, choices: Object.keys(score) }); - - contestConfig.update({ mode }); + return { mode }; } module.exports = modePrompt; diff --git a/src/prompt/contestConfig/name.js b/src/prompt/contestConfig/name.js index 42aa7b2a..229ce77b 100644 --- a/src/prompt/contestConfig/name.js +++ b/src/prompt/contestConfig/name.js @@ -1,21 +1,22 @@ "use strict"; const Enquirer = require("enquirer"); -const contestConfig = require("../../util/config/contestConfig"); const enquirer = new Enquirer(); /** * Contest name prompt */ -async function namePrompt() { +async function namePrompt(init = {}) { + const _name = init.name; const { name } = await enquirer.prompt({ type: "input", name: "name", + initial: _name, message: "Contest name:" }); - contestConfig.update({ name }); + return { name }; } module.exports = namePrompt; diff --git a/src/prompt/contestConfig/probList.js b/src/prompt/contestConfig/probList.js index 313ee07f..efbddc9d 100644 --- a/src/prompt/contestConfig/probList.js +++ b/src/prompt/contestConfig/probList.js @@ -1,22 +1,21 @@ "use strict"; const Enquirer = require("enquirer"); -const contestConfig = require("../../util/config/contestConfig"); const enquirer = new Enquirer(); /** * Contest name prompt */ -async function probListPrompt() { - let _probList = contestConfig.read().probList; +async function probListPrompt(init = {}) { + let _probList = init.probList; const { probList } = await enquirer.prompt({ type: "list", name: "probList", message: "Add problem to contest:" }); _probList = _probList.concat(probList); - contestConfig.update({ probList: _probList }); + return { probList: _probList }; } module.exports = probListPrompt; diff --git a/src/prompt/contestConfig/time.js b/src/prompt/contestConfig/time.js index 7f543556..83b7c9b0 100644 --- a/src/prompt/contestConfig/time.js +++ b/src/prompt/contestConfig/time.js @@ -2,7 +2,6 @@ const Enquirer = require("enquirer"); const getNow = require("../../util/getNow"); -const contestConfig = require("../../util/config/contestConfig"); const enquirer = new Enquirer(); @@ -54,12 +53,13 @@ async function timePrompt() { ]) .then((value) => { value.endTime = new Date(value.startTime.getTime() + value.offset); - value.startTime = value.startTime.toJSON(); - value.endTime = value.endTime.toJSON(); - return value; + return { + startTime: value.startTime.toJSON(), + endTime: value.endTime.toJSON() + }; }); - contestConfig.update({ startTime, endTime }); + return { startTime, endTime }; } module.exports = timePrompt; diff --git a/src/prompt/contestOptions.js b/src/prompt/contestOptions.js index c7a41241..50fa232f 100644 --- a/src/prompt/contestOptions.js +++ b/src/prompt/contestOptions.js @@ -1,6 +1,8 @@ const Console = require("console"); const Enquirer = require("enquirer"); +const contestConfig = require("../util/config/contestConfig"); + const time = require("./contestConfig/time"); const mode = require("./contestConfig/mode"); const name = require("./contestConfig/name"); @@ -28,14 +30,14 @@ async function contestOptionsPrompt() { choices: Object.keys(contestOptionsContainer) }); - return contestOptionsContainer[choice]().then( - () => { + return contestOptionsContainer[choice](contestConfig.read()) + .then((newData) => { + contestConfig.update(newData); Console.log("Contest's configuration saved"); - }, - (err) => { + }) + .catch((err) => { Console.log("Contest's configuration was not saved: ", err.message); - } - ); + }); } module.exports = contestOptionsPrompt; From 3c8548051b2fa95794ceefb66e23b33fc8adb378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sun, 3 Mar 2019 21:09:12 +0700 Subject: [PATCH 30/52] Add problem list manage prompt --- src/prompt/contestConfig/manProbList.js | 22 ++++++++++++++++++++++ src/prompt/contestOptions.js | 2 ++ 2 files changed, 24 insertions(+) create mode 100644 src/prompt/contestConfig/manProbList.js diff --git a/src/prompt/contestConfig/manProbList.js b/src/prompt/contestConfig/manProbList.js new file mode 100644 index 00000000..103560fa --- /dev/null +++ b/src/prompt/contestConfig/manProbList.js @@ -0,0 +1,22 @@ +"use strict"; + +const Enquirer = require("enquirer"); + +const enquirer = new Enquirer(); + +/** + * Contest name prompt + */ +async function probListPrompt(init = {}) { + let _probList = init.probList; + const { probList } = await enquirer.prompt({ + type: "multiselect", + name: "probList", + message: "Manage problems in contest:", + initial: _probList, + choices: _probList + }); + return { probList }; +} + +module.exports = probListPrompt; diff --git a/src/prompt/contestOptions.js b/src/prompt/contestOptions.js index 50fa232f..a36ba17c 100644 --- a/src/prompt/contestOptions.js +++ b/src/prompt/contestOptions.js @@ -8,6 +8,7 @@ const mode = require("./contestConfig/mode"); const name = require("./contestConfig/name"); const ace = require("./contestConfig/ace"); const probList = require("./contestConfig/probList"); +const manProbList = require("./contestConfig/manProbList"); const enquirer = new Enquirer(); @@ -20,6 +21,7 @@ async function contestOptionsPrompt() { "Change time": time, "Change mode": mode, "Add problem": probList, + "Manage problems": manProbList, "Change allowed code languages": ace }; From a198cf3cdab53442428d0ad1f69991bd8fab75a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sun, 3 Mar 2019 21:12:42 +0700 Subject: [PATCH 31/52] Change function name in manProbList --- src/prompt/contestConfig/manProbList.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/prompt/contestConfig/manProbList.js b/src/prompt/contestConfig/manProbList.js index 103560fa..cd44d2cf 100644 --- a/src/prompt/contestConfig/manProbList.js +++ b/src/prompt/contestConfig/manProbList.js @@ -7,7 +7,7 @@ const enquirer = new Enquirer(); /** * Contest name prompt */ -async function probListPrompt(init = {}) { +async function manProbPrompt(init = {}) { let _probList = init.probList; const { probList } = await enquirer.prompt({ type: "multiselect", @@ -19,4 +19,4 @@ async function probListPrompt(init = {}) { return { probList }; } -module.exports = probListPrompt; +module.exports = manProbPrompt; From f2afd37375f921b81266ce6255e97ade0ac59d5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sun, 3 Mar 2019 21:42:08 +0700 Subject: [PATCH 32/52] Move config class source code --- src/util/config/KonConfig.js | 2 +- src/util/{ => config}/config.js | 0 src/util/config/contestConfig.js | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename src/util/{ => config}/config.js (100%) diff --git a/src/util/config/KonConfig.js b/src/util/config/KonConfig.js index 3bca07a2..0bd56371 100644 --- a/src/util/config/KonConfig.js +++ b/src/util/config/KonConfig.js @@ -4,7 +4,7 @@ const validUrl = require("valid-url"); const Judger = require("../../driver/kon"); const parseProbList = require("../parseProbList"); -const Config = require("../config"); +const Config = require("./config"); /** * Parse & validate Kon config data into formatted one diff --git a/src/util/config.js b/src/util/config/config.js similarity index 100% rename from src/util/config.js rename to src/util/config/config.js diff --git a/src/util/config/contestConfig.js b/src/util/config/contestConfig.js index 84e8efba..7aa425d4 100644 --- a/src/util/config/contestConfig.js +++ b/src/util/config/contestConfig.js @@ -1,7 +1,7 @@ "use strict"; const score = require("../score"); -const Config = require("../config"); +const Config = require("./config"); const getNow = require("../getNow"); /** From b421117f05309c19b873d23771448b279174b75a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sun, 3 Mar 2019 22:36:52 +0700 Subject: [PATCH 33/52] Add Kon options menu --- src/index.js | 2 ++ src/prompt/KonConfig/add.js | 34 +++++++++++++++++++++++++++++ src/prompt/KonConfig/manage.js | 29 ++++++++++++++++++++++++ src/prompt/KonOptions.js | 40 ++++++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+) create mode 100644 src/prompt/KonConfig/add.js create mode 100644 src/prompt/KonConfig/manage.js create mode 100644 src/prompt/KonOptions.js diff --git a/src/index.js b/src/index.js index bbe58f94..dd6195a4 100644 --- a/src/index.js +++ b/src/index.js @@ -3,6 +3,7 @@ const Console = require("console"); const addUser = require("./prompt/addUser"); const contestOptions = require("./prompt/contestOptions"); +const KonOptions = require("./prompt/KonOptions"); require("./util/config/contestConfig").genIfNotExist(); require("./util/config/KonConfig").genIfNotExist(); @@ -13,6 +14,7 @@ const mainChoices = { "Start server": () => {}, "Add user": addUser, "Contest options": contestOptions, + "Kon options": KonOptions, Exit: () => {} }; diff --git a/src/prompt/KonConfig/add.js b/src/prompt/KonConfig/add.js new file mode 100644 index 00000000..ec460fb2 --- /dev/null +++ b/src/prompt/KonConfig/add.js @@ -0,0 +1,34 @@ +"use strict"; + +const Enquirer = require("enquirer"); +const validUrl = require("valid-url"); +const Judger = require("../../driver/kon"); +const contestConfig = require("../../util/config/contestConfig"); + +const enquirer = new Enquirer(); + +/** + * Kon addition prompt + */ +async function addKonPrompt() { + const _probList = contestConfig.read().probList; + const { url, prob } = await enquirer.prompt([ + { + type: "input", + name: "url", + message: "URL", + // hint: "http:// or https:// url", + validate: (val) => !!validUrl.isWebUri(val) + }, + { + type: "multiselect", + name: "prob", + message: "Problems handled by this Kon", + hint: "If none are chosen, Wafter will understand Kon accept all", + choices: _probList + } + ]); + return [{ url, prob }]; +} + +module.exports = addKonPrompt; diff --git a/src/prompt/KonConfig/manage.js b/src/prompt/KonConfig/manage.js new file mode 100644 index 00000000..20b4934d --- /dev/null +++ b/src/prompt/KonConfig/manage.js @@ -0,0 +1,29 @@ +"use strict"; + +const Enquirer = require("enquirer"); + +const enquirer = new Enquirer(); + +/** + * Kon management prompt + */ +async function manKonPrompt(init = []) { + const cached = init.reduce((obj, kon) => { + obj[kon.url] = kon; + return obj; + }, {}); + const _konList = init.map((x) => ({ + name: x.url, + hint: x.prob && x.prob.length > 0 ? JSON.stringify(x.prob) : "*" + })); + const { konList } = await enquirer.prompt({ + type: "multiselect", + name: "konList", + message: "Manage Kon in use:", + initial: _konList, + choices: _konList + }); + return konList.map((x) => cached[x]); +} + +module.exports = manKonPrompt; diff --git a/src/prompt/KonOptions.js b/src/prompt/KonOptions.js new file mode 100644 index 00000000..3b8c7605 --- /dev/null +++ b/src/prompt/KonOptions.js @@ -0,0 +1,40 @@ +const Console = require("console"); +const Enquirer = require("enquirer"); + +const KonConfig = require("../util/config/KonConfig"); + +const add = require("./KonConfig/add"); +const manage = require("./KonConfig/manage"); + +const enquirer = new Enquirer(); + +/** + * Prompt for contest's option + */ +async function KonOptionsPrompt() { + let KonOptionsContainer = { + "Add Kon": add, + "Manage Kon": manage + }; + + const { choice } = await enquirer.prompt({ + type: "select", + name: "choice", + message: "Kon's Pair Options", + choices: Object.keys(KonOptionsContainer) + }); + const jsonized = JSON.parse(JSON.stringify(KonConfig.read())); + return KonOptionsContainer[choice](jsonized) + .then((newData) => { + KonConfig.update(newData); + Console.log("Kon's pair configuration saved"); + }) + .catch((err) => { + Console.log( + "Kon's pair configuration was not saved: ", + err.message + ); + }); +} + +module.exports = KonOptionsPrompt; From ca06fc7820517dc4b3c254f1b99d8a5470f1f507 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sun, 3 Mar 2019 22:37:28 +0700 Subject: [PATCH 34/52] Add API param to alternate merge function --- src/util/config/KonConfig.js | 2 +- src/util/config/config.js | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/util/config/KonConfig.js b/src/util/config/KonConfig.js index 0bd56371..bed6c739 100644 --- a/src/util/config/KonConfig.js +++ b/src/util/config/KonConfig.js @@ -28,4 +28,4 @@ function parseKonConfig(configData) { .map((kon) => new Judger(kon.url, parseProbList(kon.prob))); } -module.exports = new Config("kon.json", parseKonConfig, () => []); +module.exports = new Config("kon.json", parseKonConfig, () => [], [].concat); diff --git a/src/util/config/config.js b/src/util/config/config.js index 7ef57c54..209a3b95 100644 --- a/src/util/config/config.js +++ b/src/util/config/config.js @@ -3,10 +3,16 @@ const { readFileSync, writeFileSync, existsSync } = require("fs"); class ConfigObject { - constructor(file, parse = (x) => x, sample = () => {}) { + constructor( + file, + parse = (x) => x, + sample = () => ({}), + merge = Object.assign + ) { this.file = file; this.parse = parse; this.sample = sample; + this.merge = merge; } genIfNotExist() { if (!existsSync(this.file)) this.write(this.sample()); @@ -43,7 +49,7 @@ class ConfigObject { } update(data) { const store = this.read(); - const updated = Object.assign(store, data); + const updated = this.merge(store, data); this.write(updated); } } From 23294bcafd2429ea706e04c0fde3e0d1a3e75f6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sun, 3 Mar 2019 22:38:24 +0700 Subject: [PATCH 35/52] Edit comment --- src/prompt/contestConfig/ace.js | 2 +- src/prompt/contestConfig/manProbList.js | 2 +- src/prompt/contestConfig/probList.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/prompt/contestConfig/ace.js b/src/prompt/contestConfig/ace.js index 6e9a4cce..87985272 100644 --- a/src/prompt/contestConfig/ace.js +++ b/src/prompt/contestConfig/ace.js @@ -5,7 +5,7 @@ const Enquirer = require("enquirer"); const enquirer = new Enquirer(); /** - * Contest name prompt + * Control Code Extension prompt */ async function acePrompt(init = {}) { let _ace = init.allowedCodeExt; diff --git a/src/prompt/contestConfig/manProbList.js b/src/prompt/contestConfig/manProbList.js index cd44d2cf..70498ddf 100644 --- a/src/prompt/contestConfig/manProbList.js +++ b/src/prompt/contestConfig/manProbList.js @@ -5,7 +5,7 @@ const Enquirer = require("enquirer"); const enquirer = new Enquirer(); /** - * Contest name prompt + * Problem management prompt */ async function manProbPrompt(init = {}) { let _probList = init.probList; diff --git a/src/prompt/contestConfig/probList.js b/src/prompt/contestConfig/probList.js index efbddc9d..13780c8a 100644 --- a/src/prompt/contestConfig/probList.js +++ b/src/prompt/contestConfig/probList.js @@ -5,7 +5,7 @@ const Enquirer = require("enquirer"); const enquirer = new Enquirer(); /** - * Contest name prompt + * Contest problem addition prompt */ async function probListPrompt(init = {}) { let _probList = init.probList; From fcdcd3dc8cc226963478d98a1318bf3245bb1104 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sun, 3 Mar 2019 22:39:00 +0700 Subject: [PATCH 36/52] Finalize menu --- src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index dd6195a4..e4a0f941 100644 --- a/src/index.js +++ b/src/index.js @@ -14,7 +14,7 @@ const mainChoices = { "Start server": () => {}, "Add user": addUser, "Contest options": contestOptions, - "Kon options": KonOptions, + "Kon's pair options": KonOptions, Exit: () => {} }; From f98944ffe9bb16742a4fbf553f9d500d39fd973d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Sun, 3 Mar 2019 23:08:52 +0700 Subject: [PATCH 37/52] Fix error message --- src/index.js | 10 ++++++++-- src/prompt/KonOptions.js | 5 +++-- src/prompt/contestOptions.js | 6 +++++- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/index.js b/src/index.js index e4a0f941..1a4483e8 100644 --- a/src/index.js +++ b/src/index.js @@ -23,7 +23,7 @@ const mainChoices = { */ async function mainPrompt() { return enquirer.prompt({ - type: "autocomplete", + type: "select", name: "main", message: "How can I help you ?", choices: Object.keys(mainChoices) @@ -41,7 +41,13 @@ async function main() { try { while (res.main !== "Exit") { res = await mainPrompt(); - await mainChoices[res.main](); + try { + await mainChoices[res.main](); + } catch (err) { + Console.log( + `Exited to menu${err.message ? `: ${err.message}` : ""}` + ); + } if (res.main === "Start server") { require("./server"); diff --git a/src/prompt/KonOptions.js b/src/prompt/KonOptions.js index 3b8c7605..4522bf55 100644 --- a/src/prompt/KonOptions.js +++ b/src/prompt/KonOptions.js @@ -31,8 +31,9 @@ async function KonOptionsPrompt() { }) .catch((err) => { Console.log( - "Kon's pair configuration was not saved: ", - err.message + `Kon's pair configuration was not saved${ + err.message ? `: ${err.message}` : "" + }` ); }); } diff --git a/src/prompt/contestOptions.js b/src/prompt/contestOptions.js index a36ba17c..2b8bd8a5 100644 --- a/src/prompt/contestOptions.js +++ b/src/prompt/contestOptions.js @@ -38,7 +38,11 @@ async function contestOptionsPrompt() { Console.log("Contest's configuration saved"); }) .catch((err) => { - Console.log("Contest's configuration was not saved: ", err.message); + Console.log( + `Contest's configuration was not saved${ + err.message ? `: ${err.message}` : "" + }` + ); }); } From cd60b5782f0b2247f6724b6264f63874dd39f2d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Mon, 4 Mar 2019 12:40:16 +0700 Subject: [PATCH 38/52] Pre-generate config files --- src/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/index.js b/src/index.js index 1a4483e8..7c366800 100644 --- a/src/index.js +++ b/src/index.js @@ -1,13 +1,13 @@ const Enquirer = require("enquirer"); const Console = require("console"); +require("./util/config/contestConfig").genIfNotExist(); +require("./util/config/KonConfig").genIfNotExist(); + const addUser = require("./prompt/addUser"); const contestOptions = require("./prompt/contestOptions"); const KonOptions = require("./prompt/KonOptions"); -require("./util/config/contestConfig").genIfNotExist(); -require("./util/config/KonConfig").genIfNotExist(); - const enquirer = new Enquirer(); const mainChoices = { From be57ec0caa41b0de4fbba9f4a05b3764d619e135 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Mon, 4 Mar 2019 12:58:15 +0700 Subject: [PATCH 39/52] Fix KonConfig is not saved --- src/prompt/KonConfig/add.js | 6 ++++-- src/util/config/KonConfig.js | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/prompt/KonConfig/add.js b/src/prompt/KonConfig/add.js index ec460fb2..a6c70c8b 100644 --- a/src/prompt/KonConfig/add.js +++ b/src/prompt/KonConfig/add.js @@ -10,7 +10,7 @@ const enquirer = new Enquirer(); /** * Kon addition prompt */ -async function addKonPrompt() { +async function addKonPrompt(init = []) { const _probList = contestConfig.read().probList; const { url, prob } = await enquirer.prompt([ { @@ -28,7 +28,9 @@ async function addKonPrompt() { choices: _probList } ]); - return [{ url, prob }]; + const newList = init; + newList.push(new Judger(url, prob)); + return init; } module.exports = addKonPrompt; diff --git a/src/util/config/KonConfig.js b/src/util/config/KonConfig.js index bed6c739..21429cd8 100644 --- a/src/util/config/KonConfig.js +++ b/src/util/config/KonConfig.js @@ -28,4 +28,4 @@ function parseKonConfig(configData) { .map((kon) => new Judger(kon.url, parseProbList(kon.prob))); } -module.exports = new Config("kon.json", parseKonConfig, () => [], [].concat); +module.exports = new Config("kon.json", parseKonConfig, () => [], (_, y) => y); From f6f7af2ac15d81eeac20c5402ec3ed32b05d92ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Mon, 4 Mar 2019 13:12:09 +0700 Subject: [PATCH 40/52] Disable options when condition is not met --- src/prompt/KonOptions.js | 7 ++++++- src/prompt/contestOptions.js | 14 +++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/prompt/KonOptions.js b/src/prompt/KonOptions.js index 4522bf55..fce1e7e9 100644 --- a/src/prompt/KonOptions.js +++ b/src/prompt/KonOptions.js @@ -17,11 +17,16 @@ async function KonOptionsPrompt() { "Manage Kon": manage }; + let KonOptionsChoices = [ + "Add Kon", + { name: "Manage Kon", disabled: KonConfig.read().length === 0 } + ]; + const { choice } = await enquirer.prompt({ type: "select", name: "choice", message: "Kon's Pair Options", - choices: Object.keys(KonOptionsContainer) + choices: KonOptionsChoices }); const jsonized = JSON.parse(JSON.stringify(KonConfig.read())); return KonOptionsContainer[choice](jsonized) diff --git a/src/prompt/contestOptions.js b/src/prompt/contestOptions.js index 2b8bd8a5..74cfef18 100644 --- a/src/prompt/contestOptions.js +++ b/src/prompt/contestOptions.js @@ -25,11 +25,23 @@ async function contestOptionsPrompt() { "Change allowed code languages": ace }; + let contestOptionsChoices = [ + "Change name", + "Change time", + "Change mode", + "Add problem", + { + name: "Manage problems", + disabled: contestConfig.read().probList.length === 0 + }, + "Change allowed code languages" + ]; + const { choice } = await enquirer.prompt({ type: "select", name: "choice", message: "Contest Options", - choices: Object.keys(contestOptionsContainer) + choices: contestOptionsChoices }); return contestOptionsContainer[choice](contestConfig.read()) From a2e9fbd08f36492ea507f67fb72068e6f8139c29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Mon, 4 Mar 2019 14:05:18 +0700 Subject: [PATCH 41/52] Fix problem where konConfig failed to parse new problem --- src/prompt/KonConfig/add.js | 2 +- src/util/parseProbList.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/prompt/KonConfig/add.js b/src/prompt/KonConfig/add.js index a6c70c8b..938cc39f 100644 --- a/src/prompt/KonConfig/add.js +++ b/src/prompt/KonConfig/add.js @@ -30,7 +30,7 @@ async function addKonPrompt(init = []) { ]); const newList = init; newList.push(new Judger(url, prob)); - return init; + return newList; } module.exports = addKonPrompt; diff --git a/src/util/parseProbList.js b/src/util/parseProbList.js index 2cdaf04c..d2a115f3 100644 --- a/src/util/parseProbList.js +++ b/src/util/parseProbList.js @@ -1,13 +1,13 @@ "use strict"; -const contest = require("../config/contest"); +const contestConfig = require("../util/config/contestConfig"); /** * parseProbList * @param {Array} obj Array of problem included in kon.json */ function parseProbList(obj) { - const probList = contest.probList; + const probList = contestConfig.read().probList; if (obj === undefined) return probList; if (!Array.isArray(obj)) return []; From c51e92b29f499746100caa0fa15e15f992578fb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Mon, 4 Mar 2019 14:23:23 +0700 Subject: [PATCH 42/52] Extend time range --- src/prompt/contestConfig/time.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/prompt/contestConfig/time.js b/src/prompt/contestConfig/time.js index 83b7c9b0..7e2aa97c 100644 --- a/src/prompt/contestConfig/time.js +++ b/src/prompt/contestConfig/time.js @@ -16,12 +16,12 @@ const timeList = (() => { })(); const offSetList = (() => { - let now = new Date(0); + let now = new Date(-7 * 60 * 60 * 1000); const res = {}; - for (let i = 1; i <= 40; ++i) { + for (let i = 1; i <= 48; ++i) { now.setMinutes(now.getMinutes() + 15); let str = ""; - if (now.getHours() > 7) str += (now.getHours() - 7).toString() + " hr "; + if (now.getHours() !== 0) str += now.getHours().toString() + " hr "; if (now.getMinutes() !== 0) str += now.getMinutes().toString() + " min"; res[str.trim()] = i * 15 * 60 * 1000; } From 1eca3a03b3caeb7047131e54abff130d4a236e93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Mon, 4 Mar 2019 14:31:40 +0700 Subject: [PATCH 43/52] Improve startTIme picker --- src/util/getNow.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/getNow.js b/src/util/getNow.js index c40dd874..02f8c82f 100644 --- a/src/util/getNow.js +++ b/src/util/getNow.js @@ -5,7 +5,7 @@ const getNow = () => { const now = new Date(); now.setMilliseconds(0); now.setSeconds(0); - now.setMinutes((Math.ceil(now.getMinutes() / 15) + 1) * 15); + now.setMinutes((Math.ceil(now.getMinutes() / 5) + 1) * 5); return now; }; From 7de27124c624798ba0b19a27fa1fe058d9ae1aea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Mon, 4 Mar 2019 15:08:11 +0700 Subject: [PATCH 44/52] Fix offset --- src/prompt/contestConfig/time.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/prompt/contestConfig/time.js b/src/prompt/contestConfig/time.js index 7e2aa97c..3922c96b 100644 --- a/src/prompt/contestConfig/time.js +++ b/src/prompt/contestConfig/time.js @@ -16,7 +16,7 @@ const timeList = (() => { })(); const offSetList = (() => { - let now = new Date(-7 * 60 * 60 * 1000); + let now = new Date(new Date().setHours(0, 0)); const res = {}; for (let i = 1; i <= 48; ++i) { now.setMinutes(now.getMinutes() + 15); From 3a92acb38abbed7e5724594cdbb74f0cbf797d3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Mon, 4 Mar 2019 16:12:28 +0700 Subject: [PATCH 45/52] Check res from kon periodically --- src/controller/submitCode.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/controller/submitCode.js b/src/controller/submitCode.js index 99c66b33..1647aa84 100644 --- a/src/controller/submitCode.js +++ b/src/controller/submitCode.js @@ -119,8 +119,9 @@ async function sendCode(source_code_path, user_id, prob_name) { Console.log(err.message); }); - // Temporary trigger - reloadSubs(); + process.nextTick(() => reloadSubs()); + // First trigger: 30 seconds + setTimeout(() => reloadSubs(), 45000); } catch (err) { Console.log(err.message); } From f4c9ba4402516c4d3b926cc37282bed6a88883fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Mon, 4 Mar 2019 18:00:27 +0700 Subject: [PATCH 46/52] Revert change in server.js --- src/server.js | 151 +++++++++++++++++++++++--------------------------- 1 file changed, 70 insertions(+), 81 deletions(-) diff --git a/src/server.js b/src/server.js index 209c01e1..be2030e8 100644 --- a/src/server.js +++ b/src/server.js @@ -8,86 +8,75 @@ const session = require("express-session"); const bodyParser = require("body-parser"); const ip = require("ip"); -/** - * Main server function - */ -function main() { - // These module call config module which read and export itself, - // thus they cannot be called outside main() - const server = require("./config/server"); - const passportConfig = require("./controller/passportConfig"); - const initJudger = require("./controller/initJudger"); - const { logToConsole, logToFile } = require("./middleware/log"); - - const info = require("./routes/info"); - const subs = require("./routes/subs"); - const users = require("./routes/users"); - const score = require("./routes/score"); - - passportConfig(passport); - initJudger(); - - const app = express(); - - const PORT = server.port; - - app.use(helmet()); - app.use(helmet.noCache()); - app.use(logToConsole); - app.use(logToFile); - - app.use(bodyParser.urlencoded({ extended: true })); - app.use( - session({ - resave: false, - saveUninitialized: true, - secret: server.secret - }) +const server = require("./config/server"); +const passportConfig = require("./controller/passportConfig"); +const initJudger = require("./controller/initJudger"); +const { logToConsole, logToFile } = require("./middleware/log"); + +const info = require("./routes/info"); +const subs = require("./routes/subs"); +const users = require("./routes/users"); +const score = require("./routes/score"); + +passportConfig(passport); +initJudger(); + +const app = express(); + +const PORT = server.port; + +app.use(helmet()); +app.use(helmet.noCache()); +app.use(logToConsole); +app.use(logToFile); + +app.use(bodyParser.urlencoded({ extended: true })); +app.use( + session({ + resave: false, + saveUninitialized: true, + secret: server.secret + }) +); +app.use(passport.initialize()); +app.use(passport.session()); + +// API +app.all("/api", (req, res) => { + res.sendStatus(204); +}); +app.use("/api/info", info); +app.use("/api/subs", subs); +app.use("/api/users", users); +app.use("/api/score", score); + +app.post("/api/login", passport.authenticate("local"), (req, res) => { + res.sendStatus(200); +}); + +app.get("/api/logout", (req, res) => { + req.logout(); + res.sendStatus(200); +}); + +app.all("/api/*", (req, res) => { + res.sendStatus(404); +}); + +app.use("/", express.static(server.staticFolder)); +// Temp solution ? +app.use("/*", (req, res) => { + res.sendFile(server.staticFolder + "/index.html"); +}); + +let serv = app.listen(PORT, () => { + Console.log( + `Wafter is serving at http://${ip.address()}:${serv.address().port}` ); - app.use(passport.initialize()); - app.use(passport.session()); - - // API - app.all("/api", (req, res) => { - res.sendStatus(204); - }); - app.use("/api/info", info); - app.use("/api/subs", subs); - app.use("/api/users", users); - app.use("/api/score", score); - - app.post("/api/login", passport.authenticate("local"), (req, res) => { - res.sendStatus(200); - }); - - app.get("/api/logout", (req, res) => { - req.logout(); - res.sendStatus(200); +}); +process.on("exit", () => { + serv.close(() => { + Console.log("Closing server"); + process.exit(0); }); - - app.all("/api/*", (req, res) => { - res.sendStatus(404); - }); - - app.use("/", express.static(server.staticFolder)); - // Temp solution ? - app.use("/*", (req, res) => { - res.sendFile(server.staticFolder + "/index.html"); - }); - - let serv = app.listen(PORT, () => { - Console.log( - `Wafter is serving at http://${ip.address()}:${serv.address().port}` - ); - }); - - process.on("exit", () => { - Console.log("Shutting down Wafter"); - // No need to callback because it's time consuming - serv.close(); - }); - - return serv; -} - -module.exports = main; +}); From d591ba8708dcf0e272e62174d02be34269f1902e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Mon, 4 Mar 2019 18:04:26 +0700 Subject: [PATCH 47/52] Revert server call method in index.js --- src/index.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index 9b40c0fd..6cb9f5cc 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,5 @@ const Enquirer = require("enquirer"); const Console = require("console"); -const server = require("./server"); const addUser = require("./prompt/addUser"); const enquirer = new Enquirer(); @@ -37,7 +36,7 @@ async function main() { await mainChoices[res.main](); if (res.main === "Start server") { - server(); + require("./server"); return; } } From d69a9e20166f5d1d7dec67bcf0eeec6b4e075a45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Mon, 4 Mar 2019 18:55:26 +0700 Subject: [PATCH 48/52] Try to reload cycle --- src/controller/submitCode.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controller/submitCode.js b/src/controller/submitCode.js index 1647aa84..9c6b1a88 100644 --- a/src/controller/submitCode.js +++ b/src/controller/submitCode.js @@ -119,8 +119,8 @@ async function sendCode(source_code_path, user_id, prob_name) { Console.log(err.message); }); - process.nextTick(() => reloadSubs()); - // First trigger: 30 seconds + setImmediate(() => reloadSubs()); + // Second trigger: 45 seconds setTimeout(() => reloadSubs(), 45000); } catch (err) { Console.log(err.message); From 4db5a01113681e923cb35b38a1f4e3a6cc0ec2ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Tue, 5 Mar 2019 06:23:21 +0700 Subject: [PATCH 49/52] Update start time select --- src/prompt/contestConfig/time.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/prompt/contestConfig/time.js b/src/prompt/contestConfig/time.js index 3922c96b..a4333e27 100644 --- a/src/prompt/contestConfig/time.js +++ b/src/prompt/contestConfig/time.js @@ -8,9 +8,9 @@ const enquirer = new Enquirer(); const timeList = (() => { let now = getNow(); const res = {}; - for (let i = 0; i < 40; ++i) { + for (let i = 0; i < 48; ++i) { res[now.toLocaleString()] = new Date(now); - now.setMinutes(now.getMinutes() + 15); + now.setMinutes(now.getMinutes() + 5); } return res; })(); From 92616b0143e844a485d76aad0344e18852808edc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Tue, 5 Mar 2019 06:42:56 +0700 Subject: [PATCH 50/52] Update initJudger.js --- src/controller/initJudger.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/controller/initJudger.js b/src/controller/initJudger.js index f06e5286..b4bfa2e2 100644 --- a/src/controller/initJudger.js +++ b/src/controller/initJudger.js @@ -17,13 +17,15 @@ function initJudger() { judger .clone(arcPath) .then((res) => { - if (res.status === 400) + if (res.status === 403) Console.log(`Kon was in used. ${judger.serverAddress}`); + else if (res.status === 500) + Console.log(`Kon unable to respond. ${judger.serverAddress}`); else if (res.status === 200) Console.log( `Sucessfully cloned ${judger.serverAddress}` ); - else throw Error("Cannot verified Kon"); + else throw Error("Cannot verify Kon"); }) .catch((err) => { Console.log( From 3bafbbda002a1ea6fb8c2cca0b9dfe1c71dd16a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Tue, 5 Mar 2019 06:58:19 +0700 Subject: [PATCH 51/52] Update initJudger.js --- src/controller/initJudger.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controller/initJudger.js b/src/controller/initJudger.js index b4bfa2e2..0de75018 100644 --- a/src/controller/initJudger.js +++ b/src/controller/initJudger.js @@ -20,12 +20,12 @@ function initJudger() { if (res.status === 403) Console.log(`Kon was in used. ${judger.serverAddress}`); else if (res.status === 500) - Console.log(`Kon unable to respond. ${judger.serverAddress}`); + Console.log(`Kon failed to process. ${judger.serverAddress}`); else if (res.status === 200) Console.log( `Sucessfully cloned ${judger.serverAddress}` ); - else throw Error("Cannot verify Kon"); + else throw Error("Kon did not respond"); }) .catch((err) => { Console.log( From 67947a59c82c13f3b263f582cb8ef94ea002a6d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Tu=E1=BA=A5n=20D=C5=A9ng?= Date: Tue, 5 Mar 2019 12:14:59 +0700 Subject: [PATCH 52/52] Apply format to initJudger.js --- src/controller/initJudger.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/controller/initJudger.js b/src/controller/initJudger.js index 0de75018..00622a7d 100644 --- a/src/controller/initJudger.js +++ b/src/controller/initJudger.js @@ -20,7 +20,9 @@ function initJudger() { if (res.status === 403) Console.log(`Kon was in used. ${judger.serverAddress}`); else if (res.status === 500) - Console.log(`Kon failed to process. ${judger.serverAddress}`); + Console.log( + `Kon failed to process. ${judger.serverAddress}` + ); else if (res.status === 200) Console.log( `Sucessfully cloned ${judger.serverAddress}`