"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);