Browse Source

Merge branch 'master' of https://github.com/syzoj/syzoj

pull/6/head
Tian Yunhao 6 years ago
parent
commit
b7dee9d409
  1. 12
      README.md
  2. 10
      app.js
  3. 12
      config-example.json
  4. 78
      libs/judgeResult.js
  5. 127
      libs/judger.js
  6. 53
      libs/judger_interfaces.js
  7. 23
      libs/winston.js
  8. 10
      models/contest_ranklist.js
  9. 10
      models/judge_state.js
  10. 1
      models/problem.js
  11. 13
      modules/admin.js
  12. 49
      modules/api_v2.js
  13. 40
      modules/contest.js
  14. 13
      modules/problem.js
  15. 298
      modules/socketio.js
  16. 38
      modules/submission.js
  17. 4158
      package-lock.json
  18. 7
      package.json
  19. BIN
      static/fonts/-_Ctzj9b56b8RgXW8FArifk_vArhqVIZ0nv9q090hN8.woff2
  20. BIN
      static/fonts/1YwB1sO8YE1Lyjf12WNiUA.woff2
  21. BIN
      static/fonts/59ZRklaO5bWGqF5A9baEERJtnKITppOI_IvcXXDNrsc.woff2
  22. BIN
      static/fonts/AcvTq8Q0lyKKNxRlL28RnxJtnKITppOI_IvcXXDNrsc.woff2
  23. BIN
      static/fonts/H2DMvhDLycM56KNuAtbJYA.woff2
  24. BIN
      static/fonts/HkF_qI1x_noxlxhrhMQYEFtXRa8TVwTICgirnJhmVJw.woff2
  25. BIN
      static/fonts/K88pR3goAWT7BTt32Z01mxJtnKITppOI_IvcXXDNrsc.woff2
  26. BIN
      static/fonts/LWCjsQkB6EMdfHrEVqA1KRJtnKITppOI_IvcXXDNrsc.woff2
  27. BIN
      static/fonts/ObQr5XYcoH0WBoUxiaYK3_Y6323mHUZFJMgTvxaG2iE.woff2
  28. BIN
      static/fonts/PLygLKRVCQnA5fhu3qk5fQ.woff2
  29. BIN
      static/fonts/PRmiXeptR36kaC0GEAetxgalQocB-__pDVGhF3uS2Ks.woff2
  30. BIN
      static/fonts/PRmiXeptR36kaC0GEAetxiFaMxiho_5XQnyRZzQsrZs.woff2
  31. BIN
      static/fonts/PRmiXeptR36kaC0GEAetxi_vZmeiCMnoWNN9rHBYaTc.woff2
  32. BIN
      static/fonts/PRmiXeptR36kaC0GEAetxmhQUTDJGru-0vvUpABgH8I.woff2
  33. BIN
      static/fonts/PRmiXeptR36kaC0GEAetxolIZu-HDpmDIZMigmsroc4.woff2
  34. BIN
      static/fonts/PRmiXeptR36kaC0GEAetxp6iIh_FvlUHQwED9Yt5Kbw.woff2
  35. BIN
      static/fonts/PRmiXeptR36kaC0GEAetxujkDdvhIIFj_YMdgqpnSB0.woff2
  36. BIN
      static/fonts/RjgO7rYTmqiVp7vzi-Q5URJtnKITppOI_IvcXXDNrsc.woff2
  37. BIN
      static/fonts/UyBMtLsHKBKXelqf4x7VRQ.woff2
  38. BIN
      static/fonts/YMOYVM-eg6Qs9YzV9OSqZfesZW2xOQ-xsNqO47m55DA.woff2
  39. BIN
      static/fonts/ZKwULyCG95tk6mOqHQfRBCEAvth_LlrfE80CYdSH47w.woff2
  40. BIN
      static/fonts/cJZKeOuBrn4kERxqtaUH3VtXRa8TVwTICgirnJhmVJw.woff2
  41. BIN
      static/fonts/k3k702ZOKiLJc3WVjuplzBWV49_lSm1NYrwo-zkhivY.woff2
  42. BIN
      static/fonts/k3k702ZOKiLJc3WVjuplzD0LW-43aMEzIO6XUTLjad8.woff2
  43. BIN
      static/fonts/k3k702ZOKiLJc3WVjuplzJX5f-9o1vgP2EXwfjgl7AY.woff2
  44. BIN
      static/fonts/k3k702ZOKiLJc3WVjuplzK-j2U0lmluP9RWlSytm3ho.woff2
  45. BIN
      static/fonts/k3k702ZOKiLJc3WVjuplzKaRobkAwv3vxw3jMhVENGA.woff2
  46. BIN
      static/fonts/k3k702ZOKiLJc3WVjuplzOgdm0LZdjqr5-oayXSOefg.woff2
  47. BIN
      static/fonts/k3k702ZOKiLJc3WVjuplzP8zf_FOSsgRmwsS7Aa9k2w.woff2
  48. BIN
      static/fonts/u-WUoqrET9fUeobQW7jkRRJtnKITppOI_IvcXXDNrsc.woff2
  49. BIN
      static/fonts/xjAJXh38I15wypJXxuGMBiYE0-AqJ3nfInTTiDXDjU4.woff2
  50. BIN
      static/fonts/xjAJXh38I15wypJXxuGMBjTOQ_MqJVwkKsUn0wKzc2I.woff2
  51. BIN
      static/fonts/xjAJXh38I15wypJXxuGMBjUj_cnvWIuuBMVgbX098Mw.woff2
  52. BIN
      static/fonts/xjAJXh38I15wypJXxuGMBkbcKLIaa1LC45dFaAfauRA.woff2
  53. BIN
      static/fonts/xjAJXh38I15wypJXxuGMBmo_sUJ8uO4YLWRInS22T3Y.woff2
  54. BIN
      static/fonts/xjAJXh38I15wypJXxuGMBo4P5ICox8Kq3LLUNMylGO4.woff2
  55. BIN
      static/fonts/xjAJXh38I15wypJXxuGMBr6up8jxqWt8HVA3mDhkV_0.woff2
  56. BIN
      static/fonts/xozscpT2726on7jbcb_pAhJtnKITppOI_IvcXXDNrsc.woff2
  57. 16570
      static/libs/Chart.js/Chart.bundle.js
  58. 16
      static/libs/Chart.js/Chart.bundle.min.js
  59. 12269
      static/libs/Chart.js/Chart.js
  60. 14
      static/libs/Chart.js/Chart.min.js
  61. 1
      static/libs/KaTeX/contrib/auto-render.min.js
  62. BIN
      static/libs/KaTeX/fonts/KaTeX_AMS-Regular.eot
  63. BIN
      static/libs/KaTeX/fonts/KaTeX_AMS-Regular.ttf
  64. BIN
      static/libs/KaTeX/fonts/KaTeX_AMS-Regular.woff
  65. BIN
      static/libs/KaTeX/fonts/KaTeX_AMS-Regular.woff2
  66. BIN
      static/libs/KaTeX/fonts/KaTeX_Caligraphic-Bold.eot
  67. BIN
      static/libs/KaTeX/fonts/KaTeX_Caligraphic-Bold.ttf
  68. BIN
      static/libs/KaTeX/fonts/KaTeX_Caligraphic-Bold.woff
  69. BIN
      static/libs/KaTeX/fonts/KaTeX_Caligraphic-Bold.woff2
  70. BIN
      static/libs/KaTeX/fonts/KaTeX_Caligraphic-Regular.eot
  71. BIN
      static/libs/KaTeX/fonts/KaTeX_Caligraphic-Regular.ttf
  72. BIN
      static/libs/KaTeX/fonts/KaTeX_Caligraphic-Regular.woff
  73. BIN
      static/libs/KaTeX/fonts/KaTeX_Caligraphic-Regular.woff2
  74. BIN
      static/libs/KaTeX/fonts/KaTeX_Fraktur-Bold.eot
  75. BIN
      static/libs/KaTeX/fonts/KaTeX_Fraktur-Bold.ttf
  76. BIN
      static/libs/KaTeX/fonts/KaTeX_Fraktur-Bold.woff
  77. BIN
      static/libs/KaTeX/fonts/KaTeX_Fraktur-Bold.woff2
  78. BIN
      static/libs/KaTeX/fonts/KaTeX_Fraktur-Regular.eot
  79. BIN
      static/libs/KaTeX/fonts/KaTeX_Fraktur-Regular.ttf
  80. BIN
      static/libs/KaTeX/fonts/KaTeX_Fraktur-Regular.woff
  81. BIN
      static/libs/KaTeX/fonts/KaTeX_Fraktur-Regular.woff2
  82. BIN
      static/libs/KaTeX/fonts/KaTeX_Main-Bold.eot
  83. BIN
      static/libs/KaTeX/fonts/KaTeX_Main-Bold.ttf
  84. BIN
      static/libs/KaTeX/fonts/KaTeX_Main-Bold.woff
  85. BIN
      static/libs/KaTeX/fonts/KaTeX_Main-Bold.woff2
  86. BIN
      static/libs/KaTeX/fonts/KaTeX_Main-Italic.eot
  87. BIN
      static/libs/KaTeX/fonts/KaTeX_Main-Italic.ttf
  88. BIN
      static/libs/KaTeX/fonts/KaTeX_Main-Italic.woff
  89. BIN
      static/libs/KaTeX/fonts/KaTeX_Main-Italic.woff2
  90. BIN
      static/libs/KaTeX/fonts/KaTeX_Main-Regular.eot
  91. BIN
      static/libs/KaTeX/fonts/KaTeX_Main-Regular.ttf
  92. BIN
      static/libs/KaTeX/fonts/KaTeX_Main-Regular.woff
  93. BIN
      static/libs/KaTeX/fonts/KaTeX_Main-Regular.woff2
  94. BIN
      static/libs/KaTeX/fonts/KaTeX_Math-BoldItalic.eot
  95. BIN
      static/libs/KaTeX/fonts/KaTeX_Math-BoldItalic.ttf
  96. BIN
      static/libs/KaTeX/fonts/KaTeX_Math-BoldItalic.woff
  97. BIN
      static/libs/KaTeX/fonts/KaTeX_Math-BoldItalic.woff2
  98. BIN
      static/libs/KaTeX/fonts/KaTeX_Math-Italic.eot
  99. BIN
      static/libs/KaTeX/fonts/KaTeX_Math-Italic.ttf
  100. BIN
      static/libs/KaTeX/fonts/KaTeX_Math-Italic.woff
  101. Some files were not shown because too many files have changed in this diff Show More

