diff --git a/app.js b/app.js index a91284a..fe2b9f8 100644 --- a/app.js +++ b/app.js @@ -39,12 +39,16 @@ global.syzoj = { console.log(obj); }, async run() { + let winstonLib = require('./libs/winston'); + winstonLib.configureWinston(true); + let Express = require('express'); global.app = Express(); syzoj.production = app.get('env') === '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)}...`); }); diff --git a/libs/judger.js b/libs/judger.js index b5ed963..cb4a67b 100644 --- a/libs/judger.js +++ b/libs/judger.js @@ -49,10 +49,12 @@ async function connect () { judge_state.result = convertedResult.result; await judge_state.save(); await judge_state.updateRelatedInfo(); - } else { + } 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) @@ -61,6 +63,34 @@ async function connect () { 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(); diff --git a/modules/winston.js b/libs/winston.js similarity index 91% rename from modules/winston.js rename to libs/winston.js index 4849203..dfd668a 100644 --- a/modules/winston.js +++ b/libs/winston.js @@ -20,4 +20,4 @@ function configureWinston(verbose) { } } -configureWinston(false); +module.exports.configureWinston = configureWinston; diff --git a/modules/socketio.js b/modules/socketio.js new file mode 100644 index 0000000..bced125 --- /dev/null +++ b/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_1 = require("../libs/judgeResult"); +const interfaces_1 = 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.judge_token); + 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_1.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_1.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); diff --git a/package-lock.json b/package-lock.json index 41b2295..9ccd318 100644 --- a/package-lock.json +++ b/package-lock.json @@ -51,6 +51,11 @@ "resolved": "https://registry.npmjs.org/addressparser/-/addressparser-1.0.1.tgz", "integrity": "sha1-R6++GiqSYhkdtoOOT9HTm0CCF0Y=" }, + "after": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", + "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=" + }, "ajv": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", @@ -106,6 +111,11 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, + "ansi-styles": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz", + "integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94=" + }, "ansi-to-html": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/ansi-to-html/-/ansi-to-html-0.4.2.tgz", @@ -164,6 +174,11 @@ "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.2.tgz", "integrity": "sha1-X8w3OSB3VyPP1k1lxkvvU7+eum0=" }, + "arraybuffer.slice": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", + "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==" + }, "asn1": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", @@ -179,6 +194,11 @@ "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=" }, + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" + }, "async-lock": { "version": "0.3.10", "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-0.3.10.tgz", @@ -202,6 +222,11 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" }, + "backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" + }, "bagpipe": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/bagpipe/-/bagpipe-0.3.5.tgz", @@ -212,11 +237,21 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, + "base64-arraybuffer": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", + "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=" + }, "base64-js": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz", "integrity": "sha1-EQHpVE9KdrG8OybUUsqW16NeeXg=" }, + "base64id": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", + "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=" + }, "base64url": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/base64url/-/base64url-2.0.0.tgz", @@ -231,6 +266,14 @@ "tweetnacl": "0.14.5" } }, + "better-assert": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", + "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", + "requires": { + "callsite": "1.0.0" + } + }, "bignumber.js": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-3.1.2.tgz", @@ -252,6 +295,11 @@ "readable-stream": "2.3.3" } }, + "blob": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz", + "integrity": "sha1-vPEwUspURj8w+fx+lbmkdjCpSSE=" + }, "block-stream": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", @@ -406,6 +454,11 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.4.0.tgz", "integrity": "sha1-fZcZb51br39pNeJZhVSe3SpsIzk=" }, + "callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=" + }, "camelcase": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", @@ -430,6 +483,33 @@ "tunnel-agent": "0.4.3" } }, + "chalk": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", + "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=", + "requires": { + "ansi-styles": "1.1.0", + "escape-string-regexp": "1.0.5", + "has-ansi": "0.1.0", + "strip-ansi": "0.3.0", + "supports-color": "0.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", + "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=" + }, + "strip-ansi": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", + "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=", + "requires": { + "ansi-regex": "0.2.1" + } + } + } + }, "cheerio": { "version": "1.0.0-rc.2", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.2.tgz", @@ -494,6 +574,21 @@ "graceful-readlink": "1.0.1" } }, + "component-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", + "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=" + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" + }, + "component-inherit": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", + "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=" + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -958,6 +1053,70 @@ "once": "1.4.0" } }, + "engine.io": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.1.4.tgz", + "integrity": "sha1-PQIRtwpVLOhB/8fahiezAamkFi4=", + "requires": { + "accepts": "1.3.3", + "base64id": "1.0.0", + "cookie": "0.3.1", + "debug": "2.6.9", + "engine.io-parser": "2.1.2", + "uws": "0.14.5", + "ws": "3.3.3" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "engine.io-client": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.1.4.tgz", + "integrity": "sha1-T88TcLRxY70s6b4nM5ckMDUNTqE=", + "requires": { + "component-emitter": "1.2.1", + "component-inherit": "0.0.3", + "debug": "2.6.9", + "engine.io-parser": "2.1.2", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "ws": "3.3.3", + "xmlhttprequest-ssl": "1.5.5", + "yeast": "0.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "engine.io-parser": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.2.tgz", + "integrity": "sha512-dInLFzr80RijZ1rGpx1+56/uFoH7/7InhH3kZt+Ms6hT8tNx3NGW/WNSA/f8As1WkOfkuyb3tnRyuXGxusclMw==", + "requires": { + "after": "0.8.2", + "arraybuffer.slice": "0.0.7", + "base64-arraybuffer": "0.1.5", + "blob": "0.0.4", + "has-binary2": "1.0.2" + } + }, "entities": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", @@ -1354,6 +1513,41 @@ "har-schema": "1.0.5" } }, + "has-ansi": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", + "integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=", + "requires": { + "ansi-regex": "0.2.1" + }, + "dependencies": { + "ansi-regex": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", + "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=" + } + } + }, + "has-binary2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.2.tgz", + "integrity": "sha1-6D26SfC5vk0CbSc2U1DZ8D9Uvpg=", + "requires": { + "isarray": "2.0.1" + }, + "dependencies": { + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" + } + } + }, + "has-cors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" + }, "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", @@ -1442,6 +1636,11 @@ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" + }, "inflection": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz", @@ -1641,6 +1840,14 @@ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, + "jsondiffpatch": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/jsondiffpatch/-/jsondiffpatch-0.2.4.tgz", + "integrity": "sha1-1LbFOz/H2htLkcHCrsi5MrdRHVw=", + "requires": { + "chalk": "0.5.1" + } + }, "jsonfile": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", @@ -2191,6 +2398,11 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, + "object-component": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", + "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=" + }, "object-keys": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", @@ -2273,6 +2485,22 @@ "@types/node": "6.0.83" } }, + "parseqs": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", + "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", + "requires": { + "better-assert": "1.0.2" + } + }, + "parseuri": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", + "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", + "requires": { + "better-assert": "1.0.2" + } + }, "parseurl": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz", @@ -2864,6 +3092,61 @@ "hoek": "2.16.3" } }, + "socket.io": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.0.4.tgz", + "integrity": "sha1-waRZDO/4fs8TxyZS8Eb3FrKeYBQ=", + "requires": { + "debug": "2.6.7", + "engine.io": "3.1.4", + "socket.io-adapter": "1.1.1", + "socket.io-client": "2.0.4", + "socket.io-parser": "3.1.2" + } + }, + "socket.io-adapter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz", + "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs=" + }, + "socket.io-client": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.0.4.tgz", + "integrity": "sha1-CRilUkBtxeVAs4Dc2Xr8SmQzL44=", + "requires": { + "backo2": "1.0.2", + "base64-arraybuffer": "0.1.5", + "component-bind": "1.0.0", + "component-emitter": "1.2.1", + "debug": "2.6.7", + "engine.io-client": "3.1.4", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "object-component": "0.0.3", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "socket.io-parser": "3.1.2", + "to-array": "0.1.4" + } + }, + "socket.io-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.1.2.tgz", + "integrity": "sha1-28IoIVH8T6675Aru3Ady66YZ9/I=", + "requires": { + "component-emitter": "1.2.1", + "debug": "2.6.7", + "has-binary2": "1.0.2", + "isarray": "2.0.1" + }, + "dependencies": { + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" + } + } + }, "source-map": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", @@ -3023,6 +3306,11 @@ "escape-string-regexp": "1.0.5" } }, + "supports-color": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz", + "integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo=" + }, "symbol-tree": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", @@ -3167,6 +3455,11 @@ "tmp": "0.0.31" } }, + "to-array": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", + "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=" + }, "topo": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/topo/-/topo-1.1.0.tgz", @@ -3257,6 +3550,11 @@ "random-bytes": "1.0.0" } }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" + }, "unbzip2-stream": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.2.4.tgz", @@ -3304,6 +3602,12 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" }, + "uws": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/uws/-/uws-0.14.5.tgz", + "integrity": "sha1-Z6rzPEaypYel9mZtAPdpEyjxSdw=", + "optional": true + }, "validate-npm-package-license": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", @@ -3467,6 +3771,16 @@ "slide": "1.1.6" } }, + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "requires": { + "async-limiter": "1.0.0", + "safe-buffer": "5.1.1", + "ultron": "1.1.1" + } + }, "xml-mapping": { "version": "1.7.1", "resolved": "https://registry.npmjs.org/xml-mapping/-/xml-mapping-1.7.1.tgz", @@ -3498,6 +3812,11 @@ "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz", "integrity": "sha1-1QH5ezvbQDr4757MIFcxh6rawOk=" }, + "xmlhttprequest-ssl": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", + "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=" + }, "xpath": { "version": "0.0.24", "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.24.tgz", @@ -3575,6 +3894,11 @@ "buffer-crc32": "0.2.13", "fd-slicer": "1.0.1" } + }, + "yeast": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" } } } diff --git a/package.json b/package.json index 78a2b8a..67c278d 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "fs-extra": "^4.0.1", "gravatar": "^1.5.2", "js-yaml": "^3.9.0", + "jsondiffpatch": "0.2.4", "jsonwebtoken": "^7.4.3", "moemark-renderer": "^1.2.6", "moment": "^2.15.0", @@ -55,6 +56,7 @@ "sendmail": "^1.1.1", "sequelize": "^3.24.3", "session-file-store": "^1.0.0", + "socket.io": "^2.0.3", "sqlite3": "^3.1.4", "syzoj-divine": "^1.0.2", "tmp-promise": "^1.0.3", diff --git a/views/submission.ejs b/views/submission.ejs index 13e2cd9..13fb3e7 100644 --- a/views/submission.ejs +++ b/views/submission.ejs @@ -108,7 +108,7 @@ Vue.component("code-box", { template: "#codeBoxTemplate", props: ['title', 'content', 'escape'], }); -const socketUrl = <%- syzoj.utils.judgeServer('detail') %>; +const socketUrl = "/detail"; const displayConfig = <%- JSON.stringify(displayConfig) %>; const token = <%- JSON.stringify(socketToken) %>; @@ -268,4 +268,4 @@ if (token != null) { } -<% include footer %> \ No newline at end of file +<% include footer %>