mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
添加部分注释,修复一些功能
This commit is contained in:
1 parent
f40e4e2c74
commit
d3e36fcb6c
31 files changed
+318
-172
No files matched your search
@@ -250,15 +250,12 @@ class ContestCreateHandler extends ContestHandler {
|
|||||||
}
|
}
|
||||||
async post({ title, content, rule, beginAtDate, beginAtTime, duration, pids }) {
|
async post({ title, content, rule, beginAtDate, beginAtTime, duration, pids }) {
|
||||||
let beginAt, endAt;
|
let beginAt, endAt;
|
||||||
console.log(beginAtDate, beginAtTime);
|
|
||||||
try {
|
try {
|
||||||
beginAt = new Date(Date.parse(`${beginAtDate} ${beginAtTime.replace('-', ':')}`));
|
beginAt = new Date(Date.parse(`${beginAtDate} ${beginAtTime.replace('-', ':')}`));
|
||||||
console.log(beginAt, duration);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new ValidationError('beginAtDate', 'beginAtTime');
|
throw new ValidationError('beginAtDate', 'beginAtTime');
|
||||||
}
|
}
|
||||||
endAt = new Date(beginAt.getTime() + duration * 3600 * 1000);
|
endAt = new Date(beginAt.getTime() + duration * 3600 * 1000);
|
||||||
console.log(endAt);
|
|
||||||
if (beginAt >= endAt) throw new ValidationError('duration');
|
if (beginAt >= endAt) throw new ValidationError('duration');
|
||||||
await this.verifyProblems(pids);
|
await this.verifyProblems(pids);
|
||||||
let tid = await contest.add(title, content, this.user._id, rule, beginAt, endAt, pids);
|
let tid = await contest.add(title, content, this.user._id, rule, beginAt, endAt, pids);
|
||||||
|
|||||||
@@ -56,10 +56,12 @@ async function end(body) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class JudgeHandler extends Handler {
|
class JudgeHandler extends Handler {
|
||||||
async prepare(){
|
async prepare() {
|
||||||
this.checkPerm(PERM_JUDGE);
|
this.checkPerm(PERM_JUDGE);
|
||||||
}
|
}
|
||||||
async get() {
|
async get({ check = false }) {
|
||||||
|
this.response.body = {};
|
||||||
|
if (check) return;
|
||||||
let rid = await queue.get('judge', false);
|
let rid = await queue.get('judge', false);
|
||||||
if (rid) {
|
if (rid) {
|
||||||
let rdoc = await record.get(rid);
|
let rdoc = await record.get(rid);
|
||||||
@@ -74,7 +76,6 @@ class JudgeHandler extends Handler {
|
|||||||
};
|
};
|
||||||
this.response.body = { task };
|
this.response.body = { task };
|
||||||
}
|
}
|
||||||
else this.response.body = {};
|
|
||||||
}
|
}
|
||||||
async post_next() {
|
async post_next() {
|
||||||
await next(this.request.body);
|
await next(this.request.body);
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class ProblemDetailHandler extends ProblemHandler {
|
|||||||
this.response.template = 'problem_detail.html';
|
this.response.template = 'problem_detail.html';
|
||||||
this.uid = this.user._id;
|
this.uid = this.user._id;
|
||||||
this.pid = pid;
|
this.pid = pid;
|
||||||
if (pid) this.pdoc = await problem.get(this);
|
if (pid) this.pdoc = await problem.get(this.pid, this.uid);
|
||||||
if (this.pdoc.hidden && this.pdoc.owner != this.uid) this.checkPerm(PERM_VIEW_PROBLEM_HIDDEN);
|
if (this.pdoc.hidden && this.pdoc.owner != this.uid) this.checkPerm(PERM_VIEW_PROBLEM_HIDDEN);
|
||||||
if (this.pdoc) this.udoc = await user.getById(this.pdoc.owner);
|
if (this.pdoc) this.udoc = await user.getById(this.pdoc.owner);
|
||||||
this.response.body = {
|
this.response.body = {
|
||||||
@@ -115,7 +115,7 @@ class ProblemManageHandler extends ProblemDetailHandler {
|
|||||||
class ProblemSettingsHandler extends ProblemManageHandler {
|
class ProblemSettingsHandler extends ProblemManageHandler {
|
||||||
async get() {
|
async get() {
|
||||||
this.response.template = 'problem_settings.html';
|
this.response.template = 'problem_settings.html';
|
||||||
this.ctx.body.path = [
|
this.response.body.path = [
|
||||||
['Hydro', '/'],
|
['Hydro', '/'],
|
||||||
['problem_main', '/p'],
|
['problem_main', '/p'],
|
||||||
[this.pdoc.title, `/p/${this.pid}`, true],
|
[this.pdoc.title, `/p/${this.pid}`, true],
|
||||||
@@ -141,15 +141,14 @@ class ProblemEditHandler extends ProblemManageHandler {
|
|||||||
}
|
}
|
||||||
async post({ title, content }) {
|
async post({ title, content }) {
|
||||||
let pid = validator.checkPid(this.request.body.pid);
|
let pid = validator.checkPid(this.request.body.pid);
|
||||||
let pdoc = await problem.get({ pid: this.params.pid });
|
let pdoc = await problem.get(this.params.pid);
|
||||||
await problem.edit(pdoc._id, { title, content, pid });
|
await problem.edit(pdoc._id, { title, content, pid });
|
||||||
this.response.redirect = `/p/${pid}`;
|
this.response.redirect = `/p/${pid}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ProblemDataUploadHandler extends ProblemManageHandler {
|
class ProblemDataUploadHandler extends ProblemManageHandler {
|
||||||
constructor(ctx) {
|
async prepare(){
|
||||||
super(ctx);
|
|
||||||
this.response.template = 'problem_upload.html';
|
this.response.template = 'problem_upload.html';
|
||||||
}
|
}
|
||||||
async get() {
|
async get() {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class RecordListHandler extends Handler {
|
|||||||
let pdict = {}, udict = {};
|
let pdict = {}, udict = {};
|
||||||
for (let rdoc of rdocs) {
|
for (let rdoc of rdocs) {
|
||||||
udict[rdoc.uid] = await user.getById(rdoc.uid);
|
udict[rdoc.uid] = await user.getById(rdoc.uid);
|
||||||
pdict[rdoc.pid] = await problem.get({ pid: rdoc.pid, uid: this.user._id });
|
pdict[rdoc.pid] = await problem.get(rdoc.pid, this.user._id);
|
||||||
}
|
}
|
||||||
this.response.body = {
|
this.response.body = {
|
||||||
path: [
|
path: [
|
||||||
@@ -51,7 +51,7 @@ class RecordRejudgeHandler extends Handler {
|
|||||||
await record.reset(rid);
|
await record.reset(rid);
|
||||||
await queue.push('judge', rid);
|
await queue.push('judge', rid);
|
||||||
}
|
}
|
||||||
this.response.back();
|
this.back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class RecordConnectionHandler extends ConnectionHandler {
|
class RecordConnectionHandler extends ConnectionHandler {
|
||||||
@@ -70,7 +70,7 @@ class RecordConnectionHandler extends ConnectionHandler {
|
|||||||
async onRecordChange(data) {
|
async onRecordChange(data) {
|
||||||
let rdoc = data.value;
|
let rdoc = data.value;
|
||||||
if (rdoc.tid && rdoc.tid.toString() != this.tid) return;
|
if (rdoc.tid && rdoc.tid.toString() != this.tid) return;
|
||||||
let [udoc, pdoc] = await Promise.all([user.getById(rdoc.uid), problem.get({ pid: rdoc.pid })]);
|
let [udoc, pdoc] = await Promise.all([user.getById(rdoc.uid), problem.getById(rdoc.pid)]);
|
||||||
if (pdoc.hidden && !this.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN)) pdoc = null;
|
if (pdoc.hidden && !this.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN)) pdoc = null;
|
||||||
this.send({ html: await this.renderHTML('record_main_tr.html', { rdoc, udoc, pdoc }) });
|
this.send({ html: await this.renderHTML('record_main_tr.html', { rdoc, udoc, pdoc }) });
|
||||||
}
|
}
|
||||||
|
|||||||
+39
-19
@@ -1,8 +1,11 @@
|
|||||||
|
require('./utils');
|
||||||
const
|
const
|
||||||
Mongo = require('mongodb'),
|
Mongo = require('mongodb'),
|
||||||
|
{ defaults } = require('lodash'),
|
||||||
builtin = require('./model/builtin'),
|
builtin = require('./model/builtin'),
|
||||||
pwhash = require('./lib/pwhash'),
|
pwhash = require('./lib/pwhash'),
|
||||||
options = require('./options');
|
options = require('./options'),
|
||||||
|
{ udoc } = require('../interfaces');
|
||||||
|
|
||||||
async function run() {
|
async function run() {
|
||||||
let mongourl = 'mongodb://';
|
let mongourl = 'mongodb://';
|
||||||
@@ -13,30 +16,47 @@ async function run() {
|
|||||||
let coll_user = db.collection('user');
|
let coll_user = db.collection('user');
|
||||||
let coll_role = db.collection('role');
|
let coll_role = db.collection('role');
|
||||||
let coll_blacklist = db.collection('blacklist');
|
let coll_blacklist = db.collection('blacklist');
|
||||||
async function createRootUser() {
|
let coll_token = db.collection('token');
|
||||||
|
async function createUser() {
|
||||||
let salt = pwhash.salt();
|
let salt = pwhash.salt();
|
||||||
await coll_user.insertOne({
|
await coll_user.insertMany([
|
||||||
_id: -1,
|
defaults({
|
||||||
email: 'root@hydro',
|
_id: 0,
|
||||||
emailLower: 'root@hydro',
|
uname: 'Hydro',
|
||||||
uname: 'Root',
|
unameLower: 'hydro',
|
||||||
unameLower: 'root',
|
mail: 'hydro@hydro',
|
||||||
password: pwhash.hash('root', salt),
|
mailLower: 'hydro@hydro',
|
||||||
salt,
|
role: 'guest'
|
||||||
regat: new Date(),
|
}, udoc),
|
||||||
regip: '127.0.0.1',
|
defaults({
|
||||||
loginat: new Date(),
|
_id: 1,
|
||||||
loginip: '127.0.0.1',
|
mail: 'guest@hydro',
|
||||||
gravatar: 'root@hydro',
|
mailLower: 'guest@hydro',
|
||||||
role: 'admin'
|
uname: 'Guest',
|
||||||
});
|
unameLower: 'guest',
|
||||||
|
role: 'guest'
|
||||||
|
}, udoc),
|
||||||
|
defaults({
|
||||||
|
_id: -1,
|
||||||
|
mail: 'root@hydro',
|
||||||
|
mailLower: 'root@hydro',
|
||||||
|
uname: 'Root',
|
||||||
|
unameLower: 'root',
|
||||||
|
hash: pwhash.hash('rootroot', salt),
|
||||||
|
salt,
|
||||||
|
gravatar: 'root@hydro',
|
||||||
|
role: 'admin'
|
||||||
|
}, udoc)
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
await coll_user.createIndex('unameLower', { unique: true });
|
await coll_user.createIndex('unameLower', { unique: true });
|
||||||
await coll_user.createIndex('emailLower', { sparse: true });
|
await coll_user.createIndex('mailLower', { sparse: true });
|
||||||
await coll_role.insertMany(builtin.BUILTIN_ROLES);
|
await coll_role.insertMany(builtin.BUILTIN_ROLES);
|
||||||
await coll_user.insertMany(builtin.BUILTIN_USERS);
|
await coll_user.insertMany(builtin.BUILTIN_USERS);
|
||||||
await coll_blacklist.createIndex('expireAt', { expireAfterSeconds: 0 });
|
await coll_blacklist.createIndex('expireAt', { expireAfterSeconds: 0 });
|
||||||
await createRootUser();
|
await coll_token.createIndex([{ uid: 1 }, { tokenType: 1 }, { updateAt: -1 }], { sparse: true });
|
||||||
|
await coll_token.createIndex('expireAt', { expireAfterSeconds: 0 });
|
||||||
|
await createUser();
|
||||||
console.log('Installed');
|
console.log('Installed');
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
const { ObjectID } = require('bson');
|
||||||
|
exports.udoc = {
|
||||||
|
_id: 0,
|
||||||
|
mail: '',
|
||||||
|
mailLower: '',
|
||||||
|
uname: '',
|
||||||
|
unameLower: '',
|
||||||
|
salt: '',
|
||||||
|
hash: '',
|
||||||
|
hashType: 'hydro',
|
||||||
|
nAccept: 0,
|
||||||
|
nSubmit: 0,
|
||||||
|
nLike: 0,
|
||||||
|
bio: '',
|
||||||
|
gender: 0,
|
||||||
|
regat: new Date(),
|
||||||
|
regip: '0.0.0.0',
|
||||||
|
gravatar: '',
|
||||||
|
loginat: new Date(),
|
||||||
|
loginip: '0.0.0.0'
|
||||||
|
};
|
||||||
|
/*
|
||||||
|
export interface Pdoc {
|
||||||
|
_id: ObjectID
|
||||||
|
pid: string
|
||||||
|
owner: number
|
||||||
|
title: string
|
||||||
|
content: string
|
||||||
|
nSubmit: number
|
||||||
|
nAccept: number
|
||||||
|
tag: string[]
|
||||||
|
category: string[],
|
||||||
|
data: ObjectID | null
|
||||||
|
hidden: boolean
|
||||||
|
}
|
||||||
|
export interface TestCase {
|
||||||
|
time: number,
|
||||||
|
memory: number,
|
||||||
|
status: number,
|
||||||
|
message: string
|
||||||
|
}
|
||||||
|
export interface Rdoc {
|
||||||
|
_id: ObjectID,
|
||||||
|
pid: ObjectID,
|
||||||
|
owner: number,
|
||||||
|
lang: string,
|
||||||
|
code: string,
|
||||||
|
score: number,
|
||||||
|
memory: number,
|
||||||
|
time: number,
|
||||||
|
judgeTexts: string[],
|
||||||
|
compilerTexts: string[],
|
||||||
|
testCases: TestCase[],
|
||||||
|
rejudged: boolean,
|
||||||
|
judger: string,
|
||||||
|
judgeAt: Date,
|
||||||
|
status: number
|
||||||
|
}
|
||||||
|
export interface Bdoc {
|
||||||
|
_id: string,
|
||||||
|
expireAt: Date
|
||||||
|
}
|
||||||
|
export interface Tdoc {
|
||||||
|
_id: ObjectID
|
||||||
|
beginAt: Date
|
||||||
|
endAt: Date
|
||||||
|
attend: number
|
||||||
|
title: string
|
||||||
|
content: string
|
||||||
|
pids: ObjectID[]
|
||||||
|
}
|
||||||
|
*/
|
||||||
+42
-24
@@ -1,18 +1,33 @@
|
|||||||
export class User {
|
import { ObjectID } from 'bson';
|
||||||
|
|
||||||
|
export interface Udoc {
|
||||||
_id: number
|
_id: number
|
||||||
email: string
|
mail: string
|
||||||
emailLower: string
|
mailLower: string
|
||||||
uname: string
|
uname: string
|
||||||
unameLower: string
|
unameLower: string
|
||||||
salt: string
|
salt: string
|
||||||
hash: string
|
hash: string
|
||||||
displayName: string = ''
|
hashType: string
|
||||||
nAccept: number = 0
|
nAccept: number
|
||||||
nSubmit: number = 0
|
nSubmit: number
|
||||||
perm: string = '0'
|
nLike: number
|
||||||
constructor(user) { }
|
perm: string
|
||||||
hasPerm(perm) { }
|
hasPerm: Function
|
||||||
checkPassword(password) { }
|
checkPassword: Function
|
||||||
|
}
|
||||||
|
export interface Pdoc {
|
||||||
|
_id: ObjectID
|
||||||
|
pid: string
|
||||||
|
owner: number
|
||||||
|
title: string
|
||||||
|
content: string
|
||||||
|
nSubmit: number
|
||||||
|
nAccept: number
|
||||||
|
tag: string[]
|
||||||
|
category: string[],
|
||||||
|
data: ObjectID | null
|
||||||
|
hidden: boolean
|
||||||
}
|
}
|
||||||
export interface TestCase {
|
export interface TestCase {
|
||||||
time: number,
|
time: number,
|
||||||
@@ -20,10 +35,10 @@ export interface TestCase {
|
|||||||
status: number,
|
status: number,
|
||||||
message: string
|
message: string
|
||||||
}
|
}
|
||||||
export interface Record {
|
export interface Rdoc {
|
||||||
_id: import('bson').ObjectID,
|
_id: ObjectID,
|
||||||
pid: string | boolean,
|
pid: ObjectID,
|
||||||
creator: number,
|
owner: number,
|
||||||
lang: string,
|
lang: string,
|
||||||
code: string,
|
code: string,
|
||||||
score: number,
|
score: number,
|
||||||
@@ -32,18 +47,21 @@ export interface Record {
|
|||||||
judgeTexts: string[],
|
judgeTexts: string[],
|
||||||
compilerTexts: string[],
|
compilerTexts: string[],
|
||||||
testCases: TestCase[],
|
testCases: TestCase[],
|
||||||
|
rejudged: boolean,
|
||||||
judger: string,
|
judger: string,
|
||||||
judgeAt: Date,
|
judgeAt: Date,
|
||||||
status: number
|
status: number
|
||||||
}
|
}
|
||||||
export interface Problem {
|
export interface Bdoc {
|
||||||
_id: number | string,
|
_id: string,
|
||||||
title: string,
|
expireAt: Date
|
||||||
content: string,
|
}
|
||||||
timeLimit: number,
|
export interface Tdoc {
|
||||||
memoryLimit: number,
|
_id: ObjectID
|
||||||
nSubmit: number,
|
beginAt: Date
|
||||||
nAccept: number,
|
endAt: Date
|
||||||
tags: string[],
|
attend: number
|
||||||
categories: string[]
|
title: string
|
||||||
|
content: string
|
||||||
|
pids: ObjectID[]
|
||||||
}
|
}
|
||||||
@@ -2,11 +2,15 @@ const
|
|||||||
db = require('../service/db.js'),
|
db = require('../service/db.js'),
|
||||||
coll = db.collection('blacklist');
|
coll = db.collection('blacklist');
|
||||||
|
|
||||||
module.exports = {
|
async function add(ip) {
|
||||||
add(ip) {
|
let expireAt = new Date(new Date().getTime() + 365 * 24 * 60 * 60 * 1000);
|
||||||
let expireAt = new Date(new Date().getTime() + 365 * 24 * 60 * 60 * 1000);
|
return coll.findOneAndUpdate({ _id: ip }, { $set: { expireAt } }, { upsert: true });
|
||||||
return coll.findOneAndUpdate({ _id: ip }, { $set: { expireAt } }, { upsert: true });
|
}
|
||||||
},
|
function get(ip) {
|
||||||
get: ip => coll.findOne({ _id: ip }),
|
return coll.findOne({ _id: ip });
|
||||||
delete: ip => coll.deleteOne({ _id: ip })
|
}
|
||||||
};
|
function del(ip) {
|
||||||
|
return coll.deleteOne({ _id: ip });
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { add, get, del };
|
||||||
+1
-34
@@ -1,37 +1,4 @@
|
|||||||
const perm = require('../permission');
|
const perm = require('../permission');
|
||||||
const BUILTIN_USERS = [
|
|
||||||
{
|
|
||||||
_id: 0,
|
|
||||||
uname: 'Hydro',
|
|
||||||
unameLower: 'hydro',
|
|
||||||
email: '',
|
|
||||||
emailLower: '',
|
|
||||||
salt: '',
|
|
||||||
hash: '',
|
|
||||||
gender: 'other',
|
|
||||||
regat: new Date(),
|
|
||||||
regip: '127.0.0.1',
|
|
||||||
gravatar: '',
|
|
||||||
loginat: new Date(),
|
|
||||||
loginip: '127.0.0.1',
|
|
||||||
role: 'guest'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
_id: 1,
|
|
||||||
email: 'guest@hydro',
|
|
||||||
emailLower: 'guest@hydro',
|
|
||||||
uname: 'Guest',
|
|
||||||
unameLower: 'guest',
|
|
||||||
hash: '',
|
|
||||||
salt: '',
|
|
||||||
regat: new Date(),
|
|
||||||
regip: '127.0.0.1',
|
|
||||||
loginat: new Date(),
|
|
||||||
loginip: '127.0.0.1',
|
|
||||||
gravatar: 'guest@hydro',
|
|
||||||
role: 'guest'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
const BUILTIN_ROLES = [
|
const BUILTIN_ROLES = [
|
||||||
{ _id: 'guest', perm: perm.PERM_BASIC },
|
{ _id: 'guest', perm: perm.PERM_BASIC },
|
||||||
{ _id: 'default', perm: perm.PERM_DEFAULT },
|
{ _id: 'default', perm: perm.PERM_DEFAULT },
|
||||||
@@ -262,6 +229,6 @@ const STATUS_CODES = {
|
|||||||
30: 'ignored'
|
30: 'ignored'
|
||||||
};
|
};
|
||||||
module.exports = {
|
module.exports = {
|
||||||
BUILTIN_USERS, BUILTIN_ROLES, CATEGORIES, VIEW_LANGS, FOOTER_EXTRA_HTMLS, LANGS,
|
BUILTIN_ROLES, CATEGORIES, VIEW_LANGS, FOOTER_EXTRA_HTMLS, LANGS,
|
||||||
LANG_TEXTS, LANG_HIGHLIGHT_ID, STATUS, STATUS_TEXTS, STATUS_CODES
|
LANG_TEXTS, LANG_HIGHLIGHT_ID, STATUS, STATUS_TEXTS, STATUS_CODES
|
||||||
};
|
};
|
||||||
+25
-1
@@ -5,13 +5,28 @@ const
|
|||||||
coll = db.collection('contest'),
|
coll = db.collection('contest'),
|
||||||
coll_status = db.collection('contest.status');
|
coll_status = db.collection('contest.status');
|
||||||
|
|
||||||
|
|
||||||
const RULES = {
|
const RULES = {
|
||||||
homework: require('../module/contest/homework'),
|
homework: require('../module/contest/homework'),
|
||||||
oi: require('../module/contest/oi'),
|
oi: require('../module/contest/oi'),
|
||||||
acm: require('../module/contest/acm')
|
acm: require('../module/contest/acm')
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import('bson').ObjectID} ObjectID
|
||||||
|
* @typedef {import('../interface').Tdoc} Tdoc
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} title
|
||||||
|
* @param {string} content
|
||||||
|
* @param {number} owner
|
||||||
|
* @param {string} rule
|
||||||
|
* @param {Date} beginAt
|
||||||
|
* @param {Date} endAt
|
||||||
|
* @param {ObjectID[]} pids
|
||||||
|
* @param {object} data
|
||||||
|
* @returns {ObjectID} tid
|
||||||
|
*/
|
||||||
async function add(title, content, owner, rule,
|
async function add(title, content, owner, rule,
|
||||||
beginAt = new Date(), endAt = new Date(), pids = [], data = {}) {
|
beginAt = new Date(), endAt = new Date(), pids = [], data = {}) {
|
||||||
validator.checkTitle(title);
|
validator.checkTitle(title);
|
||||||
@@ -23,6 +38,11 @@ async function add(title, content, owner, rule,
|
|||||||
let res = await coll.insertOne(data);
|
let res = await coll.insertOne(data);
|
||||||
return res.insertedId;
|
return res.insertedId;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @param {ObjectID} tid
|
||||||
|
* @param {object} $set
|
||||||
|
* @returns {Tdoc} tdoc after modification
|
||||||
|
*/
|
||||||
async function edit(tid, $set) {
|
async function edit(tid, $set) {
|
||||||
if ($set.title) validator.checkTitle($set.title);
|
if ($set.title) validator.checkTitle($set.title);
|
||||||
if ($set.content) validator.checkIntro($set.content);
|
if ($set.content) validator.checkIntro($set.content);
|
||||||
@@ -36,6 +56,10 @@ async function edit(tid, $set) {
|
|||||||
await coll.findOneAndUpdate({ tid }, { $set });
|
await coll.findOneAndUpdate({ tid }, { $set });
|
||||||
return tdoc;
|
return tdoc;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @param {ObjectID} tid
|
||||||
|
* @returns {Tdoc}
|
||||||
|
*/
|
||||||
async function get(tid) {
|
async function get(tid) {
|
||||||
let tdoc = await coll.findOne({ _id: tid });
|
let tdoc = await coll.findOne({ _id: tid });
|
||||||
if (!tdoc) throw new ContestNotFoundError(tid);
|
if (!tdoc) throw new ContestNotFoundError(tid);
|
||||||
|
|||||||
+37
-4
@@ -6,6 +6,12 @@ const
|
|||||||
coll = db.collection('problem'),
|
coll = db.collection('problem'),
|
||||||
coll_status = db.collection('problem.status');
|
coll_status = db.collection('problem.status');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import('../interface').Pdoc} Pdoc
|
||||||
|
* @typedef {import('bson').ObjectID} ObjectID
|
||||||
|
* @typedef {import('mongodb').Cursor} Cursor
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} title
|
* @param {string} title
|
||||||
* @param {string} content
|
* @param {string} content
|
||||||
@@ -42,7 +48,12 @@ async function add({
|
|||||||
});
|
});
|
||||||
return pid;
|
return pid;
|
||||||
}
|
}
|
||||||
async function get({ pid, uid }) {
|
/**
|
||||||
|
* @param {string|ObjectID} pid
|
||||||
|
* @param {number} uid
|
||||||
|
* @returns {Pdoc}
|
||||||
|
*/
|
||||||
|
async function get(pid, uid = null) {
|
||||||
let query = {};
|
let query = {};
|
||||||
if (pid.generationTime || pid.length == 24) query = { _id: new ObjectID(pid) };
|
if (pid.generationTime || pid.length == 24) query = { _id: new ObjectID(pid) };
|
||||||
else query = { pid: parseInt(pid) || pid };
|
else query = { pid: parseInt(pid) || pid };
|
||||||
@@ -54,18 +65,36 @@ async function get({ pid, uid }) {
|
|||||||
}
|
}
|
||||||
return pdoc;
|
return pdoc;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @param {ObjectID} pid
|
||||||
|
* @returns {Pdoc}
|
||||||
|
*/
|
||||||
async function getById(_id) {
|
async function getById(_id) {
|
||||||
_id = new ObjectID(_id);
|
_id = new ObjectID(_id);
|
||||||
let pdoc = await coll.findOne({ _id });
|
let pdoc = await coll.findOne({ _id });
|
||||||
if (!pdoc) throw new ProblemNotFoundError(_id);
|
if (!pdoc) throw new ProblemNotFoundError(_id);
|
||||||
return pdoc;
|
return pdoc;
|
||||||
}
|
}
|
||||||
async function getMany(query, sort, page, limit) {
|
/**
|
||||||
return await coll.find(query).sort(sort).skip((page - 1) * limit).limit(limit).toArray();
|
* @param {string|ObjectID} pid
|
||||||
|
* @param {number} uid
|
||||||
|
* @returns {Pdoc[]}
|
||||||
|
*/
|
||||||
|
function getMany(query, sort, page, limit) {
|
||||||
|
return coll.find(query).sort(sort).skip((page - 1) * limit).limit(limit).toArray();
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @param {object} query
|
||||||
|
* @returns {Cursor}
|
||||||
|
*/
|
||||||
function getMulti(query) {
|
function getMulti(query) {
|
||||||
return coll.find(query);
|
return coll.find(query);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @param {ObjectID} _id
|
||||||
|
* @param {object} query
|
||||||
|
* @returns {Pdoc}
|
||||||
|
*/
|
||||||
async function edit(_id, $set) {
|
async function edit(_id, $set) {
|
||||||
if ($set.title) validator.checkTitle($set.title);
|
if ($set.title) validator.checkTitle($set.title);
|
||||||
if ($set.content) validator.checkContent($set.content);
|
if ($set.content) validator.checkContent($set.content);
|
||||||
@@ -74,6 +103,10 @@ async function edit(_id, $set) {
|
|||||||
if (!pdoc) throw new ProblemNotFoundError(_id);
|
if (!pdoc) throw new ProblemNotFoundError(_id);
|
||||||
return pdoc;
|
return pdoc;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @param {object} query
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
async function count(query) {
|
async function count(query) {
|
||||||
return await coll.find(query).count();
|
return await coll.find(query).count();
|
||||||
}
|
}
|
||||||
@@ -87,7 +120,7 @@ async function random(query) {
|
|||||||
}
|
}
|
||||||
async function getList(pids) {
|
async function getList(pids) {
|
||||||
let r = {};
|
let r = {};
|
||||||
for (let pid of pids) r[pid] = await get({pid});
|
for (let pid of pids) r[pid] = await get({ pid });
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,8 @@ async function reset(rid) {
|
|||||||
judgeTexts: [],
|
judgeTexts: [],
|
||||||
compilerTexts: [],
|
compilerTexts: [],
|
||||||
judgeAt: null,
|
judgeAt: null,
|
||||||
judger: null
|
judger: null,
|
||||||
|
rejudged: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
async function count(query) {
|
async function count(query) {
|
||||||
@@ -66,6 +67,9 @@ async function getList(rids) {
|
|||||||
for (let rid of rids) r[rid] = await get(rid);
|
for (let rid of rids) r[rid] = await get(rid);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
function getUserInProblemMulti(uid, pid) {
|
||||||
|
return coll.find({ owner: uid, pid });
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
add,
|
add,
|
||||||
@@ -74,5 +78,6 @@ module.exports = {
|
|||||||
update,
|
update,
|
||||||
count,
|
count,
|
||||||
reset,
|
reset,
|
||||||
getList
|
getList,
|
||||||
|
getUserInProblemMulti
|
||||||
};
|
};
|
||||||
@@ -67,9 +67,5 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
async getMostRecentSessionByUid(uid) {
|
async getMostRecentSessionByUid(uid) {
|
||||||
return await coll.findOne({ uid, token_type: this.TYPE_SESSION }, { sort: { updateAt: -1 } });
|
return await coll.findOne({ uid, token_type: this.TYPE_SESSION }, { sort: { updateAt: -1 } });
|
||||||
},
|
}
|
||||||
init: () => Promise.all([
|
|
||||||
coll.createIndex([{ uid: 1 }, { tokenType: 1 }, { updateAt: -1 }], { sparse: true }),
|
|
||||||
coll.createIndex('expireAt', { expireAfterSeconds: 0 })
|
|
||||||
])
|
|
||||||
};
|
};
|
||||||
+13
-13
@@ -10,7 +10,7 @@ const
|
|||||||
class USER {
|
class USER {
|
||||||
constructor(user) {
|
constructor(user) {
|
||||||
this._id = user._id;
|
this._id = user._id;
|
||||||
this.email = user.email;
|
this.mail = user.mail;
|
||||||
this.uname = user.uname;
|
this.uname = user.uname;
|
||||||
this.salt = user.salt;
|
this.salt = user.salt;
|
||||||
this.hash = user.hash;
|
this.hash = user.hash;
|
||||||
@@ -52,12 +52,12 @@ async function getByUname(uname) {
|
|||||||
udoc.perm = role.perm;
|
udoc.perm = role.perm;
|
||||||
return new USER(udoc);
|
return new USER(udoc);
|
||||||
}
|
}
|
||||||
async function getByEmail(email, ignoreMissing = false) {
|
async function getByEmail(mail, ignoreMissing = false) {
|
||||||
let emailLower = email.trim().toLowerCase();
|
let mailLower = mail.trim().toLowerCase();
|
||||||
let udoc = await coll.findOne({ emailLower });
|
let udoc = await coll.findOne({ mailLower });
|
||||||
if (!udoc) {
|
if (!udoc) {
|
||||||
if (ignoreMissing) return null;
|
if (ignoreMissing) return null;
|
||||||
else throw new UserNotFoundError(email);
|
else throw new UserNotFoundError(mail);
|
||||||
}
|
}
|
||||||
let role = await coll_role.findOne({ _id: udoc.role || 'default' });
|
let role = await coll_role.findOne({ _id: udoc.role || 'default' });
|
||||||
udoc.perm = role.perm;
|
udoc.perm = role.perm;
|
||||||
@@ -70,9 +70,9 @@ async function setPassword(uid, password) {
|
|||||||
$set: { salt, hash: pwhash.hash(password, salt) }
|
$set: { salt, hash: pwhash.hash(password, salt) }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
async function setEmail(uid, email) {
|
async function setEmail(uid, mail) {
|
||||||
validator.checkEmail(email);
|
validator.checkEmail(mail);
|
||||||
return await setById(uid, { email, emailLower: email.trim().toLowerCase() });
|
return await setById(uid, { mail, mailLower: mail.trim().toLowerCase() });
|
||||||
}
|
}
|
||||||
function setById(uid, args) {
|
function setById(uid, args) {
|
||||||
coll.findOneAndUpdate({ _id: uid }, { $set: args });
|
coll.findOneAndUpdate({ _id: uid }, { $set: args });
|
||||||
@@ -88,17 +88,17 @@ async function changePassword(uid, currentPassword, newPassword) {
|
|||||||
$set: { salt, hash: pwhash.hash(newPassword, salt) }
|
$set: { salt, hash: pwhash.hash(newPassword, salt) }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
async function create({ uid, email, uname, password, regip = '127.0.0.1', role = 'default' }) {
|
async function create({ uid, mail, uname, password, regip = '127.0.0.1', role = 'default' }) {
|
||||||
validator.checkUname(uname);
|
validator.checkUname(uname);
|
||||||
validator.checkPassword(password);
|
validator.checkPassword(password);
|
||||||
validator.checkEmail(email);
|
validator.checkEmail(mail);
|
||||||
let salt = pwhash.salt();
|
let salt = pwhash.salt();
|
||||||
if (!uid) uid = system.incUserCounter();
|
if (!uid) uid = system.incUserCounter();
|
||||||
try {
|
try {
|
||||||
await coll.insertOne({
|
await coll.insertOne({
|
||||||
_id: uid,
|
_id: uid,
|
||||||
email,
|
mail,
|
||||||
emailLower: email.trim().toLowerCase(),
|
mailLower: mail.trim().toLowerCase(),
|
||||||
uname,
|
uname,
|
||||||
unameLower: uname.trim().toLowerCase(),
|
unameLower: uname.trim().toLowerCase(),
|
||||||
password: pwhash.hash(password, salt),
|
password: pwhash.hash(password, salt),
|
||||||
@@ -108,7 +108,7 @@ async function create({ uid, email, uname, password, regip = '127.0.0.1', role =
|
|||||||
loginat: new Date(),
|
loginat: new Date(),
|
||||||
loginip: regip,
|
loginip: regip,
|
||||||
role,
|
role,
|
||||||
gravatar: email
|
gravatar: mail
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new UserAlreadyExistError([uid, uname, email]);
|
throw new UserAlreadyExistError([uid, uname, email]);
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
const
|
const
|
||||||
yaml = require('js-yaml'),
|
yaml = require('js-yaml'),
|
||||||
{ defaults } = require('lodash'),
|
{ defaultsDeep } = require('lodash'),
|
||||||
fs = require('fs'),
|
fs = require('fs'),
|
||||||
path = require('path');
|
path = require('path');
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ let options = {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
let t = yaml.safeLoad(fs.readFileSync(path.resolve(process.cwd(), 'config.yaml')));
|
let t = yaml.safeLoad(fs.readFileSync(path.resolve(process.cwd(), 'config.yaml')));
|
||||||
options = defaults(t, options);
|
options = defaultsDeep(t, options);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Cannot load config');
|
console.error('Cannot load config');
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-11
@@ -13,7 +13,8 @@ const
|
|||||||
blacklist = require('../model/blacklist'),
|
blacklist = require('../model/blacklist'),
|
||||||
token = require('../model/token'),
|
token = require('../model/token'),
|
||||||
opcount = require('../model/opcount'),
|
opcount = require('../model/opcount'),
|
||||||
{ UserNotFoundError, BlacklistedError, PermissionError } = require('../error');
|
{ UserNotFoundError, BlacklistedError, PermissionError,
|
||||||
|
NotFoundError } = require('../error');
|
||||||
|
|
||||||
const options = require('../options');
|
const options = require('../options');
|
||||||
let http = options.listen.https ? require('https') : require('http');
|
let http = options.listen.https ? require('https') : require('http');
|
||||||
@@ -49,7 +50,7 @@ class Handler {
|
|||||||
this.response = {
|
this.response = {
|
||||||
body: '',
|
body: '',
|
||||||
type: '',
|
type: '',
|
||||||
status: 404,
|
status: null,
|
||||||
template: null,
|
template: null,
|
||||||
redirect: null,
|
redirect: null,
|
||||||
attachment: name => ctx.attachment(name)
|
attachment: name => ctx.attachment(name)
|
||||||
@@ -102,9 +103,10 @@ class Handler {
|
|||||||
await opcount.inc(op, this.request.ip, period_secs, max_operations);
|
await opcount.inc(op, this.request.ip, period_secs, max_operations);
|
||||||
}
|
}
|
||||||
back() {
|
back() {
|
||||||
this.ctx.redirect(this.request.headers.referer || '/');
|
this.response.redirect = this.request.headers.referer || '/';
|
||||||
}
|
}
|
||||||
async ___prepare() {
|
async ___prepare() {
|
||||||
|
this.now = new Date();
|
||||||
this._handler.sid = this.request.cookies.get('sid');
|
this._handler.sid = this.request.cookies.get('sid');
|
||||||
this._handler.save = this.request.cookies.get('save');
|
this._handler.save = this.request.cookies.get('save');
|
||||||
this._handler.tokenType = token.TYPE_SESSION;
|
this._handler.tokenType = token.TYPE_SESSION;
|
||||||
@@ -119,9 +121,8 @@ class Handler {
|
|||||||
let bdoc = await blacklist.get(this.request.ip);
|
let bdoc = await blacklist.get(this.request.ip);
|
||||||
if (bdoc) throw new BlacklistedError(this.request.ip);
|
if (bdoc) throw new BlacklistedError(this.request.ip);
|
||||||
this.user = await user.getById(this.session.uid);
|
this.user = await user.getById(this.session.uid);
|
||||||
console.log(this.user, this.session.uid);
|
|
||||||
if (!this.user) throw new UserNotFoundError(this.session.uid);
|
if (!this.user) throw new UserNotFoundError(this.session.uid);
|
||||||
this.csrf_token = (await token.add(token.TYPE_CSRF_TOKEN, 600, this.request.path))[0];
|
this.csrf_token = (await token.add(token.TYPE_CSRF_TOKEN, 600, { path: this.request.path }))[0];
|
||||||
this.preferJson = (this.request.headers['accept'] || '').includes('application/json');
|
this.preferJson = (this.request.headers['accept'] || '').includes('application/json');
|
||||||
}
|
}
|
||||||
async ___cleanup() {
|
async ___cleanup() {
|
||||||
@@ -135,6 +136,10 @@ class Handler {
|
|||||||
await this.saveCookie();
|
await this.saveCookie();
|
||||||
}
|
}
|
||||||
async renderBody() {
|
async renderBody() {
|
||||||
|
if (!(this.response.body || this.response.template)) {
|
||||||
|
this.response.body = { error: new NotFoundError() };
|
||||||
|
this.response.template = 'error.html';
|
||||||
|
}
|
||||||
if (!this.preferJson)
|
if (!this.preferJson)
|
||||||
if (this.response.body || this.response.template) {
|
if (this.response.body || this.response.template) {
|
||||||
if (this.request.query.noTemplate || this.preferJson) return;
|
if (this.request.query.noTemplate || this.preferJson) return;
|
||||||
@@ -150,9 +155,11 @@ class Handler {
|
|||||||
this.ctx.response.status = 302;
|
this.ctx.response.status = 302;
|
||||||
this.ctx.redirect(this.response.redirect);
|
this.ctx.redirect(this.response.redirect);
|
||||||
} else {
|
} else {
|
||||||
if (this.response.body) this.ctx.body = this.response.body;
|
if (this.response.body != null) {
|
||||||
|
this.ctx.response.body = this.response.body;
|
||||||
|
this.ctx.response.status = this.response.status || 200;
|
||||||
|
}
|
||||||
if (this.response.type) this.ctx.response.type = this.response.type;
|
if (this.response.type) this.ctx.response.type = this.response.type;
|
||||||
if (this.response.status) this.ctx.response.status = this.response.status;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async saveCookie() {
|
async saveCookie() {
|
||||||
@@ -210,7 +217,6 @@ function Route(route, handler) {
|
|||||||
if (h[`_${method}`]) await h[`_${method}`](args);
|
if (h[`_${method}`]) await h[`_${method}`](args);
|
||||||
if (h[method]) await h[method](args);
|
if (h[method]) await h[method](args);
|
||||||
|
|
||||||
console.log(ctx.request.body);
|
|
||||||
if (method == 'post' && ctx.request.body.operation) {
|
if (method == 'post' && ctx.request.body.operation) {
|
||||||
if (h[`${method}_${ctx.request.body.operation}`])
|
if (h[`${method}_${ctx.request.body.operation}`])
|
||||||
await h[`${method}_${ctx.request.body.operation}`](args);
|
await h[`${method}_${ctx.request.body.operation}`](args);
|
||||||
@@ -231,20 +237,22 @@ class ConnectionHandler {
|
|||||||
* @param {import('sockjs').Connection} conn
|
* @param {import('sockjs').Connection} conn
|
||||||
*/
|
*/
|
||||||
constructor(conn) {
|
constructor(conn) {
|
||||||
|
let that = this;
|
||||||
this.conn = conn;
|
this.conn = conn;
|
||||||
this.request = {
|
this.request = {
|
||||||
cookies: {
|
cookies: {
|
||||||
get(name) {
|
get(name) {
|
||||||
return conn.cookies[name];
|
return that.request.cookies[name];
|
||||||
},
|
},
|
||||||
set() { }
|
set() { }
|
||||||
},
|
},
|
||||||
params: {},
|
params: {},
|
||||||
headers: conn.headers
|
headers: conn.headers
|
||||||
};
|
};
|
||||||
|
this._handler = {};
|
||||||
let p = (conn.url.split('?')[1] || '').split('&');
|
let p = (conn.url.split('?')[1] || '').split('&');
|
||||||
for (let i in p) p[i] = p[i].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]);
|
for (let i in p) this.request.params[p[i][0]] = decodeURIComponent(p[i][1]);
|
||||||
}
|
}
|
||||||
renderHTML(name, context) {
|
renderHTML(name, context) {
|
||||||
console.time(name);
|
console.time(name);
|
||||||
@@ -292,7 +300,7 @@ function Connection(prefix, handler) {
|
|||||||
sock.on('connection', async conn => {
|
sock.on('connection', async conn => {
|
||||||
let h = new handler(conn);
|
let h = new handler(conn);
|
||||||
try {
|
try {
|
||||||
let args = Object.assign({}, h.conn.params);
|
let args = Object.assign({}, h.request.params);
|
||||||
|
|
||||||
if (args.uid) args.uid = parseInt(validator.checkUid(args.uid));
|
if (args.uid) args.uid = parseInt(validator.checkUid(args.uid));
|
||||||
if (args.page) args.page = parseInt(args.page);
|
if (args.page) args.page = parseInt(args.page);
|
||||||
@@ -313,6 +321,7 @@ function Connection(prefix, handler) {
|
|||||||
if (h.___cleanup) await h.___cleanup(args);
|
if (h.___cleanup) await h.___cleanup(args);
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
if (h.onerror) await h.onerror(e);
|
if (h.onerror) await h.onerror(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,9 +4,7 @@
|
|||||||
{%- if tdoc is none %}
|
{%- if tdoc is none %}
|
||||||
href="/p/{{ pdoc.pid }}"
|
href="/p/{{ pdoc.pid }}"
|
||||||
{%- elif tdoc['doc_type'] == vj4.model.document.TYPE_CONTEST %}
|
{%- elif tdoc['doc_type'] == vj4.model.document.TYPE_CONTEST %}
|
||||||
href="{{ reverse_url('contest_detail_problem', domain_id=pdoc['domain_id'], pid=pdoc['_id'], tid=tdoc['doc_id']) }}"
|
href="/t/{{ tdoc._id }}/{{ pdoc._id }}"
|
||||||
{%- elif tdoc['doc_type'] == vj4.model.document.TYPE_HOMEWORK %}
|
|
||||||
href="{{ reverse_url('homework_detail_problem', domain_id=pdoc['domain_id'], pid=pdoc['_id'], tid=tdoc['doc_id']) }}"
|
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
>
|
>
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
{% macro render_status_td(rdoc, rid_key='_id') %}
|
{% macro render_status_td(rdoc, rid_key='_id') %}
|
||||||
<td class="col--status record-status--border {{ builtin.STATUS_CODES[rdoc['status']] }}">
|
<td class="col--status record-status--border {{ model.builtin.STATUS_CODES[rdoc['status']] }}">
|
||||||
<div class="col--status__text">
|
<div class="col--status__text">
|
||||||
<span class="icon record-status--icon {{ builtin.STATUS_CODES[rdoc['status']] }}"></span>
|
<span class="icon record-status--icon {{ model.builtin.STATUS_CODES[rdoc['status']] }}"></span>
|
||||||
<a href="/r/{{ rdoc._id }}" class="record-status--text {{ builtin.STATUS_CODES[rdoc['status']] }}">
|
<a href="/r/{{ rdoc._id }}" class="record-status--text {{ model.builtin.STATUS_CODES[rdoc['status']] }}">
|
||||||
{{ builtin.STATUS_TEXTS[rdoc['status']] }}
|
{{ model.builtin.STATUS_TEXTS[rdoc['status']] }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{% if rdoc['status'] == status.STATUS_JUDGING %}
|
{% if rdoc['status'] == status.STATUS_JUDGING %}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
{{ udoc['uname'] }}
|
{{ udoc['uname'] }}
|
||||||
</a>
|
</a>
|
||||||
{% if badge %}
|
{% if badge %}
|
||||||
<a class="user-profile-badge v-center badge--lv{{ udoc['level']|default(0) }}" href="/u/{{ udoc['_id']|default(0) }}" title="LV {{ udoc['level']|default(0) }}: Top {{ builtin.LEVELS[udoc['level']|default(0)]|default('N/A') }}%">LV {{ udoc['level']|default(0) }}</a>
|
<a class="user-profile-badge v-center badge--lv{{ udoc['level']|default(0) }}" href="/u/{{ udoc['_id']|default(0) }}" title="LV {{ udoc['level']|default(0) }}: Top {{ model.builtin.LEVELS[udoc['level']|default(0)]|default('N/A') }}%">LV {{ udoc['level']|default(0) }}</a>
|
||||||
{% if modbadge and handler.hasPerm(perm.PERM_MOD_BADGE) %}
|
{% if modbadge and handler.hasPerm(perm.PERM_MOD_BADGE) %}
|
||||||
<span class="user-profile-badge v-center badge--mod" title="Moderator">MOD</span>
|
<span class="user-profile-badge v-center badge--mod" title="Moderator">MOD</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
{%- for column in rows[0] -%}
|
{%- for column in rows[0] -%}
|
||||||
<th class="col--{{ column['type'] }}">
|
<th class="col--{{ column['type'] }}">
|
||||||
{% if column.type == 'problem_detail' %}
|
{% if column.type == 'problem_detail' %}
|
||||||
<a href="{{ reverse_url('contest_detail_problem' , tid=tdoc._id, pid=column.raw._id) }}" data-tooltip="{{ column['raw']['title'] }}">{{ column['value'] }}</a>
|
<a href="/c/{{ tdoc._id }}/p/{{ column.raw._id }}" data-tooltip="{{ column['raw']['title'] }}">{{ column['value'] }}</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ column.value }}
|
{{ column.value }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@
|
|||||||
<li class="footer__extra-link-item" data-dropdown-target="#menu-footer-lang">
|
<li class="footer__extra-link-item" data-dropdown-target="#menu-footer-lang">
|
||||||
<span><span class="icon icon-global"></span> Language <span class="icon icon-expand_less"></span></span>
|
<span><span class="icon icon-global"></span> Language <span class="icon icon-expand_less"></span></span>
|
||||||
<ol class="dropdown-target menu" id="menu-footer-lang">
|
<ol class="dropdown-target menu" id="menu-footer-lang">
|
||||||
{% for item in builtin.VIEW_LANGS %}
|
{% for item in model.builtin.VIEW_LANGS %}
|
||||||
<li class="menu__item"><a class="menu__link" href="/lang/{{ item.code }}">{{ item.name }}</a></li>
|
<li class="menu__item"><a class="menu__link" href="/lang/{{ item.code }}">{{ item.name }}</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ol>
|
</ol>
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="footer__extra-right">
|
<div class="footer__extra-right">
|
||||||
<ol class="clearfix">
|
<ol class="clearfix">
|
||||||
{% for html in builtin.FOOTER_EXTRA_HTMLS %}
|
{% for html in model.builtin.FOOTER_EXTRA_HTMLS %}
|
||||||
<li class="footer__extra-link-item">{{ html|safe }}</li>
|
<li class="footer__extra-link-item">{{ html|safe }}</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ol>
|
</ol>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
<span class="icon icon-flag"></span> {{ _('View Problem') }}
|
<span class="icon icon-flag"></span> {{ _('View Problem') }}
|
||||||
</a></li>
|
</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<li class="menu__item scratchpad--hide"><a class="menu__link{% if page_name == 'contest_detail_problem_submit' %} active{% endif %}" href="{{ reverse_url('contest_detail_problem_submit', tid=tdoc['doc_id'], pid=pdoc['_id']) }}">
|
<li class="menu__item scratchpad--hide"><a class="menu__link{% if page_name == 'contest_detail_problem_submit' %} active{% endif %}" href="/t/{{ tdoc._id }}/p/{{ pdoc._id }}">
|
||||||
<span class="icon icon-send"></span> {{ _('Submit') }}
|
<span class="icon icon-send"></span> {{ _('Submit') }}
|
||||||
</a></li>
|
</a></li>
|
||||||
{% elif handler.is_done(tdoc) %}
|
{% elif handler.is_done(tdoc) %}
|
||||||
|
|||||||
@@ -98,9 +98,9 @@
|
|||||||
<dt>{{ _('Submission') }}</dt>
|
<dt>{{ _('Submission') }}</dt>
|
||||||
<dd>
|
<dd>
|
||||||
{% if pdoc['psdoc']['status'] %}
|
{% if pdoc['psdoc']['status'] %}
|
||||||
<span class="icon record-status--icon {{ builtin.STATUS_CODES[pdoc['psdoc']['status']] }}"></span>
|
<span class="icon record-status--icon {{ model.builtin.STATUS_CODES[pdoc['psdoc']['status']] }}"></span>
|
||||||
<a href="/r/{{ pdoc['psdoc']['rid'] }}" class="record-status--text {{ builtin.STATUS_CODES[pdoc['psdoc']['status']] }}">
|
<a href="/r/{{ pdoc['psdoc']['rid'] }}" class="record-status--text {{ model.builtin.STATUS_CODES[pdoc['psdoc']['status']] }}">
|
||||||
{{ builtin.STATUS_TEXTS[pdoc['psdoc']['status']] }}
|
{{ model.builtin.STATUS_TEXTS[pdoc['psdoc']['status']] }}
|
||||||
</a>
|
</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ _('(None)') }}
|
{{ _('(None)') }}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="section__body">
|
<div class="section__body">
|
||||||
<ul class="group-list" data-widget-cf-container>
|
<ul class="group-list" data-widget-cf-container>
|
||||||
{% for category, sub_categories in builtin.CATEGORIES %}
|
{% for category, sub_categories in model.builtin.CATEGORIES %}
|
||||||
<li class="group-list__item">
|
<li class="group-list__item">
|
||||||
<h2 class="section__title">
|
<h2 class="section__title">
|
||||||
<a href="/p?category={{ category|urlencode }}">{{ category }}</a>
|
<a href="/p?category={{ category|urlencode }}">{{ category }}</a>
|
||||||
|
|||||||
@@ -55,7 +55,7 @@
|
|||||||
<h1 class="section__title">{{ _('Categories') }} ({{ _('click to add') }})</h1>
|
<h1 class="section__title">{{ _('Categories') }} ({{ _('click to add') }})</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="section__body">
|
<div class="section__body">
|
||||||
{% for category, sub_categories in builtin.CATEGORIES %}
|
{% for category, sub_categories in model.builtin.CATEGORIES %}
|
||||||
<div>
|
<div>
|
||||||
<h2 class="section__title"><a class="category-a" href="javascript:;" data-category="{{ category }}">{{ category }}</a></h2>
|
<h2 class="section__title"><a class="category-a" href="javascript:;" data-category="{{ category }}">{{ category }}</a></h2>
|
||||||
<ol>
|
<ol>
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="medium-9 columns">
|
<div class="medium-9 columns">
|
||||||
{% if not tdoc or rdocs %}
|
{% if not tdoc or rdocs.length %}
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<div class="section__header">
|
<div class="section__header">
|
||||||
<h1 class="section__title">{{ _('Recent 10 Records') }}</h1>
|
<h1 class="section__title">{{ _('Recent 10 Records') }}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="section__body no-padding">
|
<div class="section__body no-padding">
|
||||||
{% if not rdocs %}
|
{% if not rdocs.length %}
|
||||||
{{ nothing.render('You have not submitted any solutions for this problem') }}
|
{{ nothing.render('You have not submitted any solutions for this problem') }}
|
||||||
{% else %}
|
{% else %}
|
||||||
<table class="data-table">
|
<table class="data-table">
|
||||||
@@ -44,7 +44,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="section__body">
|
<div class="section__body">
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{{ form.form_select({label:'Code language', options:builtin.LANG_TEXTS, name:'lang', value:handler.user.codeLang}) }}
|
{{ form.form_select({
|
||||||
|
label:'Code language',
|
||||||
|
options:model.builtin.LANG_TEXTS,
|
||||||
|
name:'lang',
|
||||||
|
value:handler.user.codeLang
|
||||||
|
}) }}
|
||||||
{{ form.form_textarea({label:'Code', columns:12, name:'code', extra_class:'monospace', autofocus:true}) }}
|
{{ form.form_textarea({label:'Code', columns:12, name:'code', extra_class:'monospace', autofocus:true}) }}
|
||||||
<div class="row"><div class="columns">
|
<div class="row"><div class="columns">
|
||||||
<input type="hidden" name="csrf_token" value="{{ handler.csrf_token }}">
|
<input type="hidden" name="csrf_token" value="{{ handler.csrf_token }}">
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
<h1 class="section__title">{{ _('Code') }}</h1>
|
<h1 class="section__title">{{ _('Code') }}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="section__body" data-syntax-hl-show-line-number>
|
<div class="section__body" data-syntax-hl-show-line-number>
|
||||||
<pre><code class="language-{{ builtin.LANG_HIGHLIGHT_ID[rdoc['lang']] }}">{{ rdoc['code'] }}</code></pre>
|
<pre><code class="language-{{ model.builtin.LANG_HIGHLIGHT_ID[rdoc['lang']] }}">{{ rdoc['code'] }}</code></pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -82,7 +82,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<dt>{{ _('Language') }}</dt>
|
<dt>{{ _('Language') }}</dt>
|
||||||
<dd>
|
<dd>
|
||||||
{{ builtin.LANG_TEXTS[rdoc['lang']] }}
|
{{ model.builtin.LANG_TEXTS[rdoc['lang']] }}
|
||||||
</dd>
|
</dd>
|
||||||
<dt>
|
<dt>
|
||||||
{{ _('Submit At') }}
|
{{ _('Submit At') }}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<div class="section visible" id="status">
|
<div class="section visible" id="status">
|
||||||
<div class="section__header">
|
<div class="section__header">
|
||||||
<h1 class="section__title">
|
<h1 class="section__title">
|
||||||
<span class="icon record-status--icon {{ builtin.STATUS_CODES[rdoc['status']] }}"></span>
|
<span class="icon record-status--icon {{ model.builtin.STATUS_CODES[rdoc['status']] }}"></span>
|
||||||
<span class="record-status--text {{ builtin.STATUS_CODES[rdoc['status']] }}">
|
<span class="record-status--text {{ model.builtin.STATUS_CODES[rdoc['status']] }}">
|
||||||
{{ builtin.STATUS_TEXTS[rdoc['status']] }}
|
{{ model.builtin.STATUS_TEXTS[rdoc['status']] }}
|
||||||
</span>
|
</span>
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
@@ -37,13 +37,13 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for rcdoc in rdoc.cases %}
|
{% for rcdoc in rdoc.cases %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="col--case record-status--border {{ builtin.STATUS_CODES[rcdoc['status']] }}">
|
<td class="col--case record-status--border {{ model.builtin.STATUS_CODES[rcdoc['status']] }}">
|
||||||
#{{ loop.index }}
|
#{{ loop.index }}
|
||||||
</td>
|
</td>
|
||||||
<td class="col--status">
|
<td class="col--status">
|
||||||
<span class="icon record-status--icon {{ builtin.STATUS_CODES[rcdoc['status']] }}"></span>
|
<span class="icon record-status--icon {{ model.builtin.STATUS_CODES[rcdoc['status']] }}"></span>
|
||||||
<span class="record-status--text {{ builtin.STATUS_CODES[rcdoc['status']] }}">
|
<span class="record-status--text {{ model.builtin.STATUS_CODES[rcdoc['status']] }}">
|
||||||
{{ builtin.STATUS_TEXTS[rcdoc['status']] }}
|
{{ model.builtin.STATUS_TEXTS[rcdoc['status']] }}
|
||||||
</span>
|
</span>
|
||||||
<span>{{ rcdoc['judgeText'] }}</span>
|
<span>{{ rcdoc['judgeText'] }}</span>
|
||||||
<a class="tool-button" href="/wiki/help#status"><span class="icon icon-help"></span></a>
|
<a class="tool-button" href="/wiki/help#status"><span class="icon icon-help"></span></a>
|
||||||
|
|||||||
@@ -25,6 +25,6 @@
|
|||||||
</td>
|
</td>
|
||||||
<td class="col--time">{% if rdoc['status'] == status.STATUS_TIME_LIMIT_EXCEEDED or rdoc['status'] == status.STATUS_MEMORY_LIMIT_EXCEEDED or rdoc['status'] == status.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ rdoc.time }}ms</td>
|
<td class="col--time">{% if rdoc['status'] == status.STATUS_TIME_LIMIT_EXCEEDED or rdoc['status'] == status.STATUS_MEMORY_LIMIT_EXCEEDED or rdoc['status'] == status.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ rdoc.time }}ms</td>
|
||||||
<td class="col--memory">{% if rdoc['status'] == status.STATUS_TIME_LIMIT_EXCEEDED or rdoc['status'] == status.STATUS_MEMORY_LIMIT_EXCEEDED or rdoc['status'] == status.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ rdoc.memory|format_size(1024) }}</td>
|
<td class="col--memory">{% if rdoc['status'] == status.STATUS_TIME_LIMIT_EXCEEDED or rdoc['status'] == status.STATUS_MEMORY_LIMIT_EXCEEDED or rdoc['status'] == status.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ rdoc.memory|format_size(1024) }}</td>
|
||||||
<td class="col--lang">{{ builtin.LANG_TEXTS[rdoc['lang']] }}</td>
|
<td class="col--lang">{{ model.builtin.LANG_TEXTS[rdoc['lang']] }}</td>
|
||||||
<td class="col--submit-at">{{ datetime_span(rdoc['_id'])|safe }}</td>
|
<td class="col--submit-at">{{ datetime_span(rdoc['_id'])|safe }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -50,12 +50,12 @@
|
|||||||
<span class="icon icon-wechat"></span>
|
<span class="icon icon-wechat"></span>
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if udoc.gender and udoc.gender != builtin.USER_GENDER_OTHER %}
|
{% if udoc.gender and udoc.gender != model.builtin.USER_GENDER_OTHER %}
|
||||||
<span
|
<span
|
||||||
class="profile-header__contact-item"
|
class="profile-header__contact-item"
|
||||||
data-tooltip="{{ builtin.USER_GENDER_RANGE[udoc.gender] }}"
|
data-tooltip="{{ model.builtin.USER_GENDER_RANGE[udoc.gender] }}"
|
||||||
>
|
>
|
||||||
{{ builtin.USER_GENDER_ICONS[udoc.gender] }}
|
{{ model.builtin.USER_GENDER_ICONS[udoc.gender] }}
|
||||||
</span>
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if handler.hasPerm(perm.PERM_MOD_BADGE) %}
|
{% if handler.hasPerm(perm.PERM_MOD_BADGE) %}
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ import { NamedPage } from 'vj/misc/PageLoader';
|
|||||||
async function handleCategoryClick(ev) {
|
async function handleCategoryClick(ev) {
|
||||||
const $target = $(ev.currentTarget);
|
const $target = $(ev.currentTarget);
|
||||||
const $txt = $('[name="category"]');
|
const $txt = $('[name="category"]');
|
||||||
console.log($target);
|
|
||||||
console.log($txt);
|
|
||||||
$txt.val(`${$txt.val()}, ${$target.data('category')}`);
|
$txt.val(`${$txt.val()}, ${$target.data('category')}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user