12
README.md

@ -4,7 +4,17 @@ An OnlineJudge System for OI.
The UI is powered by [Semantic UI](http://semantic-ui.com/). The UI is powered by [Semantic UI](http://semantic-ui.com/).
Template designed & coded by [Sengxian](https://www.sengxian.com) and [Menci](https://men.ci). Template designed & coded by [Sengxian](https://www.sengxian.com) and [Menci](https://men.ci).
# Upgrading
Because of an update to the database structure, users who upgrade from a commit BEFORE 4c673956959532d61b8f9ba0be3191a054b4371a **MUST** perform the following SQL on the database:
```sql
ALTER TABLE `judge_state` ADD `is_public` TINYINT(1) NOT NULL AFTER `compilation`;
UPDATE `judge_state` JOIN `problem` ON `problem`.`id` = `judge_state`.`problem_id` SET `judge_state`.`is_public` = `problem`.`is_public`;
ALTER TABLE `syzoj`.`judge_state` ADD INDEX `judge_state_is_public` (`id`, `is_public`, `type_info`, `type`);
```
# Deploying # Deploying
**Warning** The following content is **outdated**. Please refer to https://syzoj-demo.t123yh.xyz:20170/article/1 for a detailed guide and a demo server.
There's currently *no* stable version of SYZOJ 2, but you can use the unstable version from git. There's currently *no* stable version of SYZOJ 2, but you can use the unstable version from git.
``` ```
@ -161,4 +171,4 @@ systemctl restart syzoj-judge
"allowUnauthorizedTls": false "allowUnauthorizedTls": false
} }
}, },
``` ```

10
app.js

@ -32,6 +32,7 @@ const options = commandLineArgs(optionDefinitions);
global.syzoj = { global.syzoj = {
rootDir: __dirname, rootDir: __dirname,
config: require(options.config), config: require(options.config),
configDir: options.config,
models: [], models: [],
modules: [], modules: [],
db: null, db: null,
@ -43,8 +44,11 @@ global.syzoj = {
global.app = Express(); global.app = Express();
syzoj.production = app.get('env') === 'production'; syzoj.production = app.get('env') === 'production';
let winstonLib = require('./libs/winston');
winstonLib.configureWinston(!syzoj.production);
app.listen(parseInt(syzoj.config.port), syzoj.config.hostname, () => { app.server = require('http').createServer(app);
app.server.listen(parseInt(syzoj.config.port), syzoj.config.hostname, () => {
this.log(`SYZOJ is listening on ${syzoj.config.hostname}:${parseInt(syzoj.config.port)}...`); this.log(`SYZOJ is listening on ${syzoj.config.hostname}:${parseInt(syzoj.config.port)}...`);
}); });
@ -78,10 +82,8 @@ global.syzoj = {
return router; return router;
})()); })());
let csurf = require('csurf');
app.use(csurf({ cookie: true }));
await this.connectDatabase(); await this.connectDatabase();
await this.lib('judger').connect();
this.loadModules(); this.loadModules();
}, },
async connectDatabase() { async connectDatabase() {

12
config-example.json

@ -10,7 +10,7 @@
"dialect": "sqlite", "dialect": "sqlite",
"storage": "syzoj.db" "storage": "syzoj.db"
}, },
"register_mail": true, "register_mail": false,
"email": { "email": {
"method": "aliyundm", "method": "aliyundm",
"options": { "options": {
@ -48,7 +48,7 @@
"submit_code": 102400, "submit_code": 102400,
"submit_answer": 10485760, "submit_answer": 10485760,
"custom_test_input": 20971520, "custom_test_input": 20971520,
"testdata_filecount": 5 "testdata_filecount": 100
}, },
"page": { "page": {
"problem": 50, "problem": 50,
@ -166,7 +166,7 @@
} }
], ],
"session_secret": "233", "session_secret": "233",
"judge_server_addr": "http://127.0.0.1:5284", "rabbitMQ": "amqp://localhost/",
"judge_token": "233", "email_jwt_secret": "test",
"email_jwt_secret": "test" "google_analytics": "UA-XXXXXXXX-X"
} }

78
libs/judgeResult.js

@ -0,0 +1,78 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const _ = require("lodash");
const winston = require("winston");
const interfaces = require("./judger_interfaces");
const compileError = "Compile Error", systemError = "System Error", testdataError = "No Testdata";
exports.statusToString = {};
exports.statusToString[interfaces.TestcaseResultType.Accepted] = "Accepted";
exports.statusToString[interfaces.TestcaseResultType.WrongAnswer] = "Wrong Answer";
exports.statusToString[interfaces.TestcaseResultType.PartiallyCorrect] = "Partially Correct";
exports.statusToString[interfaces.TestcaseResultType.MemoryLimitExceeded] = "Memory Limit Exceeded";
exports.statusToString[interfaces.TestcaseResultType.TimeLimitExceeded] = "Time Limit Exceeded";
exports.statusToString[interfaces.TestcaseResultType.OutputLimitExceeded] = "Output Limit Exceeded";
exports.statusToString[interfaces.TestcaseResultType.RuntimeError] = "Runtime Error";
exports.statusToString[interfaces.TestcaseResultType.FileError] = "File Error";
exports.statusToString[interfaces.TestcaseResultType.JudgementFailed] = "Judgement Failed";
exports.statusToString[interfaces.TestcaseResultType.InvalidInteraction] = "Invalid Interaction";
function firstNonAC(t) {
if (t.every(v => v === interfaces.TestcaseResultType.Accepted)) {
return interfaces.TestcaseResultType.Accepted;
}
else {
return t.find(r => r !== interfaces.TestcaseResultType.Accepted);
}
}
exports.firstNonAC = firstNonAC;
function convertResult(taskId, source) {
winston.debug(`Converting result for ${taskId}`, source);
let time = null, memory = null, score = null, done = true, statusString = null;
if (source.compile && source.compile.status === interfaces.TaskStatus.Failed) {
statusString = compileError;
}
else if (source.error != null) {
done = false;
if (source.error === interfaces.ErrorType.TestDataError) {
statusString = testdataError;
}
else {
statusString = systemError;
}
}
else if (source.judge != null && source.judge.subtasks != null) {
const forEveryTestcase = function (map, reduce) {
const list = source.judge.subtasks.map(s => reduce(s.cases.filter(c => c.result != null).map(c => map(c.result))));
if (list.every(x => x == null))
return null;
else
return reduce(list);
};
time = forEveryTestcase(c => (c.time ? c.time : 0), _.sum);
memory = forEveryTestcase(c => (c.memory ? c.memory : 0), _.max);
if (source.judge.subtasks.some(s => s.cases.some(c => c.status === interfaces.TaskStatus.Failed))) {
winston.debug(`Some subtasks failed, returning system error`);
statusString = systemError;
}
else {
score = _.sum(source.judge.subtasks.map(s => s.score));
const finalResult = forEveryTestcase(c => c.type, firstNonAC);
statusString = exports.statusToString[finalResult];
}
}
else {
statusString = systemError;
}
const result = {
taskId: taskId,
time: time,
memory: memory,
score: score,
statusNumber: done ? interfaces.TaskStatus.Done : interfaces.TaskStatus.Failed,
statusString: statusString,
result: source
};
winston.debug(`Result for ${taskId}`, result);
return result;
}
exports.convertResult = convertResult;
//# sourceMappingURL=judgeResult.js.map

127
libs/judger.js

