record_detail autoupdate

This commit is contained in:
masnn committed 2020-04-09 17:06:01 +08:00
1 parent 3a90e2893f
commit c68953c59f
17 files changed
+209 -278

No files matched your search

+1 -1
View File
@@ -69,4 +69,4 @@ MIDDLEWARE(async (ctx, next) => {
console.error(e.stack);
ctx.body = { error: { message: e.message, params: e.params, stack: e.stack } };
}
});
}, true);
+9 -6
View File
@@ -1,9 +1,10 @@
const
{ GET, POST } = require('../service/server'),
queue = require('../service/queue'),
record = require('../model/record'),
{ requirePerm } = require('./tools'),
{ PERM_JUDGE } = require('../permission');
{ PERM_JUDGE } = require('../permission'),
record = require('../model/record'),
bus = require('../service/bus'),
queue = require('../service/queue'),
{ GET, POST } = require('../service/server');
queue.assert('judge');
@@ -46,7 +47,8 @@ POST('/judge/next', requirePerm(PERM_JUDGE), async ctx => {
if (body.score) $set.score = body.score;
if (body.time_ms) $set.time = body.time_ms;
if (body.memory_kb) $set.memory = body.memory_kb;
await record.update(body.rid, $set);
rdoc = await record.update(body.rid, $set);
bus.publish('record_change', rdoc);
ctx.body = {};
});
POST('/judge/end', requirePerm(PERM_JUDGE), async ctx => {
@@ -71,6 +73,7 @@ POST('/judge/end', requirePerm(PERM_JUDGE), async ctx => {
if (body.memory_kb) $set.memory = body.memory_kb;
$set.judgeAt = new Date();
$set.judger = ctx.state.user._id;
await record.update(body.rid, $set);
rdoc = await record.update(body.rid, $set);
bus.publish('record_change', rdoc);
ctx.body = {};
});
+50 -6
View File
@@ -1,26 +1,49 @@
const
bson = require('bson'),
{ constants } = require('../options'),
{ GET, POST } = require('../service/server'),
{ PERM_READ_RECORD_CODE, PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD,
PERM_REJUDGE, PERM_VIEW_PROBLEM_HIDDEN } = require('../permission'),
{ requirePerm } = require('../handler/tools'),
queue = require('../service/queue'),
problem = require('../model/problem'),
record = require('../model/record'),
user = require('../model/user'),
problem = require('../model/problem'),
{ PERM_READ_RECORD_CODE, PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD, PERM_REJUDGE } = require('../permission');
bus = require('../service/bus'),
queue = require('../service/queue'),
{ GET, POST, SOCKET } = require('../service/server');
GET('/r', async ctx => {
ctx.templateName = 'record_main.html';
let q = {},
page = ctx.query.page || 1;
let rdocs = await record.getMany(q, { rid: 1 }, page, constants.RECORD_PER_PAGE);
let pdict, udict;
for (let rdoc in rdocs) {
let pdict = {}, udict = {};
for (let rdoc of rdocs) {
udict[rdoc.uid] = await user.getById(rdoc.uid);
pdict[rdoc.pid] = await problem.get({ pid: rdoc.pid, uid: ctx.state.user._id });
}
ctx.body = { page, rdocs, pdict, udict };
});
SOCKET('/record-conn', class RecordConnHandler {
constructor(conn) {
this.tid = null;
this.h = async data => {
let rdoc = data.value;
if (rdoc.tid && rdoc.tid != this.tid.toString()) return;
let [udoc, pdoc] = await Promise.all([user.getById(rdoc.uid), problem.get({ pid: rdoc.pid })]);
if (pdoc.hidden && !conn.state.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN)) pdoc = null;
conn.send({ html: await conn.renderHTML('record_main_tr.html', { rdoc, udoc, pdoc }) });
};
}
onOpen() {
bus.subscribe(['record_change'], this.h);
}
onMessage(data) {
if (data.tid) this.tid = data.tid;
}
onClose() {
bus.unsubscribe(['record_change'], this.h);
}
});
GET('/r/:rid', async ctx => {
ctx.templateName = 'record_detail.html';
let uid = ctx.state.user._id, rid = new bson.ObjectID(ctx.params.rid);
@@ -29,6 +52,27 @@ GET('/r/:rid', async ctx => {
if (rdoc.uid != uid && !ctx.state.user.hasPerm(PERM_READ_RECORD_CODE)) rdoc.code = null;
ctx.body = { rdoc, show_status: true };
});
SOCKET('/record-detail-conn', class RecordDetailConnHandler {
constructor(conn) {
this.rid = conn.params.rid;
this.h = async data => {
let rdoc = data.value;
if (rdoc.rid.toString() != this.rid) return;
let [udoc, pdoc] = await Promise.all([user.getById(rdoc.uid), problem.get({ pid: rdoc.pid })]);
if (pdoc.hidden && !conn.state.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN)) pdoc = null;
conn.send({ html: await conn.renderHTML('record_main_tr.html', { rdoc, udoc, pdoc }) });
};
}
async onOpen() {
bus.subscribe(['record_change'], this.h);
}
onMessage(data) {
if (data.rid) this.rid = data.rid;
}
onClose() {
bus.unsubscribe(['record_change'], this.h);
}
});
POST('/r/:rid/rejudge', requirePerm(PERM_REJUDGE), async ctx => {
ctx.templateName = 'record_detail.html';
let uid = ctx.state.user._id, rid = new bson.ObjectID(ctx.params.rid);
+13 -6
View File
@@ -30,10 +30,11 @@ class Markdown extends MarkdownIt {
}
const md = new Markdown();
function datetime_span(dt, { relative = true } = {}) {
dt = new Date(dt);
if (dt.generationTime) dt = new Date(dt.generationTime * 1000);
else if (typeof dt == 'number' || typeof dt == 'string') dt = new Date(dt);
return '<span class="time{0}" data-timestamp="{1}">{2}</span>'.format(
relative ? ' relative' : '',
dt.getTime(),
dt.getTime() / 1000,
dt.toLocaleString()
);
}
@@ -93,14 +94,14 @@ MIDDLEWARE(async (ctx, next) => {
url_prefix: '/'
};
ctx.preferJson = (ctx.request.headers['accept'] || '').includes('application/json');
ctx.render = async (name, context) => {
ctx.renderHTML = (name, context) => {
ctx.user = ctx.state.user;
ctx.translate = str => {
if (!str) return '';
return str.toString().translate(ctx.state.user.language);
};
ctx.has_perm = perm => ctx.state.user.hasPerm(perm);
ctx.body = await new Promise((resolve, reject) => {
return new Promise((resolve, reject) => {
env.render(name, Object.assign(ctx.state, context, {
handler: ctx,
_: ctx.translate,
@@ -110,6 +111,9 @@ MIDDLEWARE(async (ctx, next) => {
else resolve(res);
});
});
};
ctx.render = async (name, context) => {
ctx.body = await ctx.renderHTML(name, context);
ctx.response.type = 'text/html';
};
try {
@@ -125,8 +129,11 @@ MIDDLEWARE(async (ctx, next) => {
ctx.response.type = 'application/octet-stream';
ctx.redirect(ctx.setRedirect);
}
} else throw new NotFoundError();
} else {
throw new NotFoundError();
}
} catch (error) {
if (error instanceof NotFoundError) ctx.status = 404;
if (error.toString().startsWith('NotFoundError')) console.log(error);
if (error.toString().startsWith('Template render error')) throw error;
if (ctx.preferJson) ctx.body = { error };
@@ -136,4 +143,4 @@ MIDDLEWARE(async (ctx, next) => {
if (ctx.preferJson) ctx.body = { error };
else await ctx.render('bsod.html', { error });
}
});
}, true);
+3
View File
@@ -14,6 +14,9 @@ class USER {
this.salt = user.salt;
this.hash = user.hash;
this.perm = user.perm;
this.lang = user.language || 'zh_CN';
this.codeLang = user.codeLang || 'c';
this.codeTemplate = user.codeTemplate || '';
}
hasPerm(perm) {
return this.perm == '-' || (this.perm || '').includes(perm);
+15
View File
@@ -0,0 +1,15 @@
const EventEmitter = require('events').EventEmitter;
const bus = new EventEmitter();
module.exports = {
subscribe(events, handler) {
for (let event of events)
bus.on(event, handler);
},
unsubscribe(events, handler) {
for (let event of events)
bus.off(event, handler);
},
publish(event, data) {
bus.emit(event, { value: data, event });
}
};
+51 -6
View File
@@ -3,14 +3,13 @@ const
morgan = require('koa-morgan'),
Body = require('koa-body'),
Router = require('koa-router'),
SocketIO = require('socket.io');
sockjs = require('sockjs');
const options = require('../options');
let http = options.listen.https ? require('https') : require('http');
let app = new Koa();
let server = http.createServer(app.callback());
let io = SocketIO(server);
app.keys = options.session.keys;
app.use(Body({
multipart: true,
@@ -19,12 +18,14 @@ app.use(Body({
}
}));
let router = new Router();
let m = [];
/**
* @param {import('koa').Middleware} middleware
* @param {boolean} installForSocket
*/
function MIDDLEWARE(middleware) {
function MIDDLEWARE(middleware, installForSocket = false) {
app.use(middleware);
if (installForSocket) m.push(middleware);
}
/**
* @param {string} route
@@ -44,13 +45,57 @@ function POST(route, ...handler) {
* @callback SocketIOHandler
* @param {import('socket.io').Socket} socket
*/
/**
* @callback SockJSHandler
* @param {import('sockjs').Connection} conn
*/
/**
* @param {string} prefix
* @param {SocketIOHandler} handler
*/
function SOCKET(prefix, handler) {
io.of(prefix).on('connection', handler);
const sock = sockjs.createServer({ prefix });
sock.on('connection', async conn => {
conn.cookies = {
get(name) {
return conn.cookies[name];
},
set() { }
};
conn.state = {};
conn.params = {};
conn.request = {
headers: conn.headers
};
let p = conn.url.split('?')[1].split('&');
for (let i in p) p[i] = p[i].split('=');
for (let i in p) conn.params[p[i][0]] = decodeURIComponent(p[i][1]);
await new Promise((resolve, reject) => {
conn.once('data', msg => {
for (let i of msg.split(';')) {
i = i.trim().split('=');
conn.cookies[i[0]] = i[1];
}
resolve();
});
setTimeout(reject, 5000);
});
for (let i of m) {
await new Promise((resolve, reject) => {
i(conn, resolve).catch(reject);
});
}
conn.send = data => {
conn.write(JSON.stringify(data));
};
let h = new handler(conn);
if (h.onOpen) conn.on('open', h.onOpen);
if (h.onClose) conn.on('close', h.onClose);
if (h.onMessage) conn.on('data', h.onMessage);
});
sock.installHandlers(server);
}
exports.MIDDLEWARE = MIDDLEWARE;
exports.GET = GET;
exports.POST = POST;
@@ -58,6 +103,6 @@ exports.SOCKET = SOCKET;
exports.start = function start() {
app.use(morgan(':method :url :status :res[content-length] - :response-time ms'));
app.use(router.routes()).use(router.allowedMethods());
app.listen(options.listen.port);
server.listen(options.listen.port);
console.log('Server listening at: %s', options.listen.port);
};