mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 09:36:08 +08:00
添加部分注释,修复一些功能
This commit is contained in:
1 parent
f40e4e2c74
commit
d3e36fcb6c
32 files changed
+320
-174
No files matched your search
@@ -250,15 +250,12 @@ class ContestCreateHandler extends ContestHandler {
|
||||
}
|
||||
async post({ title, content, rule, beginAtDate, beginAtTime, duration, pids }) {
|
||||
let beginAt, endAt;
|
||||
console.log(beginAtDate, beginAtTime);
|
||||
try {
|
||||
beginAt = new Date(Date.parse(`${beginAtDate} ${beginAtTime.replace('-', ':')}`));
|
||||
console.log(beginAt, duration);
|
||||
} catch (e) {
|
||||
throw new ValidationError('beginAtDate', 'beginAtTime');
|
||||
}
|
||||
endAt = new Date(beginAt.getTime() + duration * 3600 * 1000);
|
||||
console.log(endAt);
|
||||
if (beginAt >= endAt) throw new ValidationError('duration');
|
||||
await this.verifyProblems(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 {
|
||||
async prepare(){
|
||||
async prepare() {
|
||||
this.checkPerm(PERM_JUDGE);
|
||||
}
|
||||
async get() {
|
||||
async get({ check = false }) {
|
||||
this.response.body = {};
|
||||
if (check) return;
|
||||
let rid = await queue.get('judge', false);
|
||||
if (rid) {
|
||||
let rdoc = await record.get(rid);
|
||||
@@ -74,7 +76,6 @@ class JudgeHandler extends Handler {
|
||||
};
|
||||
this.response.body = { task };
|
||||
}
|
||||
else this.response.body = {};
|
||||
}
|
||||
async post_next() {
|
||||
await next(this.request.body);
|
||||
|
||||
@@ -56,7 +56,7 @@ class ProblemDetailHandler extends ProblemHandler {
|
||||
this.response.template = 'problem_detail.html';
|
||||
this.uid = this.user._id;
|
||||
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) this.udoc = await user.getById(this.pdoc.owner);
|
||||
this.response.body = {
|
||||
@@ -115,7 +115,7 @@ class ProblemManageHandler extends ProblemDetailHandler {
|
||||
class ProblemSettingsHandler extends ProblemManageHandler {
|
||||
async get() {
|
||||
this.response.template = 'problem_settings.html';
|
||||
this.ctx.body.path = [
|
||||
this.response.body.path = [
|
||||
['Hydro', '/'],
|
||||
['problem_main', '/p'],
|
||||
[this.pdoc.title, `/p/${this.pid}`, true],
|
||||
@@ -141,15 +141,14 @@ class ProblemEditHandler extends ProblemManageHandler {
|
||||
}
|
||||
async post({ title, content }) {
|
||||
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 });
|
||||
this.response.redirect = `/p/${pid}`;
|
||||
}
|
||||
}
|
||||
|
||||
class ProblemDataUploadHandler extends ProblemManageHandler {
|
||||
constructor(ctx) {
|
||||
super(ctx);
|
||||
async prepare(){
|
||||
this.response.template = 'problem_upload.html';
|
||||
}
|
||||
async get() {
|
||||
|
||||
@@ -17,7 +17,7 @@ class RecordListHandler extends Handler {
|
||||
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: this.user._id });
|
||||
pdict[rdoc.pid] = await problem.get(rdoc.pid, this.user._id);
|
||||
}
|
||||
this.response.body = {
|
||||
path: [
|
||||
@@ -51,7 +51,7 @@ class RecordRejudgeHandler extends Handler {
|
||||
await record.reset(rid);
|
||||
await queue.push('judge', rid);
|
||||
}
|
||||
this.response.back();
|
||||
this.back();
|
||||
}
|
||||
}
|
||||
class RecordConnectionHandler extends ConnectionHandler {
|
||||
@@ -70,7 +70,7 @@ class RecordConnectionHandler extends ConnectionHandler {
|
||||
async onRecordChange(data) {
|
||||
let rdoc = data.value;
|
||||
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;
|
||||
this.send({ html: await this.renderHTML('record_main_tr.html', { rdoc, udoc, pdoc }) });
|
||||
}
|
||||
|
||||
+39
-19
@@ -1,8 +1,11 @@
|
||||
require('./utils');
|
||||
const
|
||||
Mongo = require('mongodb'),
|
||||
{ defaults } = require('lodash'),
|
||||
builtin = require('./model/builtin'),
|
||||
pwhash = require('./lib/pwhash'),
|
||||
options = require('./options');
|
||||
options = require('./options'),
|
||||
{ udoc } = require('../interfaces');
|
||||
|
||||
async function run() {
|
||||
let mongourl = 'mongodb://';
|
||||
@@ -13,30 +16,47 @@ async function run() {
|
||||
let coll_user = db.collection('user');
|
||||
let coll_role = db.collection('role');
|
||||
let coll_blacklist = db.collection('blacklist');
|
||||
async function createRootUser() {
|
||||
let coll_token = db.collection('token');
|
||||
async function createUser() {
|
||||
let salt = pwhash.salt();
|
||||
await coll_user.insertOne({
|
||||
_id: -1,
|
||||
email: 'root@hydro',
|
||||
emailLower: 'root@hydro',
|
||||
uname: 'Root',
|
||||
unameLower: 'root',
|
||||
password: pwhash.hash('root', salt),
|
||||
salt,
|
||||
regat: new Date(),
|
||||
regip: '127.0.0.1',
|
||||
loginat: new Date(),
|
||||
loginip: '127.0.0.1',
|
||||
gravatar: 'root@hydro',
|
||||
role: 'admin'
|
||||
});
|
||||
await coll_user.insertMany([
|
||||
defaults({
|
||||
_id: 0,
|
||||
uname: 'Hydro',
|
||||
unameLower: 'hydro',
|
||||
mail: 'hydro@hydro',
|
||||
mailLower: 'hydro@hydro',
|
||||
role: 'guest'
|
||||
}, udoc),
|
||||
defaults({
|
||||
_id: 1,
|
||||
mail: 'guest@hydro',
|
||||
mailLower: 'guest@hydro',
|
||||
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('emailLower', { sparse: true });
|
||||
await coll_user.createIndex('mailLower', { sparse: true });
|
||||
await coll_role.insertMany(builtin.BUILTIN_ROLES);
|
||||
await coll_user.insertMany(builtin.BUILTIN_USERS);
|
||||
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');
|
||||
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
|
||||
email: string
|
||||
emailLower: string
|
||||
mail: string
|
||||
mailLower: string
|
||||
uname: string
|
||||
unameLower: string
|
||||
salt: string
|
||||
hash: string
|
||||
displayName: string = ''
|
||||
nAccept: number = 0
|
||||
nSubmit: number = 0
|
||||
perm: string = '0'
|
||||
constructor(user) { }
|
||||
hasPerm(perm) { }
|
||||
checkPassword(password) { }
|
||||
hashType: string
|
||||
nAccept: number
|
||||
nSubmit: number
|
||||
nLike: number
|
||||
perm: string
|
||||
hasPerm: Function
|
||||
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 {
|
||||
time: number,
|
||||
@@ -20,10 +35,10 @@ export interface TestCase {
|
||||
status: number,
|
||||
message: string
|
||||
}
|
||||
export interface Record {
|
||||
_id: import('bson').ObjectID,
|
||||
pid: string | boolean,
|
||||
creator: number,
|
||||
export interface Rdoc {
|
||||
_id: ObjectID,
|
||||
pid: ObjectID,
|
||||
owner: number,
|
||||
lang: string,
|
||||
code: string,
|
||||
score: number,
|
||||
@@ -32,18 +47,21 @@ export interface Record {
|
||||
judgeTexts: string[],
|
||||
compilerTexts: string[],
|
||||
testCases: TestCase[],
|
||||
rejudged: boolean,
|
||||
judger: string,
|
||||
judgeAt: Date,
|
||||
status: number
|
||||
}
|
||||
export interface Problem {
|
||||
_id: number | string,
|
||||
title: string,
|
||||
content: string,
|
||||
timeLimit: number,
|
||||
memoryLimit: number,
|
||||
nSubmit: number,
|
||||
nAccept: number,
|
||||
tags: string[],
|
||||
categories: string[]
|
||||
export interface Bdoc {
|
||||
_id: string,
|
||||
expireAt: Date
|
||||
}
|
||||
export interface Tdoc {
|
||||
_id: ObjectID
|
||||
beginAt: Date
|
||||
endAt: Date
|
||||
attend: number
|
||||
title: string
|
||||
content: string
|
||||
pids: ObjectID[]
|
||||
}
|
||||
@@ -2,11 +2,15 @@ const
|
||||
db = require('../service/db.js'),
|
||||
coll = db.collection('blacklist');
|
||||
|
||||
module.exports = {
|
||||
add(ip) {
|
||||
let expireAt = new Date(new Date().getTime() + 365 * 24 * 60 * 60 * 1000);
|
||||
return coll.findOneAndUpdate({ _id: ip }, { $set: { expireAt } }, { upsert: true });
|
||||
},
|
||||
get: ip => coll.findOne({ _id: ip }),
|
||||
delete: ip => coll.deleteOne({ _id: ip })
|
||||
};
|
||||
async function add(ip) {
|
||||
let expireAt = new Date(new Date().getTime() + 365 * 24 * 60 * 60 * 1000);
|
||||
return coll.findOneAndUpdate({ _id: ip }, { $set: { expireAt } }, { upsert: true });
|
||||
}
|
||||
function get(ip) {
|
||||
return coll.findOne({ _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 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 = [
|
||||
{ _id: 'guest', perm: perm.PERM_BASIC },
|
||||
{ _id: 'default', perm: perm.PERM_DEFAULT },
|
||||
@@ -262,6 +229,6 @@ const STATUS_CODES = {
|
||||
30: 'ignored'
|
||||
};
|
||||
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
|
||||
};
|
||||
+25
-1
@@ -5,13 +5,28 @@ const
|
||||
coll = db.collection('contest'),
|
||||
coll_status = db.collection('contest.status');
|
||||
|
||||
|
||||
const RULES = {
|
||||
homework: require('../module/contest/homework'),
|
||||
oi: require('../module/contest/oi'),
|
||||
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,
|
||||
beginAt = new Date(), endAt = new Date(), pids = [], data = {}) {
|
||||
validator.checkTitle(title);
|
||||
@@ -23,6 +38,11 @@ async function add(title, content, owner, rule,
|
||||
let res = await coll.insertOne(data);
|
||||
return res.insertedId;
|
||||
}
|
||||
/**
|
||||
* @param {ObjectID} tid
|
||||
* @param {object} $set
|
||||
* @returns {Tdoc} tdoc after modification
|
||||
*/
|
||||
async function edit(tid, $set) {
|
||||
if ($set.title) validator.checkTitle($set.title);
|
||||
if ($set.content) validator.checkIntro($set.content);
|
||||
@@ -36,6 +56,10 @@ async function edit(tid, $set) {
|
||||
await coll.findOneAndUpdate({ tid }, { $set });
|
||||
return tdoc;
|
||||
}
|
||||
/**
|
||||
* @param {ObjectID} tid
|
||||
* @returns {Tdoc}
|
||||
*/
|
||||
async function get(tid) {
|
||||
let tdoc = await coll.findOne({ _id: tid });
|
||||
if (!tdoc) throw new ContestNotFoundError(tid);
|
||||
|
||||
+37
-4
@@ -6,6 +6,12 @@ const
|
||||
coll = db.collection('problem'),
|
||||
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} content
|
||||
@@ -42,7 +48,12 @@ async function add({
|
||||
});
|
||||
return pid;
|
||||
}
|
||||
async function get({ pid, uid }) {
|
||||
/**
|
||||
* @param {string|ObjectID} pid
|
||||
* @param {number} uid
|
||||
* @returns {Pdoc}
|
||||
*/
|
||||
async function get(pid, uid = null) {
|
||||
let query = {};
|
||||
if (pid.generationTime || pid.length == 24) query = { _id: new ObjectID(pid) };
|
||||
else query = { pid: parseInt(pid) || pid };
|
||||
@@ -54,18 +65,36 @@ async function get({ pid, uid }) {
|
||||
}
|
||||
return pdoc;
|
||||
}
|
||||
/**
|
||||
* @param {ObjectID} pid
|
||||
* @returns {Pdoc}
|
||||
*/
|
||||
async function getById(_id) {
|
||||
_id = new ObjectID(_id);
|
||||
let pdoc = await coll.findOne({ _id });
|
||||
if (!pdoc) throw new ProblemNotFoundError(_id);
|
||||
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) {
|
||||
return coll.find(query);
|
||||
}
|
||||
/**
|
||||
* @param {ObjectID} _id
|
||||
* @param {object} query
|
||||
* @returns {Pdoc}
|
||||
*/
|
||||
async function edit(_id, $set) {
|
||||
if ($set.title) validator.checkTitle($set.title);
|
||||
if ($set.content) validator.checkContent($set.content);
|
||||
@@ -74,6 +103,10 @@ async function edit(_id, $set) {
|
||||
if (!pdoc) throw new ProblemNotFoundError(_id);
|
||||
return pdoc;
|
||||
}
|
||||
/**
|
||||
* @param {object} query
|
||||
* @returns {number}
|
||||
*/
|
||||
async function count(query) {
|
||||
return await coll.find(query).count();
|
||||
}
|
||||
@@ -87,7 +120,7 @@ async function random(query) {
|
||||
}
|
||||
async function getList(pids) {
|
||||
let r = {};
|
||||
for (let pid of pids) r[pid] = await get({pid});
|
||||
for (let pid of pids) r[pid] = await get({ pid });
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,8 @@ async function reset(rid) {
|
||||
judgeTexts: [],
|
||||
compilerTexts: [],
|
||||
judgeAt: null,
|
||||
judger: null
|
||||
judger: null,
|
||||
rejudged: true
|
||||
});
|
||||
}
|
||||
async function count(query) {
|
||||
@@ -66,6 +67,9 @@ async function getList(rids) {
|
||||
for (let rid of rids) r[rid] = await get(rid);
|
||||
return r;
|
||||
}
|
||||
function getUserInProblemMulti(uid, pid) {
|
||||
return coll.find({ owner: uid, pid });
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
add,
|
||||
@@ -74,5 +78,6 @@ module.exports = {
|
||||
update,
|
||||
count,
|
||||
reset,
|
||||
getList
|
||||
getList,
|
||||
getUserInProblemMulti
|
||||
};
|
||||
@@ -67,9 +67,5 @@ module.exports = {
|
||||
},
|
||||
async getMostRecentSessionByUid(uid) {
|
||||
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 {
|
||||
constructor(user) {
|
||||
this._id = user._id;
|
||||
this.email = user.email;
|
||||
this.mail = user.mail;
|
||||
this.uname = user.uname;
|
||||
this.salt = user.salt;
|
||||
this.hash = user.hash;
|
||||
@@ -52,12 +52,12 @@ async function getByUname(uname) {
|
||||
udoc.perm = role.perm;
|
||||
return new USER(udoc);
|
||||
}
|
||||
async function getByEmail(email, ignoreMissing = false) {
|
||||
let emailLower = email.trim().toLowerCase();
|
||||
let udoc = await coll.findOne({ emailLower });
|
||||
async function getByEmail(mail, ignoreMissing = false) {
|
||||
let mailLower = mail.trim().toLowerCase();
|
||||
let udoc = await coll.findOne({ mailLower });
|
||||
if (!udoc) {
|
||||
if (ignoreMissing) return null;
|
||||
else throw new UserNotFoundError(email);
|
||||
else throw new UserNotFoundError(mail);
|
||||
}
|
||||
let role = await coll_role.findOne({ _id: udoc.role || 'default' });
|
||||
udoc.perm = role.perm;
|
||||
@@ -70,9 +70,9 @@ async function setPassword(uid, password) {
|
||||
$set: { salt, hash: pwhash.hash(password, salt) }
|
||||
});
|
||||
}
|
||||
async function setEmail(uid, email) {
|
||||
validator.checkEmail(email);
|
||||
return await setById(uid, { email, emailLower: email.trim().toLowerCase() });
|
||||
async function setEmail(uid, mail) {
|
||||
validator.checkEmail(mail);
|
||||
return await setById(uid, { mail, mailLower: mail.trim().toLowerCase() });
|
||||
}
|
||||
function setById(uid, args) {
|
||||
coll.findOneAndUpdate({ _id: uid }, { $set: args });
|
||||
@@ -88,17 +88,17 @@ async function changePassword(uid, currentPassword, newPassword) {
|
||||
$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.checkPassword(password);
|
||||
validator.checkEmail(email);
|
||||
validator.checkEmail(mail);
|
||||
let salt = pwhash.salt();
|
||||
if (!uid) uid = system.incUserCounter();
|
||||
try {
|
||||
await coll.insertOne({
|
||||
_id: uid,
|
||||
email,
|
||||
emailLower: email.trim().toLowerCase(),
|
||||
mail,
|
||||
mailLower: mail.trim().toLowerCase(),
|
||||
uname,
|
||||
unameLower: uname.trim().toLowerCase(),
|
||||
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(),
|
||||
loginip: regip,
|
||||
role,
|
||||
gravatar: email
|
||||
gravatar: mail
|
||||
});
|
||||
} catch (e) {
|
||||
throw new UserAlreadyExistError([uid, uname, email]);
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
const
|
||||
yaml = require('js-yaml'),
|
||||
{ defaults } = require('lodash'),
|
||||
{ defaultsDeep } = require('lodash'),
|
||||
fs = require('fs'),
|
||||
path = require('path');
|
||||
|
||||
@@ -47,7 +47,7 @@ let options = {
|
||||
|
||||
try {
|
||||
let t = yaml.safeLoad(fs.readFileSync(path.resolve(process.cwd(), 'config.yaml')));
|
||||
options = defaults(t, options);
|
||||
options = defaultsDeep(t, options);
|
||||
} catch (e) {
|
||||
console.error('Cannot load config');
|
||||
}
|
||||
|
||||
@@ -12,4 +12,4 @@ module.exports = {
|
||||
publish(event, data) {
|
||||
bus.emit(event, { value: data, event });
|
||||
}
|
||||
};
|
||||
};
|
||||
+20
-11
@@ -13,7 +13,8 @@ const
|
||||
blacklist = require('../model/blacklist'),
|
||||
token = require('../model/token'),
|
||||
opcount = require('../model/opcount'),
|
||||
{ UserNotFoundError, BlacklistedError, PermissionError } = require('../error');
|
||||
{ UserNotFoundError, BlacklistedError, PermissionError,
|
||||
NotFoundError } = require('../error');
|
||||
|
||||
const options = require('../options');
|
||||
let http = options.listen.https ? require('https') : require('http');
|
||||
@@ -49,7 +50,7 @@ class Handler {
|
||||
this.response = {
|
||||
body: '',
|
||||
type: '',
|
||||
status: 404,
|
||||
status: null,
|
||||
template: null,
|
||||
redirect: null,
|
||||
attachment: name => ctx.attachment(name)
|
||||
@@ -102,9 +103,10 @@ class Handler {
|
||||
await opcount.inc(op, this.request.ip, period_secs, max_operations);
|
||||
}
|
||||
back() {
|
||||
this.ctx.redirect(this.request.headers.referer || '/');
|
||||
this.response.redirect = this.request.headers.referer || '/';
|
||||
}
|
||||
async ___prepare() {
|
||||
this.now = new Date();
|
||||
this._handler.sid = this.request.cookies.get('sid');
|
||||
this._handler.save = this.request.cookies.get('save');
|
||||
this._handler.tokenType = token.TYPE_SESSION;
|
||||
@@ -119,9 +121,8 @@ class Handler {
|
||||
let bdoc = await blacklist.get(this.request.ip);
|
||||
if (bdoc) throw new BlacklistedError(this.request.ip);
|
||||
this.user = await user.getById(this.session.uid);
|
||||
console.log(this.user, 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');
|
||||
}
|
||||
async ___cleanup() {
|
||||
@@ -135,6 +136,10 @@ class Handler {
|
||||
await this.saveCookie();
|
||||
}
|
||||
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.response.body || this.response.template) {
|
||||
if (this.request.query.noTemplate || this.preferJson) return;
|
||||
@@ -150,9 +155,11 @@ class Handler {
|
||||
this.ctx.response.status = 302;
|
||||
this.ctx.redirect(this.response.redirect);
|
||||
} 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.status) this.ctx.response.status = this.response.status;
|
||||
}
|
||||
}
|
||||
async saveCookie() {
|
||||
@@ -210,7 +217,6 @@ function Route(route, handler) {
|
||||
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 (h[`${method}_${ctx.request.body.operation}`])
|
||||
await h[`${method}_${ctx.request.body.operation}`](args);
|
||||
@@ -231,20 +237,22 @@ class ConnectionHandler {
|
||||
* @param {import('sockjs').Connection} conn
|
||||
*/
|
||||
constructor(conn) {
|
||||
let that = this;
|
||||
this.conn = conn;
|
||||
this.request = {
|
||||
cookies: {
|
||||
get(name) {
|
||||
return conn.cookies[name];
|
||||
return that.request.cookies[name];
|
||||
},
|
||||
set() { }
|
||||
},
|
||||
params: {},
|
||||
headers: conn.headers
|
||||
};
|
||||
this._handler = {};
|
||||
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]);
|
||||
for (let i in p) this.request.params[p[i][0]] = decodeURIComponent(p[i][1]);
|
||||
}
|
||||
renderHTML(name, context) {
|
||||
console.time(name);
|
||||
@@ -292,7 +300,7 @@ function Connection(prefix, handler) {
|
||||
sock.on('connection', async conn => {
|
||||
let h = new handler(conn);
|
||||
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.page) args.page = parseInt(args.page);
|
||||
@@ -313,6 +321,7 @@ function Connection(prefix, handler) {
|
||||
if (h.___cleanup) await h.___cleanup(args);
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
if (h.onerror) await h.onerror(e);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
{%- if tdoc is none %}
|
||||
href="/p/{{ pdoc.pid }}"
|
||||
{%- 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']) }}"
|
||||
{%- 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']) }}"
|
||||
href="/t/{{ tdoc._id }}/{{ pdoc._id }}"
|
||||
{%- endif %}
|
||||
>
|
||||
{%- endif %}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{% 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">
|
||||
<span class="icon record-status--icon {{ builtin.STATUS_CODES[rdoc['status']] }}"></span>
|
||||
<a href="/r/{{ rdoc._id }}" class="record-status--text {{ builtin.STATUS_CODES[rdoc['status']] }}">
|
||||
{{ builtin.STATUS_TEXTS[rdoc['status']] }}
|
||||
<span class="icon record-status--icon {{ model.builtin.STATUS_CODES[rdoc['status']] }}"></span>
|
||||
<a href="/r/{{ rdoc._id }}" class="record-status--text {{ model.builtin.STATUS_CODES[rdoc['status']] }}">
|
||||
{{ model.builtin.STATUS_TEXTS[rdoc['status']] }}
|
||||
</a>
|
||||
</div>
|
||||
{% if rdoc['status'] == status.STATUS_JUDGING %}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
{{ udoc['uname'] }}
|
||||
</a>
|
||||
{% 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) %}
|
||||
<span class="user-profile-badge v-center badge--mod" title="Moderator">MOD</span>
|
||||
{% endif %}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
{%- for column in rows[0] -%}
|
||||
<th class="col--{{ column['type'] }}">
|
||||
{% 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 %}
|
||||
{{ column.value }}
|
||||
{% endif %}
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
<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>
|
||||
<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>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
@@ -61,7 +61,7 @@
|
||||
</div>
|
||||
<div class="footer__extra-right">
|
||||
<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>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<span class="icon icon-flag"></span> {{ _('View Problem') }}
|
||||
</a></li>
|
||||
{% 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') }}
|
||||
</a></li>
|
||||
{% elif handler.is_done(tdoc) %}
|
||||
|
||||
@@ -98,9 +98,9 @@
|
||||
<dt>{{ _('Submission') }}</dt>
|
||||
<dd>
|
||||
{% if pdoc['psdoc']['status'] %}
|
||||
<span class="icon record-status--icon {{ builtin.STATUS_CODES[pdoc['psdoc']['status']] }}"></span>
|
||||
<a href="/r/{{ pdoc['psdoc']['rid'] }}" class="record-status--text {{ builtin.STATUS_CODES[pdoc['psdoc']['status']] }}">
|
||||
{{ builtin.STATUS_TEXTS[pdoc['psdoc']['status']] }}
|
||||
<span class="icon record-status--icon {{ model.builtin.STATUS_CODES[pdoc['psdoc']['status']] }}"></span>
|
||||
<a href="/r/{{ pdoc['psdoc']['rid'] }}" class="record-status--text {{ model.builtin.STATUS_CODES[pdoc['psdoc']['status']] }}">
|
||||
{{ model.builtin.STATUS_TEXTS[pdoc['psdoc']['status']] }}
|
||||
</a>
|
||||
{% else %}
|
||||
{{ _('(None)') }}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
</div>
|
||||
<div class="section__body">
|
||||
<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">
|
||||
<h2 class="section__title">
|
||||
<a href="/p?category={{ category|urlencode }}">{{ category }}</a>
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
<h1 class="section__title">{{ _('Categories') }} ({{ _('click to add') }})</h1>
|
||||
</div>
|
||||
<div class="section__body">
|
||||
{% for category, sub_categories in builtin.CATEGORIES %}
|
||||
{% for category, sub_categories in model.builtin.CATEGORIES %}
|
||||
<div>
|
||||
<h2 class="section__title"><a class="category-a" href="javascript:;" data-category="{{ category }}">{{ category }}</a></h2>
|
||||
<ol>
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="medium-9 columns">
|
||||
{% if not tdoc or rdocs %}
|
||||
{% if not tdoc or rdocs.length %}
|
||||
<div class="section">
|
||||
<div class="section__header">
|
||||
<h1 class="section__title">{{ _('Recent 10 Records') }}</h1>
|
||||
</div>
|
||||
<div class="section__body no-padding">
|
||||
{% if not rdocs %}
|
||||
{% if not rdocs.length %}
|
||||
{{ nothing.render('You have not submitted any solutions for this problem') }}
|
||||
{% else %}
|
||||
<table class="data-table">
|
||||
@@ -44,7 +44,12 @@
|
||||
</div>
|
||||
<div class="section__body">
|
||||
<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}) }}
|
||||
<div class="row"><div class="columns">
|
||||
<input type="hidden" name="csrf_token" value="{{ handler.csrf_token }}">
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<h1 class="section__title">{{ _('Code') }}</h1>
|
||||
</div>
|
||||
<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>
|
||||
{% endif %}
|
||||
@@ -82,7 +82,7 @@
|
||||
{% endif %}
|
||||
<dt>{{ _('Language') }}</dt>
|
||||
<dd>
|
||||
{{ builtin.LANG_TEXTS[rdoc['lang']] }}
|
||||
{{ model.builtin.LANG_TEXTS[rdoc['lang']] }}
|
||||
</dd>
|
||||
<dt>
|
||||
{{ _('Submit At') }}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<div class="section visible" id="status">
|
||||
<div class="section__header">
|
||||
<h1 class="section__title">
|
||||
<span class="icon record-status--icon {{ builtin.STATUS_CODES[rdoc['status']] }}"></span>
|
||||
<span class="record-status--text {{ builtin.STATUS_CODES[rdoc['status']] }}">
|
||||
{{ builtin.STATUS_TEXTS[rdoc['status']] }}
|
||||
<span class="icon record-status--icon {{ model.builtin.STATUS_CODES[rdoc['status']] }}"></span>
|
||||
<span class="record-status--text {{ model.builtin.STATUS_CODES[rdoc['status']] }}">
|
||||
{{ model.builtin.STATUS_TEXTS[rdoc['status']] }}
|
||||
</span>
|
||||
</h1>
|
||||
</div>
|
||||
@@ -37,13 +37,13 @@
|
||||
<tbody>
|
||||
{% for rcdoc in rdoc.cases %}
|
||||
<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 }}
|
||||
</td>
|
||||
<td class="col--status">
|
||||
<span class="icon record-status--icon {{ builtin.STATUS_CODES[rcdoc['status']] }}"></span>
|
||||
<span class="record-status--text {{ builtin.STATUS_CODES[rcdoc['status']] }}">
|
||||
{{ builtin.STATUS_TEXTS[rcdoc['status']] }}
|
||||
<span class="icon record-status--icon {{ model.builtin.STATUS_CODES[rcdoc['status']] }}"></span>
|
||||
<span class="record-status--text {{ model.builtin.STATUS_CODES[rcdoc['status']] }}">
|
||||
{{ model.builtin.STATUS_TEXTS[rcdoc['status']] }}
|
||||
</span>
|
||||
<span>{{ rcdoc['judgeText'] }}</span>
|
||||
<a class="tool-button" href="/wiki/help#status"><span class="icon icon-help"></span></a>
|
||||
|
||||
@@ -25,6 +25,6 @@
|
||||
</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--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>
|
||||
</tr>
|
||||
@@ -50,12 +50,12 @@
|
||||
<span class="icon icon-wechat"></span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if udoc.gender and udoc.gender != builtin.USER_GENDER_OTHER %}
|
||||
{% if udoc.gender and udoc.gender != model.builtin.USER_GENDER_OTHER %}
|
||||
<span
|
||||
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>
|
||||
{% endif %}
|
||||
{% if handler.hasPerm(perm.PERM_MOD_BADGE) %}
|
||||
|
||||
@@ -3,8 +3,6 @@ import { NamedPage } from 'vj/misc/PageLoader';
|
||||
async function handleCategoryClick(ev) {
|
||||
const $target = $(ev.currentTarget);
|
||||
const $txt = $('[name="category"]');
|
||||
console.log($target);
|
||||
console.log($txt);
|
||||
$txt.val(`${$txt.val()}, ${$target.data('category')}`);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user