judge, problem_settings, problem_upload

This commit is contained in:
masnn committed 2020-04-05 09:02:35 +08:00
1 parent 5b4f5b6520
commit b3024bebb0
45 files changed
+598 -250

No files matched your search

-1
View File
@@ -59,7 +59,6 @@ MIDDLEWARE(async (ctx, next) => {
update_ip: ctx.request.ip,
update_ua: ctx.request.headers['user-agent'] || ''
}, ctx.session));
console.log(ctx.session.sid, ctx.session.uid);
let cookie = { secure: options.session.secure, httponly: true };
if (save) {
cookie.expires = ctx.session.expireAt, cookie.maxAge = expireSeconds;
+47 -2
View File
@@ -14,7 +14,7 @@ GET('/judge/fetch', requirePerm(PERM_JUDGE), async ctx => {
let rid = await queue.get('judge', false);
if (rid) {
let rdoc = await record.get(rid);
let data = {
let task = {
event: 'judge',
rid, type: 0,
pid: rdoc.pid,
@@ -22,12 +22,57 @@ GET('/judge/fetch', requirePerm(PERM_JUDGE), async ctx => {
lang: rdoc.lang,
code: rdoc.code
};
ctx.body = data;
ctx.body = { task };
}
else ctx.body = {};
});
POST('/judge/next', requirePerm(PERM_JUDGE), async ctx => {
console.log(ctx.request.body);
let body = ctx.request.body;
let rdoc = await record.get(body.rid);
let $set = {};
if (body.case) {
rdoc.testCases.push(body.case);
$set.testCases = rdoc.testCases;
}
if (body.judge_text) {
rdoc.judgeTexts.push(body.judge_text);
$set.judgeTexts = rdoc.judgeTexts;
}
if (body.compiler_text) {
rdoc.compilerTexts.push(body.compilerTexts);
$set.compilerTexts = rdoc.compilerTexts;
}
if (body.status) $set.status = body.status;
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);
ctx.body = {};
});
POST('/judge/end', requirePerm(PERM_JUDGE), async ctx => {
console.log(ctx.request.body);
let body = ctx.request.body;
let rdoc = await record.get(body.rid);
let $set = {};
if (body.case) {
rdoc.testCases.push(body.case);
$set.testCases = rdoc.testCases;
}
if (body.judge_text) {
rdoc.judgeTexts.push(body.judge_text);
$set.judgeTexts = rdoc.judgeTexts;
}
if (body.compiler_text) {
rdoc.compilerTexts.push(body.compilerTexts);
$set.compilerTexts = rdoc.compilerTexts;
}
if (body.status) $set.status = body.status;
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;
$set.judgeAt = new Date();
$set.judger = ctx.state.user._id;
await record.update(body.rid, $set);
ctx.body = {};
});
+73 -7
View File
@@ -1,29 +1,37 @@
const
fs = require('fs'),
bson = require('bson'),
validator = require('../lib/validator'),
problem = require('../model/problem'),
record = require('../model/record'),
user = require('../model/user'),
system = require('../model/system'),
{ GET, POST } = require('../service/server'),
gridfs = require('../service/gridfs'),
queue = require('../service/queue'),
{ requirePerm } = require('./tools'),
{ NoProblemError } = require('../error'),
{ NoProblemError, ProblemDataNotFoundError, BadRequestError } = require('../error'),
{ constants } = require('../options'),
{
PERM_VIEW_PROBLEM,
PERM_VIEW_PROBLEM_HIDDEN,
PERM_SUBMIT_PROBLEM,
PERM_CREATE_PROBLEM
PERM_CREATE_PROBLEM,
PERM_READ_PROBLEM_DATA,
PERM_EDIT_PROBLEM,
PERM_JUDGE
} = require('../permission');
queue.assert('judge');
GET('/p', requirePerm(PERM_VIEW_PROBLEM), async ctx => {
ctx.templateName = 'problem_main.html';
let q = {},
page = ctx.query.page || 1;
if (ctx.query.category) q.category = ctx.query.category;
if (!ctx.state.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN)) q.hidden = false;
let pdocs = await problem.getMany(q, { pid: 1 }, page, constants.PROBLEM_PER_PAGE);
ctx.templateName = 'problem_main.html';
ctx.body = { page_name: 'problem_list', page, pdocs, category: '' };
ctx.body = { page, pdocs, category: '' };
});
GET('/problem/random', requirePerm(PERM_VIEW_PROBLEM), async ctx => {
let q = {};
@@ -33,33 +41,89 @@ GET('/problem/random', requirePerm(PERM_VIEW_PROBLEM), async ctx => {
ctx.body = { pid };
});
GET('/p/:pid', requirePerm(PERM_VIEW_PROBLEM), async ctx => {
ctx.templateName = 'problem_detail.html';
let uid = ctx.state.user._id,
pid = ctx.params.pid;
let pdoc = await problem.get({ pid, uid });
if (pdoc.hidden) ctx.checkPerm(PERM_VIEW_PROBLEM_HIDDEN);
let udoc = await user.getById(pdoc.owner);
ctx.templateName = 'problem_detail.html';
ctx.body = { pdoc, udoc, title: pdoc.title };
});
GET('/p/:pid/submit', requirePerm(PERM_SUBMIT_PROBLEM), async ctx => {
ctx.templateName = 'problem_submit.html';
let uid = ctx.state.user._id,
pid = ctx.params.pid;
let pdoc = await problem.get({ pid, uid });
if (pdoc.hidden) ctx.checkPerm(PERM_VIEW_PROBLEM_HIDDEN);
let udoc = await user.getById(pdoc.owner);
ctx.templateName = 'problem_submit.html';
ctx.body = { pdoc, udoc, title: pdoc.title };
});
POST('/p/:pid/submit', requirePerm(PERM_SUBMIT_PROBLEM), async ctx => {
let rid = await record.add({
uid: ctx.state.user._id,
lang: ctx.request.body.language,
lang: ctx.request.body.lang,
code: ctx.request.body.code,
pid: ctx.params.pid
});
await queue.push('judge', rid);
ctx.state.user.nSubmit++;
ctx.body = { rid };
ctx.setRedirect = `/r/${rid}`;
});
GET('/p/:pid/settings', async ctx => {
ctx.templateName = 'problem_settings.html';
let pdoc = await problem.get({ pid: ctx.params.pid, uid: ctx.state.user._id });
if (pdoc.hidden) ctx.checkPerm(PERM_VIEW_PROBLEM_HIDDEN);
ctx.body = { pdoc };
});
POST('/p/:pid/settings', async ctx => {
ctx.templateName = 'problem_settings.html';
// TODO(masnn)
let pdoc = await problem.get({ pid: ctx.params.pid, uid: ctx.state.user._id });
ctx.body = { pdoc };
});
GET('/p/:pid/upload', async ctx => {
ctx.templateName = 'problem_upload.html';
let pdoc = await problem.get({ pid: ctx.params.pid, uid: ctx.state.user._id });
if (pdoc.owner != ctx.state.user._id) ctx.checkPerm(PERM_EDIT_PROBLEM);
let md5;
if (typeof pdoc.data == 'object') {
let files = await gridfs.find({ _id: pdoc.data }).toArray();
md5 = files[0].md5;
}
ctx.body = { pdoc, md5 };
});
POST('/p/:pid/upload', async ctx => {
ctx.templateName = 'problem_upload.html';
if (!ctx.request.files.file) throw new BadRequestError();
let pdoc = await problem.get({ pid: ctx.params.pid, uid: ctx.state.user._id });
if (pdoc.owner != ctx.state.user._id) ctx.checkPerm(PERM_EDIT_PROBLEM);
const r = fs.createReadStream(ctx.request.files.file.path);
let f = gridfs.openUploadStream('data.zip');
await new Promise((resolve, reject) => {
r.pipe(f);
f.once('finish', resolve);
f.once('error', reject);
});
let md5;
if (typeof pdoc.data == 'object') {
let files = await gridfs.find({ _id: pdoc.data }).toArray();
md5 = files[0].md5;
}
pdoc = await problem.edit(ctx.params.pid, { data: f.id });
ctx.body = { pdoc, md5 };
});
GET('/p/:pid/data', async ctx => {
let pdoc = await problem.get({ pid: ctx.params.pid, uid: ctx.state.user._id });
if (!ctx.state.user._id == pdoc.owner) ctx.checkPerm([PERM_READ_PROBLEM_DATA, PERM_JUDGE]);
if (!pdoc.data) throw new ProblemDataNotFoundError(ctx.params.pid);
else if (typeof pdoc.data == 'string') ctx.setRedirect = pdoc.data.split('from:')[1];
ctx.attachment(`${pdoc.title}.zip`);
ctx.body = gridfs.openDownloadStream(pdoc.data);
});
GET('/problem/create', requirePerm(PERM_CREATE_PROBLEM), async ctx => {
ctx.templateName = 'problem_edit.html';
ctx.body = { page_name: 'problem_create' };
});
POST('/problem/create', requirePerm(PERM_CREATE_PROBLEM), async ctx => {
let { title, pid, content, hidden } = ctx.request.body;
@@ -67,8 +131,10 @@ POST('/problem/create', requirePerm(PERM_CREATE_PROBLEM), async ctx => {
pid = pid || await system.incPidCounter();
await problem.add({ title, content, owner: ctx.state.user._id, pid, hidden });
ctx.body = { pid };
ctx.setRedirect = `/p/${pid}/settings`;
});
/*
@app.route('/p/category/{category:[^/]*}/random', 'problem_category_random')
+15 -4
View File
@@ -1,13 +1,16 @@
const
bson = require('bson'),
{ constants } = require('../options'),
{ GET } = require('../service/server'),
{ GET, POST } = require('../service/server'),
{ requirePerm } = require('../handler/tools'),
queue = require('../service/queue'),
record = require('../model/record'),
user = require('../model/user'),
problem = require('../model/problem'),
{ PERM_READ_RECORD_CODE, PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD } = require('../permission');
{ PERM_READ_RECORD_CODE, PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD, PERM_REJUDGE } = require('../permission');
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);
@@ -16,16 +19,24 @@ GET('/r', async ctx => {
udict[rdoc.uid] = await user.getById(rdoc.uid);
pdict[rdoc.pid] = await problem.get({ pid: rdoc.pid, uid: ctx.state.user._id });
}
ctx.templateName = 'record_main.html';
ctx.body = { page, rdocs, pdict, udict };
});
GET('/r/:rid', async ctx => {
ctx.templateName = 'record_detail.html';
let uid = ctx.state.user._id, rid = new bson.ObjectID(ctx.params.rid);
let rdoc = await record.get(rid);
if (rdoc.hidden) ctx.checkPerm(PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD);
if (rdoc.uid != uid && !ctx.state.user.hasPerm(PERM_READ_RECORD_CODE)) rdoc.code = null;
ctx.body = { rdoc, show_status: true };
});
POST('/r/:rid/rejudge', requirePerm(PERM_REJUDGE), async ctx => {
ctx.templateName = 'record_detail.html';
ctx.body = { rdoc };
let uid = ctx.state.user._id, rid = new bson.ObjectID(ctx.params.rid);
let rdoc = await record.get(rid);
if (rdoc.hidden) ctx.checkPerm(PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD);
if (rdoc.uid != uid && !ctx.state.user.hasPerm(PERM_READ_RECORD_CODE)) rdoc.code = null;
if (rdoc) await queue.push('judge', rid);
ctx.body = { rdoc, show_status: true };
});
/*
+34 -13
View File
@@ -1,6 +1,5 @@
const
path = require('path'),
md5 = require('blueimp-md5'),
static = require('koa-static'),
nunjucks = require('nunjucks'),
hljs = require('hljs'),
@@ -9,14 +8,9 @@ const
options = require('../options'),
perm = require('../permission'),
builtin = require('../model/builtin'),
md5 = require('../lib/md5'),
{ MIDDLEWARE } = require('../service/server'),
{ NotFoundError } = require('../error'),
MD5_REGEX = /^[0-9a-f]{32}$/;
function getHash(email) {
email = (typeof email === 'string') ? email.trim().toLowerCase() : 'unspecified';
return email.match(MD5_REGEX) ? email : md5(email);
}
{ NotFoundError } = require('../error');
class Markdown extends MarkdownIt {
constructor() {
@@ -35,6 +29,14 @@ class Markdown extends MarkdownIt {
}
}
const md = new Markdown();
function datetime_span(dt, { relative = true } = {}) {
dt = new Date(dt);
return '<span class="time{0}" data-timestamp="{1}">{2}</span>'.format(
relative ? ' relative' : '',
dt.getTime(),
dt.toLocaleString()
);
}
class Nunjucks extends nunjucks.Environment {
constructor() {
super(
@@ -51,12 +53,26 @@ class Nunjucks extends nunjucks.Environment {
return md.render(self);
});
this.addFilter('gravatar_url', function (email, size) {
return `//gravatar.loli.net/avatar/${getHash(email)}?d=mm&s=${size}`;
return `//gravatar.loli.net/avatar/${md5(email.toString().trim().toLowerCase())}?d=mm&s=${size}`;
});
this.addFilter('format_size', function (size, base = 1) {
size *= base;
let unit = 1024;
let unit_names = ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
for (let unit_name of unit_names) {
if (size < unit) return '{0} {1}'.format(Math.round(size), unit_name);
size /= unit;
}
return '{0} {1}'.format(Math.round(size * unit), unit_names[unit_names.length - 1]);
});
this.addGlobal('typeof', o => typeof o);
this.addGlobal('console', console);
this.addGlobal('static_url', str => `/${str}`);
this.addGlobal('reverse_url', str => str);
this.addGlobal('datetime_span', datetime_span);
this.addGlobal('perm', perm);
this.addGlobal('builtin', builtin);
this.addGlobal('status', builtin.STATUS);
}
}
const env = new Nunjucks();
@@ -67,6 +83,7 @@ MIDDLEWARE(async (ctx, next) => {
cdn_prefix: '/',
url_prefix: '/'
};
ctx.preferJson = ctx.request.headers['accept'].includes('application/json');
ctx.render = async (name, context) => {
ctx.user = ctx.state.user;
ctx.translate = str => {
@@ -77,7 +94,8 @@ MIDDLEWARE(async (ctx, next) => {
ctx.body = await new Promise((resolve, reject) => {
env.render(name, Object.assign(ctx.state, context, {
handler: ctx,
_: ctx.translate
_: ctx.translate,
user: ctx.state.user
}), (error, res) => {
if (error) reject(error);
else resolve(res);
@@ -89,6 +107,7 @@ MIDDLEWARE(async (ctx, next) => {
try {
await next();
if (ctx.body || ctx.templateName) {
if (ctx.preferJson) return;
if (ctx.query.template || ctx.templateName) {
ctx.body = ctx.body || {};
Object.assign(ctx.body, JSON.parse(ctx.query.data || '{}'));
@@ -99,11 +118,13 @@ MIDDLEWARE(async (ctx, next) => {
}
} else throw new NotFoundError();
} catch (error) {
console.log(error);
if (error.toString().startsWith('NotFoundError')) console.log(error);
if (error.toString().startsWith('Template render error')) throw error;
await ctx.render('error.html', { error });
if (ctx.preferJson) ctx.body = { error };
else await ctx.render('error.html', { error });
}
} catch (error) {
await ctx.render('bsod.html', { error });
if (ctx.preferJson) ctx.body = { error };
else await ctx.render('bsod.html', { error });
}
});