@ -2,14 +2,110 @@ const enums = require('./enums'),
rp = require('request-promise'), rp = require('request-promise'),
url = require('url'); url = require('url');
const amqp = require('amqplib');
const util = require('util'); const util = require('util');
const winston = require('winston');
const msgPack = require('msgpack-lite');
const interface = require('./judger_interfaces');
const judgeResult = require('./judgeResult');
let amqpConnection;
let amqpSendChannel;
let amqpConsumeChannel;
async function connect () {
amqpConnection = await amqp.connect(syzoj.config.rabbitMQ);
amqpSendChannel = await amqpConnection.createChannel();
await amqpSendChannel.assertQueue('judge', {
maxPriority: 5,
durable: true
});
await amqpSendChannel.assertQueue('result', {
durable: true
});
await amqpSendChannel.assertExchange('progress', 'fanout', {
durable: false
});
amqpConsumeChannel = await amqpConnection.createChannel();
amqpConsumeChannel.prefetch(1);
amqpConsumeChannel.consume('result', async (msg) => {
(async(msg) => {
const data = msgPack.decode(msg.content);
winston.verbose('Received report for task ' + data.taskId);
let JudgeState = syzoj.model('judge_state');
let judge_state = await JudgeState.findOne({ where: { task_id: data.taskId } });
if(data.type === interface.ProgressReportType.Finished) {
const convertedResult = judgeResult.convertResult(data.taskId, data.progress);
winston.verbose('Reporting report finished: ' + data.taskId);
const payload = msgPack.encode({ type: interface.ProgressReportType.Reported, taskId: data.taskId });
amqpSendChannel.publish('progress', '', payload);
if(!judge_state) return;
judge_state.score = convertedResult.score;
judge_state.pending = false;
judge_state.status = convertedResult.statusString;
judge_state.total_time = convertedResult.time;
judge_state.max_memory = convertedResult.memory;
judge_state.result = convertedResult.result;
await judge_state.save();
await judge_state.updateRelatedInfo();
} else if(data.type == interface.ProgressReportType.Compiled) {
if(!judge_state) return;
judge_state.compilation = data.progress;
await judge_state.save();
} else {
winston.error("Unsupported result type: " + data.type);
}
})(msg).then(async() => {
amqpConsumeChannel.ack(msg)
}, async(err) => {
winston.error('Error handling report', err);
amqpConsumeChannel.nack(msg, false, false);
});
});
socketio = require('../modules/socketio');
const progressChannel = await amqpConnection.createChannel();
const queueName = (await progressChannel.assertQueue('', { exclusive: true })).queue;
await progressChannel.bindQueue(queueName, 'progress', '');
await progressChannel.consume(queueName, (msg) => {
const data = msgPack.decode(msg.content);
winston.verbose(`Got result from progress exchange, id: ${data.taskId}`);
(async (result) => {
if (result.type === interface.ProgressReportType.Started) {
socketio.createTask(result.taskId);
} else if (result.type === interface.ProgressReportType.Compiled) {
socketio.updateCompileStatus(result.taskId, result.progress);
} else if (result.type === interface.ProgressReportType.Progress) {
socketio.updateProgress(result.taskId, result.progress);
} else if (result.type === interface.ProgressReportType.Finished) {
socketio.updateResult(result.taskId, result.progress);
} else if (result.type === interface.ProgressReportType.Reported) {
socketio.cleanupProgress(result.taskId);
}
})(data).then(async() => {
progressChannel.ack(msg)
}, async(err) => {
winston.error('Error handling progress', err);
progressChannel.nack(msg, false, false);
});
});
winston.debug('Created progress exchange queue', queueName);
amqpConnection.on('error', (err) => {
winston.error('RabbitMQ connection failure: ${err.toString()}');
amqpConnection.close();
process.exit(1);
});
}
module.exports.connect = connect;
module.exports.judge = async function (judge_state, problem, priority) { module.exports.judge = async function (judge_state, problem, priority) {
let type, param, extraFile = null; let type, param, extraData = null;
switch (problem.type) { switch (problem.type) {
case 'submit-answer': case 'submit-answer':
type = enums.ProblemType.AnswerSubmission; type = enums.ProblemType.AnswerSubmission;
param = null; param = null;
extraFile = 'static/uploads/answer/' + judge_state.code; let fs = Promise.promisifyAll(require('fs-extra'));
extraData = await fs.readFileAsync(syzoj.model('file').resolvePath('answer', judge_state.code));
break; break;
case 'interaction': case 'interaction':
type = enums.ProblemType.Interaction; type = enums.ProblemType.Interaction;
@ -33,24 +129,13 @@ module.exports.judge = async function (judge_state, problem, priority) {
break; break;
} }
const req = { const content = {
content: { taskId: judge_state.task_id,
taskId: judge_state.task_id, testData: problem.id.toString(),
testData: problem.id.toString(), type: type,
type: type, priority: priority,
priority: priority, param: param
param: param
},
extraFileLocation: extraFile
}; };
await rp(url.resolve(syzoj.config.judge_server_addr, "/daemon/task"), { amqpSendChannel.sendToQueue('judge', msgPack.encode({ content: content, extraData: extraData }), { priority: priority });
method: 'PUT', }
body: req,
headers: {
Token: syzoj.config.judge_token
},
json: true,
simple: true
});
}

53
libs/judger_interfaces.js

@ -0,0 +1,53 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var RPCTaskType;
(function (RPCTaskType) {
RPCTaskType[RPCTaskType["Compile"] = 1] = "Compile";
RPCTaskType[RPCTaskType["RunStandard"] = 2] = "RunStandard";
RPCTaskType[RPCTaskType["RunSubmitAnswer"] = 3] = "RunSubmitAnswer";
RPCTaskType[RPCTaskType["RunInteraction"] = 4] = "RunInteraction";
})(RPCTaskType = exports.RPCTaskType || (exports.RPCTaskType = {}));
;
var ErrorType;
(function (ErrorType) {
ErrorType[ErrorType["SystemError"] = 0] = "SystemError";
ErrorType[ErrorType["TestDataError"] = 1] = "TestDataError";
})(ErrorType = exports.ErrorType || (exports.ErrorType = {}));
var TaskStatus;
(function (TaskStatus) {
TaskStatus[TaskStatus["Waiting"] = 0] = "Waiting";
TaskStatus[TaskStatus["Running"] = 1] = "Running";
TaskStatus[TaskStatus["Done"] = 2] = "Done";
TaskStatus[TaskStatus["Failed"] = 3] = "Failed";
TaskStatus[TaskStatus["Skipped"] = 4] = "Skipped";
})(TaskStatus = exports.TaskStatus || (exports.TaskStatus = {}));
var TestcaseResultType;
(function (TestcaseResultType) {
TestcaseResultType[TestcaseResultType["Accepted"] = 1] = "Accepted";
TestcaseResultType[TestcaseResultType["WrongAnswer"] = 2] = "WrongAnswer";
TestcaseResultType[TestcaseResultType["PartiallyCorrect"] = 3] = "PartiallyCorrect";
TestcaseResultType[TestcaseResultType["MemoryLimitExceeded"] = 4] = "MemoryLimitExceeded";
TestcaseResultType[TestcaseResultType["TimeLimitExceeded"] = 5] = "TimeLimitExceeded";
TestcaseResultType[TestcaseResultType["OutputLimitExceeded"] = 6] = "OutputLimitExceeded";
TestcaseResultType[TestcaseResultType["FileError"] = 7] = "FileError";
TestcaseResultType[TestcaseResultType["RuntimeError"] = 8] = "RuntimeError";
TestcaseResultType[TestcaseResultType["JudgementFailed"] = 9] = "JudgementFailed";
TestcaseResultType[TestcaseResultType["InvalidInteraction"] = 10] = "InvalidInteraction";
})(TestcaseResultType = exports.TestcaseResultType || (exports.TestcaseResultType = {}));
var RPCReplyType;
(function (RPCReplyType) {
RPCReplyType[RPCReplyType["Started"] = 1] = "Started";
RPCReplyType[RPCReplyType["Finished"] = 2] = "Finished";
RPCReplyType[RPCReplyType["Error"] = 3] = "Error";
})(RPCReplyType = exports.RPCReplyType || (exports.RPCReplyType = {}));
var ProgressReportType;
(function (ProgressReportType) {
ProgressReportType[ProgressReportType["Started"] = 1] = "Started";
ProgressReportType[ProgressReportType["Compiled"] = 2] = "Compiled";
ProgressReportType[ProgressReportType["Progress"] = 3] = "Progress";
ProgressReportType[ProgressReportType["Finished"] = 4] = "Finished";
ProgressReportType[ProgressReportType["Reported"] = 5] = "Reported";
})(ProgressReportType = exports.ProgressReportType || (exports.ProgressReportType = {}));
exports.redisBinarySuffix = '-bin';
exports.redisMetadataSuffix = '-meta';
//# sourceMappingURL=interfaces.js.map

23
libs/winston.js

@ -0,0 +1,23 @@
const winston = require('winston');
const _ = require('lodash');
const util = require('util');
function formatter(args) {
var msg = args.level + ' - ' + args.message + (_.isEmpty(args.meta) ? '' : (' - ' + util.inspect(args.meta)));
return msg;
}
function configureWinston(verbose) {
winston.configure({
transports: [
new (winston.transports.Console)({ formatter: formatter })
]
});
if (verbose) {
winston.level = 'debug';
} else {
winston.level = 'info';
}
}
module.exports.configureWinston = configureWinston;

10
models/contest_ranklist.js

@ -28,6 +28,7 @@ let ContestPlayer = syzoj.model('contest_player');
let model = db.define('contest_ranklist', { let model = db.define('contest_ranklist', {
id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
ranking_params: { type: Sequelize.TEXT, json: true },
ranklist: { type: Sequelize.TEXT, json: true } ranklist: { type: Sequelize.TEXT, json: true }
}, { }, {
timestamps: false, timestamps: false,
@ -38,6 +39,7 @@ let Model = require('./common');
class ContestRanklist extends Model { class ContestRanklist extends Model {
static async create(val) { static async create(val) {
return ContestRanklist.fromRecord(ContestRanklist.model.build(Object.assign({ return ContestRanklist.fromRecord(ContestRanklist.model.build(Object.assign({
ranking_params: '{}',
ranklist: '{}' ranklist: '{}'
}, val))); }, val)));
} }
@ -68,9 +70,17 @@ class ContestRanklist extends Model {
if (contest.type === 'noi' || contest.type === 'ioi') { if (contest.type === 'noi' || contest.type === 'ioi') {
for (let player of players) { for (let player of players) {
player.latest = 0; player.latest = 0;
player.score = 0;
for (let i in player.score_details) { for (let i in player.score_details) {
let judge_state = await JudgeState.fromID(player.score_details[i].judge_id); let judge_state = await JudgeState.fromID(player.score_details[i].judge_id);
player.latest = Math.max(player.latest, judge_state.submit_time); player.latest = Math.max(player.latest, judge_state.submit_time);
if (player.score_details[i].score != null) {
let multiplier = this.ranking_params[i] || 1.0;
player.score_details[i].weighted_score = Math.round(player.score_details[i].score * multiplier);
player.score += player.score_details[i].weighted_score;
}
} }
} }

10
models/judge_state.js

@ -60,7 +60,8 @@ let model = db.define('judge_state', {
* use this way represent because it's easy to expand // Menci:这锅我不背,是 Chenyao 留下来的坑。 * use this way represent because it's easy to expand // Menci:这锅我不背,是 Chenyao 留下来的坑。
*/ */
type: { type: Sequelize.INTEGER }, type: { type: Sequelize.INTEGER },
type_info: { type: Sequelize.INTEGER } type_info: { type: Sequelize.INTEGER },
is_public: { type: Sequelize.BOOLEAN }
}, { }, {
timestamps: false, timestamps: false,
tableName: 'judge_state', tableName: 'judge_state',
@ -79,6 +80,9 @@ let model = db.define('judge_state', {
}, },
{ {
fields: ['task_id'], fields: ['task_id'],
},
{
fields: ['id', 'is_public', 'type_info', 'type']
} }
] ]
}); });
@ -104,7 +108,8 @@ class JudgeState extends Model {
max_memory: null, max_memory: null,
status: 'Unknown', status: 'Unknown',
result: null, result: null,
task_id: randomstring.generate(10) task_id: randomstring.generate(10),
is_public: false
}, val))); }, val)));
} }
@ -194,6 +199,7 @@ class JudgeState extends Model {
this.status = 'Waiting'; this.status = 'Waiting';
await this.save(); await this.save();
} catch (err) { } catch (err) {
console.log("Error while connecting to judge frontend: " + err.toString());
throw new ErrorMessage("无法开始评测。"); throw new ErrorMessage("无法开始评测。");
} }
}); });

1
models/problem.js

@ -454,6 +454,7 @@ class Problem extends Model {
if (this.time_limit > syzoj.config.limit.time_limit) return 'Time limit too large'; if (this.time_limit > syzoj.config.limit.time_limit) return 'Time limit too large';
if (this.memory_limit <= 0) return 'Invalid memory limit'; if (this.memory_limit <= 0) return 'Invalid memory limit';
if (this.memory_limit > syzoj.config.limit.memory_limit) return 'Memory limit too large'; if (this.memory_limit > syzoj.config.limit.memory_limit) return 'Memory limit too large';
if (!['traditional', 'submit-answer', 'interaction'].includes(this.type)) return 'Invalid problem type';
if (this.type === 'traditional') { if (this.type === 'traditional') {
let filenameRE = /^[\w \-\+\.]*$/; let filenameRE = /^[\w \-\+\.]*$/;

13
modules/admin.js

@ -59,10 +59,7 @@ app.get('/admin/info', async (req, res) => {
let configItems = { let configItems = {
'title': { name: '站点标题', type: String }, 'title': { name: '站点标题', type: String },
'邮箱验证': null, 'google_analytics': { name: 'Google Analytics', type: String },
'register_mail.enabled': { name: '启用', type: Boolean },
'register_mail.address': { name: '发件人地址', type: String },
'register_mail.key': { name: '密钥', type: String },
'默认参数': null, '默认参数': null,
'default.problem.time_limit': { name: '时间限制(单位:ms)', type: Number }, 'default.problem.time_limit': { name: '时间限制(单位:ms)', type: Number },
'default.problem.memory_limit': { name: '空间限制(单位:MiB)', type: Number }, 'default.problem.memory_limit': { name: '空间限制(单位:MiB)', type: Number },
@ -325,6 +322,14 @@ app.post('/admin/other', async (req, res) => {
for (const a of articles) { for (const a of articles) {
await a.resetReplyCountAndTime(); await a.resetReplyCountAndTime();
} }
} else if (req.body.type === 'reset_codelen') {
const submissions = await JudgeState.query();
for (const s of submissions) {
if (s.type !== 'submit-answer') {
s.code_length = s.code.length;
await s.save();
}
}
} else { } else {
throw new ErrorMessage("操作类型不正确"); throw new ErrorMessage("操作类型不正确");
} }

49
modules/api_v2.js

@ -111,52 +111,3 @@ app.apiRouter.post('/api/v2/markdown', async (req, res) => {
res.send(e); res.send(e);
} }
}); });
app.apiRouter.post('/api/v2/judge/compiled', async (req, res) => {
try {
if (req.get('Token') !== syzoj.config.judge_token) return res.status(403).send({ err: 'Incorrect token' });
let data = req.body;
let JudgeState = syzoj.model('judge_state');
let judge_state = await JudgeState.findOne({ where: { task_id: req.body.taskId } });
if (!judge_state) {
res.send({ return: 1 }); // The task might have been rejudging.
return;
}
judge_state.compilation = req.body.result;
await judge_state.save();
res.send({ return: 0 });
} catch (e) {
syzoj.log(e);
res.status(500).send(e);
}
});
app.apiRouter.post('/api/v2/judge/finished', async (req, res) => {
try {
if (req.get('Token') !== syzoj.config.judge_token) return res.status(403).send({ err: 'Incorrect token' });
let data = req.body;
let JudgeState = syzoj.model('judge_state');
let judge_state = await JudgeState.findOne({ where: { task_id: req.body.taskId } });
if (!judge_state) {
res.send({ return: 1 }); // The task might have been rejudging.
return;
}
// await judge_state.updateResult(JSON.parse(req.body));
judge_state.score = data.score;
judge_state.pending = false;
judge_state.status = data.statusString;
judge_state.total_time = data.time;
judge_state.max_memory = data.memory;
judge_state.result = data.result;
await judge_state.save();
await judge_state.updateRelatedInfo();
res.send({ return: 0 });
} catch (e) {
syzoj.log(e);
res.status(500).send(e);
}
});

40
modules/contest.js

@ -61,6 +61,8 @@ app.get('/contest/:id/edit', async (req, res) => {
if (!contest) { if (!contest) {
contest = await Contest.create(); contest = await Contest.create();
contest.id = 0; contest.id = 0;
} else {
await contest.loadRelationships();
} }
let problems = [], admins = []; let problems = [], admins = [];
@ -86,20 +88,26 @@ app.post('/contest/:id/edit', async (req, res) => {
let contest_id = parseInt(req.params.id); let contest_id = parseInt(req.params.id);
let contest = await Contest.fromID(contest_id); let contest = await Contest.fromID(contest_id);
let ranklist = null;
if (!contest) { if (!contest) {
contest = await Contest.create(); contest = await Contest.create();
contest.holder_id = res.locals.user.id; contest.holder_id = res.locals.user.id;
let ranklist = await ContestRanklist.create(); ranklist = await ContestRanklist.create();
await ranklist.save();
contest.ranklist_id = ranklist.id;
// Only new contest can be set type // Only new contest can be set type
if (!['noi', 'ioi', 'acm'].includes(req.body.type)) throw new ErrorMessage('无效的赛制。'); if (!['noi', 'ioi', 'acm'].includes(req.body.type)) throw new ErrorMessage('无效的赛制。');
contest.type = req.body.type; contest.type = req.body.type;
} else {
await contest.loadRelationships();
ranklist = contest.ranklist;
} }
ranklist.ranking_params = JSON.parse(req.body.ranking_params);
await ranklist.save();
contest.ranklist_id = ranklist.id;
if (!req.body.title.trim()) throw new ErrorMessage('比赛名不能为空。'); if (!req.body.title.trim()) throw new ErrorMessage('比赛名不能为空。');
contest.title = req.body.title; contest.title = req.body.title;
contest.subtitle = req.body.subtitle; contest.subtitle = req.body.subtitle;
@ -131,7 +139,7 @@ app.get('/contest/:id', async (req, res) => {
let contest = await Contest.fromID(contest_id); let contest = await Contest.fromID(contest_id);
if (!contest) throw new ErrorMessage('无此比赛。'); if (!contest) throw new ErrorMessage('无此比赛。');
if (!contest.is_public && (!res.locals.user || !res.locals.user.is_admin)) throw new ErrorMessage('无此比赛。'); if (!contest.is_public && (!res.locals.user || !res.locals.user.is_admin)) throw new ErrorMessage('比赛未公开,请耐心等待 (´∀ `)');
const isSupervisior = await contest.isSupervisior(curUser); const isSupervisior = await contest.isSupervisior(curUser);
contest.running = contest.isRunning(); contest.running = contest.isRunning();
@ -168,6 +176,9 @@ app.get('/contest/:id', async (req, res) => {
let judge_state = await JudgeState.fromID(player.score_details[problem.problem.id].judge_id); let judge_state = await JudgeState.fromID(player.score_details[problem.problem.id].judge_id);
problem.status = judge_state.status; problem.status = judge_state.status;
problem.judge_id = player.score_details[problem.problem.id].judge_id; problem.judge_id = player.score_details[problem.problem.id].judge_id;
await contest.loadRelationships();
let multiplier = contest.ranklist.ranking_params[problem.problem.id] || 1.0;
problem.feedback = (judge_state.score * multiplier).toString() + ' / ' + (100 * multiplier).toString();
} }
} else if (contest.type === 'acm') { } else if (contest.type === 'acm') {
if (player.score_details[problem.problem.id]) { if (player.score_details[problem.problem.id]) {
@ -232,6 +243,7 @@ app.get('/contest/:id/ranklist', async (req, res) => {
const curUser = res.locals.user; const curUser = res.locals.user;
if (!contest) throw new ErrorMessage('无此比赛。'); if (!contest) throw new ErrorMessage('无此比赛。');
if (!contest.is_public && (!res.locals.user || !res.locals.user.is_admin)) throw new ErrorMessage('比赛未公开,请耐心等待 (´∀ `)');
if ([contest.allowedSeeingResult() && contest.allowedSeeingOthers(), if ([contest.allowedSeeingResult() && contest.allowedSeeingOthers(),
contest.isEnded(), contest.isEnded(),
await contest.isSupervisior(curUser)].every(x => !x)) await contest.isSupervisior(curUser)].every(x => !x))
@ -244,8 +256,20 @@ app.get('/contest/:id/ranklist', async (req, res) => {
let ranklist = await players_id.mapAsync(async player_id => { let ranklist = await players_id.mapAsync(async player_id => {
let player = await ContestPlayer.fromID(player_id); let player = await ContestPlayer.fromID(player_id);
if (contest.type === 'noi' || contest.type === 'ioi') {
player.score = 0;
}
for (let i in player.score_details) { for (let i in player.score_details) {
player.score_details[i].judge_state = await JudgeState.fromID(player.score_details[i].judge_id); player.score_details[i].judge_state = await JudgeState.fromID(player.score_details[i].judge_id);
/*** XXX: Clumsy duplication, see ContestRanklist::updatePlayer() ***/
if (contest.type === 'noi' || contest.type === 'ioi') {
let multiplier = contest.ranklist.ranking_params[i] || 1.0;
player.score_details[i].weighted_score = player.score_details[i].score == null ? null : Math.round(player.score_details[i].score * multiplier);
player.score += player.score_details[i].weighted_score;
}
} }
let user = await User.fromID(player.user_id); let user = await User.fromID(player.user_id);
@ -290,7 +314,7 @@ app.get('/contest/:id/submissions', async (req, res) => {
try { try {
let contest_id = parseInt(req.params.id); let contest_id = parseInt(req.params.id);
let contest = await Contest.fromID(contest_id); let contest = await Contest.fromID(contest_id);
if (!contest) throw new ErrorMessage('无此比赛。'); if (!contest.is_public && (!res.locals.user || !res.locals.user.is_admin)) throw new ErrorMessage('比赛未公开,请耐心等待 (´∀ `)');
if (contest.isEnded()) { if (contest.isEnded()) {
res.redirect(syzoj.utils.makeUrl(['submissions'], { contest: contest_id })); res.redirect(syzoj.utils.makeUrl(['submissions'], { contest: contest_id }));
@ -366,7 +390,7 @@ app.get('/contest/:id/submissions', async (req, res) => {
taskId: x.task_id, taskId: x.task_id,
type: pushType, type: pushType,
displayConfig: displayConfig displayConfig: displayConfig
}, syzoj.config.judge_token) : null, }, syzoj.config.session_secret) : null,
result: getRoughResult(x, displayConfig), result: getRoughResult(x, displayConfig),
running: false, running: false,
})), })),
@ -421,7 +445,7 @@ app.get('/contest/submission/:id', async (req, res) => {
taskId: judge.task_id, taskId: judge.task_id,
displayConfig: displayConfig, displayConfig: displayConfig,
type: 'detail' type: 'detail'
}, syzoj.config.judge_token) : null, }, syzoj.config.session_secret) : null,
displayConfig: displayConfig, displayConfig: displayConfig,
contest: contest, contest: contest,
}); });
@ -497,7 +521,7 @@ app.get('/contest/:id/:pid/download/additional_file', async (req, res) => {
let problem = await Problem.fromID(problem_id); let problem = await Problem.fromID(problem_id);
contest.ended = contest.isEnded(); contest.ended = contest.isEnded();
if (!(contest.isRunning() || contest.idEnded())) { if (!(contest.isRunning() || contest.isEnded())) {
if (await problem.isAllowedUseBy(res.locals.user)) { if (await problem.isAllowedUseBy(res.locals.user)) {
return res.redirect(syzoj.utils.makeUrl(['problem', problem_id, 'download', 'additional_file'])); return res.redirect(syzoj.utils.makeUrl(['problem', problem_id, 'download', 'additional_file']));
} }

13
modules/problem.js

@ -279,6 +279,7 @@ app.get('/problem/:id/export', async (req, res) => {
file_io: problem.file_io, file_io: problem.file_io,
file_io_input_name: problem.file_io_input_name, file_io_input_name: problem.file_io_input_name,
file_io_output_name: problem.file_io_output_name, file_io_output_name: problem.file_io_output_name,
type: problem.type,
tags: [] tags: []
}; };
@ -464,6 +465,7 @@ app.post('/problem/:id/import', async (req, res) => {
problem.file_io = json.obj.file_io; problem.file_io = json.obj.file_io;
problem.file_io_input_name = json.obj.file_io_input_name; problem.file_io_input_name = json.obj.file_io_input_name;
problem.file_io_output_name = json.obj.file_io_output_name; problem.file_io_output_name = json.obj.file_io_output_name;
if (json.obj.type) problem.type = json.obj.type;
let validateMsg = await problem.validate(); let validateMsg = await problem.validate();
if (validateMsg) throw new ErrorMessage('无效的题目数据配置。', null, validateMsg); if (validateMsg) throw new ErrorMessage('无效的题目数据配置。', null, validateMsg);
@ -581,6 +583,11 @@ async function setPublic(req, res, is_public) {
problem.publicizer_id = res.locals.user.id; problem.publicizer_id = res.locals.user.id;
await problem.save(); await problem.save();
JudgeState.model.update(
{ is_public: is_public },
{ where: { problem_id: id } }
);
res.redirect(syzoj.utils.makeUrl(['problem', id])); res.redirect(syzoj.utils.makeUrl(['problem', id]));
} catch (e) { } catch (e) {
syzoj.log(e); syzoj.log(e);
@ -634,7 +641,8 @@ app.post('/problem/:id/submit', app.multer.fields([{ name: 'answer', maxCount: 1
code_length: size, code_length: size,
language: null, language: null,
user_id: curUser.id, user_id: curUser.id,
problem_id: req.params.id problem_id: req.params.id,
is_public: problem.is_public
}); });
} else { } else {
let code; let code;
@ -652,7 +660,8 @@ app.post('/problem/:id/submit', app.multer.fields([{ name: 'answer', maxCount: 1
code_length: code.length, code_length: code.length,
language: req.body.language, language: req.body.language,
user_id: curUser.id, user_id: curUser.id,
problem_id: req.params.id problem_id: req.params.id,
is_public: problem.is_public
}); });
} }

298
modules/socketio.js

@ -0,0 +1,298 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const socketio = require("socket.io");
const diff = require("jsondiffpatch");
const jwt = require("jsonwebtoken");
const winston = require("winston");
const judgeResult = require("../libs/judgeResult");
const interfaces = require("../libs/judger_interfaces");
let ioInstance;
let detailProgressNamespace;
let roughProgressNamespace;
let compileProgressNamespace;
const currentJudgeList = {};
const finishedJudgeList = {};
const compiledList = [];
const clientDetailProgressList = {};
const clientDisplayConfigList = {};
function processOverallResult(source, config) {
if (source == null)
return null;
if (source.error != null) {
return {
error: source.error,
systemMessage: source.systemMessage
};
}
return {
compile: source.compile,
judge: config.showDetailResult ? (source.judge && {
subtasks: source.judge.subtasks && source.judge.subtasks.map(st => ({
score: st.score,
cases: st.cases.map(cs => ({
status: cs.status,
result: cs.result && {
type: cs.result.type,
time: config.showUsage ? cs.result.time : undefined,
memory: config.showUsage ? cs.result.memory : undefined,
scoringRate: cs.result.scoringRate,
systemMessage: cs.result.systemMessage,
input: config.showTestdata ? cs.result.input : undefined,
output: config.showTestdata ? cs.result.output : undefined,
userOutput: config.showTestdata ? cs.result.userOutput : undefined,
userError: config.showTestdata ? cs.result.userError : undefined,
spjMessage: config.showTestdata ? cs.result.spjMessage : undefined,
}
}))
}))
}) : null
};
}
function getCompileStatus(status) {
if (["System Error", "Compile Error", "No Testdata"].includes(status)) {
return status;
}
else {
return "Submitted";
}
}
function processRoughResult(source, config) {
const result = config.showResult ?
source.result :
getCompileStatus(source.result);
return {
result: result,
time: config.showUsage ? source.time : null,
memory: config.showUsage ? source.memory : null,
score: config.showScore ? source.score : null
};
}
function forAllClients(ns, taskId, exec) {
ns.in(taskId.toString()).clients((err, clients) => {
if (!err) {
clients.forEach(client => {
exec(client);
});
}
else {
winston.warn(`Error while listing socketio clients in ${taskId}`, err);
}
});
}
function initializeSocketIO(s) {
ioInstance = socketio(s);
const initializeNamespace = (name, exec) => {
winston.debug('initializing socketIO', name);
const newNamespace = ioInstance.of('/' + name);
newNamespace.on('connection', (socket) => {
socket.on('disconnect', () => {
winston.info(`Client ${socket.id} disconnected.`);
delete clientDisplayConfigList[socket.id];
if (clientDetailProgressList[socket.id]) {
delete clientDetailProgressList[socket.id];
}
});
socket.on('join', (reqJwt, cb) => {
winston.info(`Client ${socket.id} connected.`);
let req;
try {
req = jwt.verify(reqJwt, syzoj.config.session_secret);
if (req.type !== name) {
throw new Error("Request type in token mismatch.");
}
clientDisplayConfigList[socket.id] = req.displayConfig;
const taskId = req.taskId;
winston.verbose(`A client trying to join ${name} namespace for ${taskId}.`);
socket.join(taskId.toString());
exec(req, socket).then(x => cb(x), err => cb({ ok: false, message: err.toString() }));
}
catch (err) {
winston.info('Error while joining.');
cb({
ok: false,
message: err.toString()
});
return;
}
});
});
return newNamespace;
};
detailProgressNamespace = initializeNamespace('detail', async (req, socket) => {
const taskId = req.taskId;
if (finishedJudgeList[taskId]) {
winston.debug(`Judge task #${taskId} has been finished, ${JSON.stringify(currentJudgeList[taskId])}`);
return {
ok: true,
running: false,
finished: true,
result: processOverallResult(currentJudgeList[taskId], clientDisplayConfigList[socket.id]),
roughResult: processRoughResult(finishedJudgeList[taskId], clientDisplayConfigList[socket.id])
};
}
else {
winston.debug(`Judge task #${taskId} has not been finished`);
if (currentJudgeList[taskId]) {
clientDetailProgressList[socket.id] = {
version: 0,
content: processOverallResult(currentJudgeList[taskId], clientDisplayConfigList[socket.id])
};
return {
ok: true,
finished: false,
running: true,
current: clientDetailProgressList[socket.id]
};
}
else {
return {
ok: true,
finished: false,
running: false
};
}
}
});
roughProgressNamespace = initializeNamespace('rough', async (req, socket) => {
const taskId = req.taskId;
if (finishedJudgeList[taskId]) {
return {
ok: true,
running: false,
finished: true,
result: processRoughResult(finishedJudgeList[taskId], clientDisplayConfigList[socket.id])
};
}
else if (currentJudgeList[taskId]) {
return {
ok: true,
running: true,
finished: false
};
}
else {
return {
ok: true,
running: false,
finished: false
};
}
});
compileProgressNamespace = initializeNamespace('compile', async (req, socket) => {
const taskId = req.taskId;
if (compiledList[taskId]) {
return {
ok: true,
running: false,
finished: true,
result: compiledList[taskId]
};
}
else if (currentJudgeList[taskId]) {
return {
ok: true,
running: true,
finished: false
};
}
else {
return {
ok: true,
running: false,
finished: false
};
}
});
}
exports.initializeSocketIO = initializeSocketIO;
function createTask(taskId) {
winston.debug(`Judge task #${taskId} has started`);
currentJudgeList[taskId] = {};
finishedJudgeList[taskId] = null;
forAllClients(detailProgressNamespace, taskId, (clientId) => {
clientDetailProgressList[clientId] = {
version: 0,
content: {}
};
});
roughProgressNamespace.to(taskId.toString()).emit("start", { taskId: taskId });
detailProgressNamespace.to(taskId.toString()).emit("start", { taskId: taskId });
compileProgressNamespace.to(taskId.toString()).emit("start", { taskId: taskId });
}
exports.createTask = createTask;
function updateCompileStatus(taskId, result) {
winston.debug(`Updating compilation status for #${taskId}`);
compiledList[taskId] = { result: result.status === interfaces.TaskStatus.Done ? 'Submitted' : 'Compile Error' };
compileProgressNamespace.to(taskId.toString()).emit('finish', {
taskId: taskId,
result: compiledList[taskId]
});
}
exports.updateCompileStatus = updateCompileStatus;
function updateProgress(taskId, data) {
winston.verbose(`Updating progress for #${taskId}`);
currentJudgeList[taskId] = data;
forAllClients(detailProgressNamespace, taskId, (client) => {
winston.debug(`Pushing progress update to ${client}`);
if (clientDetailProgressList[client] && clientDisplayConfigList[client]) {
const original = clientDetailProgressList[client].content;
const updated = processOverallResult(currentJudgeList[taskId], clientDisplayConfigList[client]);
const version = clientDetailProgressList[client].version;
detailProgressNamespace.sockets[client].emit('update', {
taskId: taskId,
from: version,
to: version + 1,
delta: diff.diff(original, updated)
});
clientDetailProgressList[client].version++;
}
});
}
exports.updateProgress = updateProgress;
function updateResult(taskId, data) {
currentJudgeList[taskId] = data;
if (compiledList[taskId] == null) {
if (data.error != null) {
compiledList[taskId] = { result: "System Error" };
compileProgressNamespace.to(taskId.toString()).emit('finish', {
taskId: taskId,
result: compiledList[taskId]
});
}
}
const finalResult = judgeResult.convertResult(taskId, data);
const roughResult = {
result: finalResult.statusString,
time: finalResult.time,
memory: finalResult.memory,
score: finalResult.score
};
finishedJudgeList[taskId] = roughResult;
forAllClients(roughProgressNamespace, taskId, (client) => {
winston.debug(`Pushing rough result to ${client}`);
roughProgressNamespace.sockets[client].emit('finish', {
taskId: taskId,
result: processRoughResult(finishedJudgeList[taskId], clientDisplayConfigList[client])
});
});
forAllClients(detailProgressNamespace, taskId, (client) => {
if (clientDisplayConfigList[client]) {
winston.debug(`Pushing detail result to ${client}`);
detailProgressNamespace.sockets[client].emit('finish', {
taskId: taskId,
result: processOverallResult(currentJudgeList[taskId], clientDisplayConfigList[client]),
roughResult: processRoughResult(finishedJudgeList[taskId], clientDisplayConfigList[client])
});
delete clientDetailProgressList[client];
}
});
}
exports.updateResult = updateResult;
function cleanupProgress(taskId) {
setTimeout(() => { delete currentJudgeList[taskId]; }, 10000);
}
exports.cleanupProgress = cleanupProgress;
//# sourceMappingURL=socketio.js.map
initializeSocketIO(app.server);

38
modules/submission.js

@ -22,6 +22,7 @@
let JudgeState = syzoj.model('judge_state'); let JudgeState = syzoj.model('judge_state');
let User = syzoj.model('user'); let User = syzoj.model('user');
let Contest = syzoj.model('contest'); let Contest = syzoj.model('contest');
let Problem = syzoj.model('problem');
const jwt = require('jsonwebtoken'); const jwt = require('jsonwebtoken');
const { getSubmissionInfo, getRoughResult, processOverallResult } = require('../libs/submissions_process'); const { getSubmissionInfo, getRoughResult, processOverallResult } = require('../libs/submissions_process');
@ -49,12 +50,12 @@ app.get('/submissions', async (req, res) => {
else if (req.query.submitter) where.user_id = -1; else if (req.query.submitter) where.user_id = -1;
if (!req.query.contest) { if (!req.query.contest) {
where.type = { $ne: 1 }; where.type = { $eq: 0 };
} else { } else {
const contestId = Number(req.query.contest); const contestId = Number(req.query.contest);
const contest = await Contest.fromID(contestId); const contest = await Contest.fromID(contestId);
contest.ended = contest.isEnded(); contest.ended = contest.isEnded();
if (contest.ended || // If the contest is ended if ((contest.ended && contest.is_public) || // If the contest is ended and is not hidden
(curUser && await contest.isSupervisior(curUser)) // Or if the user have the permission to check (curUser && await contest.isSupervisior(curUser)) // Or if the user have the permission to check
) { ) {
where.type = { $eq: 1 }; where.type = { $eq: 1 };
@ -89,15 +90,22 @@ app.get('/submissions', async (req, res) => {
if (!inContest && (!curUser || !await curUser.hasPrivilege('manage_problem'))) { if (!inContest && (!curUser || !await curUser.hasPrivilege('manage_problem'))) {
if (req.query.problem_id) { if (req.query.problem_id) {
where.problem_id = { let problem_id = parseInt(req.query.problem_id);
$and: [ let problem = await Problem.fromID(problem_id);
{ $in: syzoj.db.literal('(SELECT `id` FROM `problem` WHERE `is_public` = 1' + (res.locals.user ? (' OR `user_id` = ' + res.locals.user.id) : '') + ')') }, if(!problem)
{ $eq: where.problem_id = parseInt(req.query.problem_id) || -1 } throw new ErrorMessage("无此题目。");
] if(await problem.isAllowedUseBy(res.locals.user)) {
}; where.problem_id = {
$and: [
{ $eq: where.problem_id = problem_id }
]
};
} else {
throw new ErrorMessage("您没有权限进行此操作。");
}
} else { } else {
where.problem_id = { where.is_public = {
$in: syzoj.db.literal('(SELECT `id` FROM `problem` WHERE `is_public` = 1' + (res.locals.user ? (' OR `user_id` = ' + res.locals.user.id) : '') + ')'), $eq: true,
}; };
} }
} else { } else {
@ -105,7 +113,7 @@ app.get('/submissions', async (req, res) => {
} }
let paginate = syzoj.utils.paginate(await JudgeState.count(where), req.query.page, syzoj.config.page.judge_state); let paginate = syzoj.utils.paginate(await JudgeState.count(where), req.query.page, syzoj.config.page.judge_state);
let judge_state = await JudgeState.query(paginate, where, [['submit_time', 'desc']]); let judge_state = await JudgeState.query(paginate, where, [['id', 'desc']]);
await judge_state.forEachAsync(async obj => obj.loadRelationships()); await judge_state.forEachAsync(async obj => obj.loadRelationships());
@ -117,7 +125,7 @@ app.get('/submissions', async (req, res) => {
taskId: x.task_id, taskId: x.task_id,
type: 'rough', type: 'rough',
displayConfig: displayConfig displayConfig: displayConfig
}, syzoj.config.judge_token) : null, }, syzoj.config.session_secret) : null,
result: getRoughResult(x, displayConfig), result: getRoughResult(x, displayConfig),
running: false, running: false,
})), })),
@ -147,9 +155,9 @@ app.get('/submission/:id', async (req, res) => {
contest = await Contest.fromID(judge.type_info); contest = await Contest.fromID(judge.type_info);
contest.ended = contest.isEnded(); contest.ended = contest.isEnded();
if (!contest.ended && if ((!contest.ended || !contest.is_public) &&
!(await judge.problem.isAllowedEditBy(res.locals.user) || await contest.isSupervisior(curUser))) { !(await judge.problem.isAllowedEditBy(res.locals.user) || await contest.isSupervisior(curUser))) {
throw new Error("对不起,在比赛结束之前,您不能查看评测结果。"); throw new Error("比赛没有结束或者没有公开哦");
} }
} }
@ -170,7 +178,7 @@ app.get('/submission/:id', async (req, res) => {
taskId: judge.task_id, taskId: judge.task_id,
type: 'detail', type: 'detail',
displayConfig: displayConfig displayConfig: displayConfig
}, syzoj.config.judge_token) : null, }, syzoj.config.session_secret) : null,
displayConfig: displayConfig, displayConfig: displayConfig,
}); });
} catch (e) { } catch (e) {

4158
package-lock.json generated

File diff suppressed because it is too large Load Diff

7
package.json

@ -23,6 +23,7 @@
}, },
"homepage": "https://github.com/syzoj/syzoj#readme", "homepage": "https://github.com/syzoj/syzoj#readme",
"dependencies": { "dependencies": {
"amqplib": "^0.5.2",
"ansi-to-html": "^0.4.2", "ansi-to-html": "^0.4.2",
"async-lock": "^0.3.9", "async-lock": "^0.3.9",
"body-parser": "^1.15.2", "body-parser": "^1.15.2",
@ -30,7 +31,6 @@
"command-line-args": "^4.0.7", "command-line-args": "^4.0.7",
"cookie-parser": "^1.4.3", "cookie-parser": "^1.4.3",
"cssfilter": "0.0.10", "cssfilter": "0.0.10",
"csurf": "^1.9.0",
"download": "^5.0.3", "download": "^5.0.3",
"ejs": "^2.5.2", "ejs": "^2.5.2",
"express": "^4.14.0", "express": "^4.14.0",
@ -39,9 +39,12 @@
"fs-extra": "^4.0.1", "fs-extra": "^4.0.1",
"gravatar": "^1.5.2", "gravatar": "^1.5.2",
"js-yaml": "^3.9.0", "js-yaml": "^3.9.0",
"jsondiffpatch": "0.2.4",
"jsonwebtoken": "^7.4.3", "jsonwebtoken": "^7.4.3",
"katex": "^0.10.0-rc.1",
"moemark-renderer": "^1.2.6", "moemark-renderer": "^1.2.6",
"moment": "^2.15.0", "moment": "^2.15.0",
"msgpack-lite": "^0.1.26",
"multer": "^1.2.0", "multer": "^1.2.0",
"mysql": "^2.11.1", "mysql": "^2.11.1",
"node-7z": "^0.4.0", "node-7z": "^0.4.0",
@ -53,10 +56,12 @@
"sendmail": "^1.1.1", "sendmail": "^1.1.1",
"sequelize": "^3.24.3", "sequelize": "^3.24.3",
"session-file-store": "^1.0.0", "session-file-store": "^1.0.0",
"socket.io": "^2.0.3",
"sqlite3": "^3.1.4", "sqlite3": "^3.1.4",
"syzoj-divine": "^1.0.2", "syzoj-divine": "^1.0.2",
"tmp-promise": "^1.0.3", "tmp-promise": "^1.0.3",
"waliyun": "^3.1.1", "waliyun": "^3.1.1",
"winston": "^2.3.1",
"xss": "^0.3.3" "xss": "^0.3.3"
} }
} }

BIN
static/fonts/-_Ctzj9b56b8RgXW8FArifk_vArhqVIZ0nv9q090hN8.woff2

Binary file not shown.

BIN
static/fonts/1YwB1sO8YE1Lyjf12WNiUA.woff2

Binary file not shown.

BIN
static/fonts/59ZRklaO5bWGqF5A9baEERJtnKITppOI_IvcXXDNrsc.woff2

Binary file not shown.

BIN
static/fonts/AcvTq8Q0lyKKNxRlL28RnxJtnKITppOI_IvcXXDNrsc.woff2

Binary file not shown.

BIN
static/fonts/H2DMvhDLycM56KNuAtbJYA.woff2

Binary file not shown.

BIN
static/fonts/HkF_qI1x_noxlxhrhMQYEFtXRa8TVwTICgirnJhmVJw.woff2

Binary file not shown.

BIN
static/fonts/K88pR3goAWT7BTt32Z01mxJtnKITppOI_IvcXXDNrsc.woff2

Binary file not shown.

BIN
static/fonts/LWCjsQkB6EMdfHrEVqA1KRJtnKITppOI_IvcXXDNrsc.woff2

Binary file not shown.

BIN
static/fonts/ObQr5XYcoH0WBoUxiaYK3_Y6323mHUZFJMgTvxaG2iE.woff2

Binary file not shown.

BIN
static/fonts/PLygLKRVCQnA5fhu3qk5fQ.woff2

Binary file not shown.

BIN
static/fonts/PRmiXeptR36kaC0GEAetxgalQocB-__pDVGhF3uS2Ks.woff2

Binary file not shown.

BIN
static/fonts/PRmiXeptR36kaC0GEAetxiFaMxiho_5XQnyRZzQsrZs.woff2

Binary file not shown.

BIN
static/fonts/PRmiXeptR36kaC0GEAetxi_vZmeiCMnoWNN9rHBYaTc.woff2

Binary file not shown.

BIN
static/fonts/PRmiXeptR36kaC0GEAetxmhQUTDJGru-0vvUpABgH8I.woff2

Binary file not shown.

BIN
static/fonts/PRmiXeptR36kaC0GEAetxolIZu-HDpmDIZMigmsroc4.woff2

Binary file not shown.

BIN
static/fonts/PRmiXeptR36kaC0GEAetxp6iIh_FvlUHQwED9Yt5Kbw.woff2

Binary file not shown.

BIN
static/fonts/PRmiXeptR36kaC0GEAetxujkDdvhIIFj_YMdgqpnSB0.woff2

Binary file not shown.

BIN
static/fonts/RjgO7rYTmqiVp7vzi-Q5URJtnKITppOI_IvcXXDNrsc.woff2

Binary file not shown.

BIN
static/fonts/UyBMtLsHKBKXelqf4x7VRQ.woff2

Binary file not shown.

BIN
static/fonts/YMOYVM-eg6Qs9YzV9OSqZfesZW2xOQ-xsNqO47m55DA.woff2

Binary file not shown.

BIN
static/fonts/ZKwULyCG95tk6mOqHQfRBCEAvth_LlrfE80CYdSH47w.woff2

Binary file not shown.

BIN
static/fonts/cJZKeOuBrn4kERxqtaUH3VtXRa8TVwTICgirnJhmVJw.woff2

Binary file not shown.

BIN
static/fonts/k3k702ZOKiLJc3WVjuplzBWV49_lSm1NYrwo-zkhivY.woff2

Binary file not shown.

BIN
static/fonts/k3k702ZOKiLJc3WVjuplzD0LW-43aMEzIO6XUTLjad8.woff2

Binary file not shown.

BIN
static/fonts/k3k702ZOKiLJc3WVjuplzJX5f-9o1vgP2EXwfjgl7AY.woff2

Binary file not shown.

BIN
static/fonts/k3k702ZOKiLJc3WVjuplzK-j2U0lmluP9RWlSytm3ho.woff2

Binary file not shown.

BIN
static/fonts/k3k702ZOKiLJc3WVjuplzKaRobkAwv3vxw3jMhVENGA.woff2

Binary file not shown.

BIN
static/fonts/k3k702ZOKiLJc3WVjuplzOgdm0LZdjqr5-oayXSOefg.woff2

Binary file not shown.

BIN
static/fonts/k3k702ZOKiLJc3WVjuplzP8zf_FOSsgRmwsS7Aa9k2w.woff2

Binary file not shown.

BIN
static/fonts/u-WUoqrET9fUeobQW7jkRRJtnKITppOI_IvcXXDNrsc.woff2

Binary file not shown.

BIN
static/fonts/xjAJXh38I15wypJXxuGMBiYE0-AqJ3nfInTTiDXDjU4.woff2

Binary file not shown.

BIN
static/fonts/xjAJXh38I15wypJXxuGMBjTOQ_MqJVwkKsUn0wKzc2I.woff2

Binary file not shown.

BIN
static/fonts/xjAJXh38I15wypJXxuGMBjUj_cnvWIuuBMVgbX098Mw.woff2

Binary file not shown.

BIN
static/fonts/xjAJXh38I15wypJXxuGMBkbcKLIaa1LC45dFaAfauRA.woff2

Binary file not shown.

BIN
static/fonts/xjAJXh38I15wypJXxuGMBmo_sUJ8uO4YLWRInS22T3Y.woff2

Binary file not shown.

BIN
static/fonts/xjAJXh38I15wypJXxuGMBo4P5ICox8Kq3LLUNMylGO4.woff2

Binary file not shown.

BIN
static/fonts/xjAJXh38I15wypJXxuGMBr6up8jxqWt8HVA3mDhkV_0.woff2

Binary file not shown.

BIN
static/fonts/xozscpT2726on7jbcb_pAhJtnKITppOI_IvcXXDNrsc.woff2

Binary file not shown.

16570
static/libs/Chart.js/Chart.bundle.js

File diff suppressed because it is too large Load Diff

16
static/libs/Chart.js/Chart.bundle.min.js vendored

File diff suppressed because one or more lines are too long

12269
static/libs/Chart.js/Chart.js vendored

File diff suppressed because it is too large Load Diff

14
static/libs/Chart.js/Chart.min.js vendored

File diff suppressed because one or more lines are too long

1
static/libs/KaTeX/contrib/auto-render.min.js vendored

@ -1 +0,0 @@
(function(e){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=e()}else if(typeof define==="function"&&define.amd){define([],e)}else{var t;if(typeof window!=="undefined"){t=window}else if(typeof global!=="undefined"){t=global}else if(typeof self!=="undefined"){t=self}else{t=this}t.renderMathInElement=e()}})(function(){var e,t,r;return function n(e,t,r){function a(o,l){if(!t[o]){if(!e[o]){var f=typeof require=="function"&&require;if(!l&&f)return f(o,!0);if(i)return i(o,!0);var d=new Error("Cannot find module '"+o+"'");throw d.code="MODULE_NOT_FOUND",d}var s=t[o]={exports:{}};e[o][0].call(s.exports,function(t){var r=e[o][1][t];return a(r?r:t)},s,s.exports,n,e,t,r)}return t[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)a(r[o]);return a}({1:[function(e,t,r){var n=e("./splitAtDelimiters");var a=function(e,t){var r=[{type:"text",data:e}];for(var a=0;a<t.length;a++){var i=t[a];r=n(r,i.left,i.right,i.display||false)}return r};var i=function(e,t){var r=a(e,t);var n=document.createDocumentFragment();for(var i=0;i<r.length;i++){if(r[i].type==="text"){n.appendChild(document.createTextNode(r[i].data))}else{var o=document.createElement("span");var l=r[i].data;try{katex.render(l,o,{displayMode:r[i].display})}catch(f){if(!(f instanceof katex.ParseError)){throw f}console.error("KaTeX auto-render: Failed to parse `"+r[i].data+"` with ",f);n.appendChild(document.createTextNode(r[i].rawData));continue}n.appendChild(o)}}return n};var o=function(e,t,r){for(var n=0;n<e.childNodes.length;n++){var a=e.childNodes[n];if(a.nodeType===3){var l=i(a.textContent,t);n+=l.childNodes.length-1;e.replaceChild(l,a)}else if(a.nodeType===1){var f=r.indexOf(a.nodeName.toLowerCase())===-1;if(f){o(a,t,r)}}}};var l={delimiters:[{left:"$$",right:"$$",display:true},{left:"\\[",right:"\\]",display:true},{left:"\\(",right:"\\)",display:false}],ignoredTags:["script","noscript","style","textarea","pre","code"]};var f=function(e){var t;var r;for(var n=1,a=arguments.length;n<a;n++){t=arguments[n];for(r in t){if(Object.prototype.hasOwnProperty.call(t,r)){e[r]=t[r]}}}return e};var d=function(e,t){if(!e){throw new Error("No element provided to render")}t=f({},l,t);o(e,t.delimiters,t.ignoredTags)};t.exports=d},{"./splitAtDelimiters":2}],2:[function(e,t,r){var n=function(e,t,r){var n=r;var a=0;var i=e.length;while(n<t.length){var o=t[n];if(a<=0&&t.slice(n,n+i)===e){return n}else if(o==="\\"){n++}else if(o==="{"){a++}else if(o==="}"){a--}n++}return-1};var a=function(e,t,r,a){var i=[];for(var o=0;o<e.length;o++){if(e[o].type==="text"){var l=e[o].data;var f=true;var d=0;var s;s=l.indexOf(t);if(s!==-1){d=s;i.push({type:"text",data:l.slice(0,d)});f=false}while(true){if(f){s=l.indexOf(t,d);if(s===-1){break}i.push({type:"text",data:l.slice(d,s)});d=s}else{s=n(r,l,d+t.length);if(s===-1){break}i.push({type:"math",data:l.slice(d+t.length,s),rawData:l.slice(d,s+r.length),display:a});d=s+r.length}f=!f}i.push({type:"text",data:l.slice(d)})}else{i.push(e[o])}}return i};t.exports=a},{}]},{},[1])(1)});

BIN
static/libs/KaTeX/fonts/KaTeX_AMS-Regular.eot

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_AMS-Regular.ttf

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_AMS-Regular.woff

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_AMS-Regular.woff2

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Caligraphic-Bold.eot

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Caligraphic-Bold.ttf

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Caligraphic-Bold.woff

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Caligraphic-Bold.woff2

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Caligraphic-Regular.eot

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Caligraphic-Regular.ttf

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Caligraphic-Regular.woff

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Caligraphic-Regular.woff2

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Fraktur-Bold.eot

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Fraktur-Bold.ttf

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Fraktur-Bold.woff

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Fraktur-Bold.woff2

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Fraktur-Regular.eot

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Fraktur-Regular.ttf

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Fraktur-Regular.woff

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Fraktur-Regular.woff2

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Main-Bold.eot

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Main-Bold.ttf

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Main-Bold.woff

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Main-Bold.woff2

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Main-Italic.eot

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Main-Italic.ttf

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Main-Italic.woff

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Main-Italic.woff2

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Main-Regular.eot

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Main-Regular.ttf

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Main-Regular.woff

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Main-Regular.woff2

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Math-BoldItalic.eot

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Math-BoldItalic.ttf

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Math-BoldItalic.woff

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Math-BoldItalic.woff2

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Math-Italic.eot

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Math-Italic.ttf

Binary file not shown.

BIN
static/libs/KaTeX/fonts/KaTeX_Math-Italic.woff

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save