Hello airbnb

This commit is contained in:
masnn committed 2020-04-15 15:08:05 +08:00
1 parent f9468536de
commit 8c195661d7
78 files changed
+2032 -1471

No files matched your search

+4 -4
View File
@@ -1,9 +1,9 @@
const
db = require('../service/db.js'),
coll = db.collection('blacklist');
const db = require('../service/db.js');
const coll = db.collection('blacklist');
async function add(ip) {
let expireAt = new Date(new Date().getTime() + 365 * 24 * 60 * 60 * 1000);
const expireAt = new Date(new Date().getTime() + 365 * 24 * 60 * 60 * 1000);
return coll.findOneAndUpdate({ _id: ip }, { $set: { expireAt } }, { upsert: true });
}
function get(ip) {
+49 -40
View File
@@ -1,20 +1,21 @@
const perm = require('../permission');
const BUILTIN_ROLES = [
{ _id: 'guest', perm: perm.PERM_BASIC },
{ _id: 'default', perm: perm.PERM_DEFAULT },
{ _id: 'admin', perm: perm.PERM_ADMIN }
{ _id: 'admin', perm: perm.PERM_ADMIN },
];
const CATEGORIES = {
'动态规划': [
动态规划: [
'LCS',
'LIS',
'背包',
'单调性DP',
'环形DP',
'树形DP',
'状态压缩DP'
'状态压缩DP',
],
'搜索': [
搜索: [
'枚举',
'搜索与剪枝',
'启发式搜索',
@@ -22,9 +23,9 @@ const CATEGORIES = {
'双向搜索',
'折半搜索',
'记忆化搜索',
'模拟退火'
'模拟退火',
],
'计算几何': [
计算几何: [
'半平面交',
'凸包',
'几何图形的交与并',
@@ -37,19 +38,19 @@ const CATEGORIES = {
'平面图的对偶图',
'三角剖分',
'梯形剖分',
'几何知识'
'几何知识',
],
'贪心': [],
'树结构': [
贪心: [],
树结构: [
'最近公共祖先',
'生成树',
'DFS序列',
'树上倍增',
'树的分治',
'树链剖分',
'Link-Cut-Tree'
'Link-Cut-Tree',
],
'图结构': [
图结构: [
'平面图',
'二分图',
'二分图匹配',
@@ -60,9 +61,9 @@ const CATEGORIES = {
'强连通分量',
'割点割边',
'欧拉回路',
'2-SAT'
'2-SAT',
],
'数论': [
数论: [
'素数判定',
'欧几里得算法',
'不定方程',
@@ -73,10 +74,10 @@ const CATEGORIES = {
'大整数质因数分解',
'勾股方程',
'积性函数',
'Fibonacci数列'
'Fibonacci数列',
],
'模拟': [],
'数据结构': [
模拟: [],
数据结构: [
'栈',
'队列',
'链表',
@@ -93,41 +94,41 @@ const CATEGORIES = {
'k-d树',
'块状链表',
'Hashing',
'函数式编程'
'函数式编程',
],
'博弈论': [],
'字符串': [
博弈论: [],
字符串: [
'KMP',
'后缀数据结构',
'Trie树',
'AC自动机',
'Manacher',
'表达式处理',
'最小表示法'
'最小表示法',
],
'组合数学': [
组合数学: [
'生成函数',
'容斥原理',
'康托展开',
'Catalan数列',
'Stirling数',
'差分',
'Polya定理'
'Polya定理',
],
'线性代数': [
线性代数: [
'矩阵乘法',
'高斯消元',
'线性规划'
'线性规划',
],
'高精度': [
'FFT'
高精度: [
'FFT',
],
'递推': [],
'概率论': [
'随机化'
递推: [],
概率论: [
'随机化',
],
'NPC': [],
'其他': [
NPC: [],
其他: [
'二分查找',
'三分查找',
'双指针扫描',
@@ -137,15 +138,15 @@ const CATEGORIES = {
'快速幂',
'数学',
'排序',
'构造'
]
'构造',
],
};
const FOOTER_EXTRA_HTMLS = [];
const VIEW_LANGS = [
{ code: 'zh-CN', name: 'zh-CN' }
{ code: 'zh-CN', name: 'zh-CN' },
];
const LANGS = {
cc: 'cpp'
cc: 'cpp',
};
const LANG_TEXTS = {
c: 'C',
@@ -192,7 +193,7 @@ const STATUS = {
STATUS_JUDGING: 20,
STATUS_COMPILING: 21,
STATUS_FETCHED: 22,
STATUS_IGNORED: 30
STATUS_IGNORED: 30,
};
const STATUS_TEXTS = {
[STATUS.STATUS_WAITING]: 'Waiting',
@@ -226,9 +227,17 @@ const STATUS_CODES = {
20: 'progress',
21: 'progress',
22: 'progress',
30: 'ignored'
30: 'ignored',
};
module.exports = {
BUILTIN_ROLES, CATEGORIES, VIEW_LANGS, FOOTER_EXTRA_HTMLS, LANGS,
LANG_TEXTS, LANG_HIGHLIGHT_ID, STATUS, STATUS_TEXTS, STATUS_CODES
};
BUILTIN_ROLES,
CATEGORIES,
VIEW_LANGS,
FOOTER_EXTRA_HTMLS,
LANGS,
LANG_TEXTS,
LANG_HIGHLIGHT_ID,
STATUS,
STATUS_TEXTS,
STATUS_CODES,
};
+84 -64
View File
@@ -1,18 +1,20 @@
const
user = require('./user'),
problem = require('./problem'),
{ ValidationError, ContestNotFoundError, ContestAlreadyAttendedError,
ContestNotAttendedError, ProblemNotFoundError, ContestScoreboardHiddenError } = require('../error'),
{ PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD } = require('../permission'),
validator = require('../lib/validator'),
db = require('../service/db'),
coll = db.collection('contest'),
coll_status = db.collection('contest.status');
const user = require('./user');
const problem = require('./problem');
const {
ValidationError, ContestNotFoundError, ContestAlreadyAttendedError,
ContestNotAttendedError, ProblemNotFoundError, ContestScoreboardHiddenError,
} = require('../error');
const { PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD } = require('../permission');
const validator = require('../lib/validator');
const db = require('../service/db');
const coll = db.collection('contest');
const collStatus = db.collection('contest.status');
const RULES = {
homework: require('../module/contest/homework'),
oi: require('../module/contest/oi'),
acm: require('../module/contest/acm')
acm: require('../module/contest/acm'),
};
/**
@@ -21,13 +23,13 @@ const RULES = {
*/
/**
* @param {string} title
* @param {string} content
* @param {number} owner
* @param {string} rule
* @param {Date} beginAt
* @param {Date} endAt
* @param {ObjectID[]} pids
* @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
*/
@@ -37,112 +39,120 @@ async function add(title, content, owner, rule,
validator.checkContent(content);
if (!this.RULES[rule]) throw new ValidationError('rule');
if (beginAt >= endAt) throw new ValidationError('beginAt', 'endAt');
Object.assign(data, { content, owner, title, rule, beginAt, endAt, pids, attend: 0 });
Object.assign(data, {
content, owner, title, rule, beginAt, endAt, pids, attend: 0,
});
this.RULES[rule].check(data);
let res = await coll.insertOne(data);
const res = await coll.insertOne(data);
return res.insertedId;
}
/**
* @param {ObjectID} tid
* @param {object} $set
* @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);
if ($set.rule)
if (!this.RULES[$set.rule]) throw new ValidationError('rule');
if ($set.beginAt && $set.endAt)
if ($set.beginAt >= $set.endAt) throw new ValidationError('beginAt', 'endAt');
let tdoc = await coll.findOne({ tid });
if ($set.rule) { if (!this.RULES[$set.rule]) throw new ValidationError('rule'); }
if ($set.beginAt && $set.endAt) if ($set.beginAt >= $set.endAt) throw new ValidationError('beginAt', 'endAt');
const tdoc = await coll.findOne({ tid });
if (!tdoc) throw new ContestNotFoundError(tid);
this.RULES[$set.rule || tdoc.rule].check(Object.assign(tdoc, $set));
await coll.findOneAndUpdate({ tid }, { $set });
return tdoc;
}
/**
* @param {ObjectID} tid
* @param {ObjectID} tid
* @returns {Tdoc}
*/
async function get(tid) {
let tdoc = await coll.findOne({ _id: tid });
const tdoc = await coll.findOne({ _id: tid });
if (!tdoc) throw new ContestNotFoundError(tid);
return tdoc;
}
async function updateStatus(tid, uid, rid, pid, accept, score) {
let tdoc = await get(tid);
let tsdoc = await coll_status.findOneAndUpdate({ tid: tdoc._id, uid }, {
$push: { journal: { rid, pid, accept, score } },
$inc: { rev: 1 }
const tdoc = await get(tid);
const tsdoc = await collStatus.findOneAndUpdate({ tid: tdoc._id, uid }, {
$push: {
journal: {
rid, pid, accept, score,
},
},
$inc: { rev: 1 },
}, { upsert: true });
if (!tsdoc.value.attend) throw new ContestNotAttendedError(tid, uid);
}
async function getListStatus(uid, tids) {
let r = {};
for (let tid of tids) r[tid] = await this.getStatus(tid, uid);
const r = {};
for (const tid of tids) r[tid] = await this.getStatus(tid, uid); // eslint-disable-line no-await-in-loop
return r;
}
async function attend(tid, uid) {
try {
await coll_status.insertOne({ tid, uid, attend: 1 });
await collStatus.insertOne({ tid, uid, attend: 1 });
} catch (e) {
throw new ContestAlreadyAttendedError(tid, uid);
}
await coll.findOneAndUpdate({ _id: tid }, { $inc: { attend: 1 } });
}
function getMultiStatus(query) {
return coll_status.find(query);
return collStatus.find(query);
}
function is_new(tdoc, days = 1) {
let now = new Date().getTime();
let readyAt = tdoc.beginAt.getTime();
const now = new Date().getTime();
const readyAt = tdoc.beginAt.getTime();
return (now < readyAt - days * 24 * 3600 * 1000);
}
function is_upcoming(tdoc, days = 1) {
let now = new Date().getTime();
let readyAt = tdoc.beginAt.getTime();
const now = new Date().getTime();
const readyAt = tdoc.beginAt.getTime();
return (now > readyAt - days * 24 * 3600 * 1000 && now < tdoc.beginAt);
}
function is_not_started(tdoc) {
return (new Date()) < tdoc.beginAt;
}
function is_ongoing(tdoc) {
let now = new Date();
const now = new Date();
return (tdoc.beginAt <= now && now < tdoc.endAt);
}
function is_done(tdoc) {
return tdoc.endAt <= new Date();
}
const ContestHandlerMixin = c => class extends c {
const ContestHandlerMixin = (c) => class extends c {
canViewHiddenScoreboard() {
return this.user.hasPerm(PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD);
}
canShowRecord(tdoc, allowPermOverride = true) {
if (RULES[tdoc.rule].showRecord(tdoc, new Date())) return true;
if (allowPermOverride && this.canViewHiddenScoreboard(tdoc)) return true;
return false;
}
canShowScoreboard(tdoc, allowPermOverride = true) {
if (RULES[tdoc.rule].showScoreboard(tdoc, new Date())) return true;
if (allowPermOverride && this.canViewHiddenScoreboard(tdoc)) return true;
return false;
}
async getScoreboard(tid, isExport = false) {
let tdoc = await get(tid);
const tdoc = await get(tid);
if (!this.canShowScoreboard(tdoc)) throw new ContestScoreboardHiddenError(tid);
let tsdocs = await getMultiStatus(tid).sort(RULES[tdoc.rule].statusSort).toArray();
let uids = [];
for (let tsdoc of tsdocs) uids.push(tsdoc.uid);
let [udict, pdict] = await Promise.all([user.getList(uids), problem.getList(tdoc['pids'])]);
let ranked_tsdocs = RULES[tdoc.rule].rank(tsdocs);
let rows = RULES[tdoc.rule].scoreboard(isExport, str => str ? str.toString().translate(this.user.language) : '', tdoc, ranked_tsdocs, udict, pdict);
const tsdocs = await getMultiStatus(tid).sort(RULES[tdoc.rule].statusSort).toArray();
const uids = [];
for (const tsdoc of tsdocs) uids.push(tsdoc.uid);
const [udict, pdict] = await Promise.all([user.getList(uids), problem.getList(tdoc.pids)]);
const rankedTsdocs = RULES[tdoc.rule].rank(tsdocs);
const rows = RULES[tdoc.rule].scoreboard(isExport, (str) => (str ? str.toString().translate(this.user.language) : ''), tdoc, rankedTsdocs, udict, pdict);
return [tdoc, rows, udict];
}
async verifyProblems(pids) {
let r = [];
for (let pid of pids) {
let res = await problem.get(pid);
async verifyProblems(pids) { // eslint-disable-line class-methods-use-this
const r = [];
for (const pid of pids) {
const res = await problem.get(pid); // eslint-disable-line no-await-in-loop
if (res) r.push(res._id);
else throw new ProblemNotFoundError(pid);
}
@@ -151,26 +161,37 @@ const ContestHandlerMixin = c => class extends c {
};
module.exports = {
RULES, ContestHandlerMixin, add, getListStatus, attend, edit, get, updateStatus,
count: query => coll.find(query).count(),
getMulti: query => coll.find(query),
RULES,
ContestHandlerMixin,
add,
getListStatus,
attend,
edit,
get,
updateStatus,
count: (query) => coll.find(query).count(),
getMulti: (query) => coll.find(query),
getStatus: (tid, uid) => coll_status.findOne({ tid, uid }),
setStatus: (tid, uid, $set) => coll_status.findOneAndUpdate({ tid, uid }, { $set }),
is_new, is_upcoming, is_not_started, is_ongoing, is_done,
status_text: tdoc =>
is_new,
is_upcoming,
is_not_started,
is_ongoing,
is_done,
status_text: (tdoc) => (
is_new(tdoc)
? 'New'
: is_upcoming(tdoc)
? 'Ready (☆▽☆)'
: is_ongoing(tdoc)
? 'Live...'
: 'Done',
get_status: tdoc =>
: 'Done'),
get_status: (tdoc) => (
is_not_started(tdoc)
? 'not_started'
: is_ongoing(tdoc)
? 'ongoing'
: 'finished',
: 'finished'),
};
/*
@@ -336,7 +357,6 @@ def _format_pids(pids_list):
return ','.join([str(pid) for pid in pids_list])
class ContestStatusMixin(object):
@property
@functools.lru_cache()
@@ -447,4 +467,4 @@ class ContestCommonOperationMixin(object):
class ContestMixin(ContestStatusMixin, ContestVisibilityMixin, ContestCommonOperationMixin):
pass
*/
*/
+1 -1
View File
@@ -9,5 +9,5 @@ module.exports = {
system: require('./system'),
token: require('./token'),
training: require('./training'),
user: require('./user')
user: require('./user'),
};
+18 -25
View File
@@ -1,35 +1,28 @@
const
{ OpcountExceededError } = require('../error'),
db = require('../service/db.js'),
coll = db.collection('opcount');
const { OpcountExceededError } = require('../error');
const db = require('../service/db.js');
const coll = db.collection('opcount');
module.exports = {
/**
* @param {string} op
* @param {string} ident
* @param {number} period_secs
* @param {number} max_operations
* @param {string} op
* @param {string} ident
* @param {number} period_secs
* @param {number} max_operations
*/
async inc(op, ident, period_secs, max_operations) {
let cur_time = new Date().getTime();
let begin_at = new Date(cur_time - cur_time % (period_secs * 1000));
let expire_at = new Date(begin_at.getTime() + period_secs * 1000);
async inc(op, ident, periodSecs, maxOperations) {
const curTime = new Date().getTime();
const beginAt = new Date(curTime - (curTime % (periodSecs * 1000)));
const expireAt = new Date(beginAt.getTime() + periodSecs * 1000);
try {
await coll.findOneAndUpdate({
ident, begin_at, expire_at,
op: { $not: { $gte: max_operations } }
ident,
beginAt,
expireAt,
op: { $not: { $gte: maxOperations } },
}, { $inc: { op: 1 } }, { upsert: true });
} catch (e) {
throw new OpcountExceededError(op, period_secs, max_operations);
throw new OpcountExceededError(op, periodSecs, maxOperations);
}
}
},
};
/*
@argmethod.wrap
async def ensure_indexes():
coll = db.coll('opcount')
await coll.create_index([('ident', 1),
('begin_at', 1),
('expire_at', 1)], unique=True)
await coll.create_index('expire_at', expireAfterSeconds=0)
*/
+34 -37
View File
@@ -1,10 +1,10 @@
const
{ ObjectID } = require('bson'),
{ ProblemNotFoundError } = require('../error'),
validator = require('../lib/validator'),
db = require('../service/db.js'),
coll = db.collection('problem'),
coll_status = db.collection('problem.status');
const { ObjectID } = require('bson');
const { ProblemNotFoundError } = require('../error');
const validator = require('../lib/validator');
const db = require('../service/db.js');
const coll = db.collection('problem');
const collStatus = db.collection('problem.status');
/**
* @typedef {import('../interface').Pdoc} Pdoc
@@ -13,14 +13,14 @@ const
*/
/**
* @param {string} title
* @param {string} content
* @param {number} owner
* @param {number} pid
* @param {import('bson').ObjectID} data
* @param {string[]} category
* @param {string[]} tag
* @param {boolean} hidden
* @param {string} title
* @param {string} content
* @param {number} owner
* @param {number} pid
* @param {import('bson').ObjectID} data
* @param {string[]} category
* @param {string[]} tag
* @param {boolean} hidden
*/
async function add({
title,
@@ -30,7 +30,7 @@ async function add({
data = null,
category = [],
tag = [],
hidden = false
hidden = false,
}) {
validator.checkTitle(title);
validator.checkContent(content);
@@ -44,7 +44,7 @@ async function add({
tag,
hidden,
nSubmit: 0,
nAccept: 0
nAccept: 0,
});
return pid;
}
@@ -55,13 +55,13 @@ async function add({
*/
async function get(pid, uid = null) {
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 };
let pdoc = await coll.findOne(query);
const pdoc = await coll.findOne(query);
if (!pdoc) throw new ProblemNotFoundError(pid);
if (uid) {
query.uid = uid;
pdoc.psdoc = await coll_status.findOne(query);
pdoc.psdoc = await collStatus.findOne(query);
}
return pdoc;
}
@@ -71,7 +71,7 @@ async function get(pid, uid = null) {
*/
async function getById(_id) {
_id = new ObjectID(_id);
let pdoc = await coll.findOne({ _id });
const pdoc = await coll.findOne({ _id });
if (!pdoc) throw new ProblemNotFoundError(_id);
return pdoc;
}
@@ -81,7 +81,8 @@ async function getById(_id) {
* @returns {Pdoc[]}
*/
function getMany(query, sort, page, limit) {
return coll.find(query).sort(sort).skip((page - 1) * limit).limit(limit).toArray();
return coll.find(query).sort(sort).skip((page - 1) * limit).limit(limit)
.toArray();
}
/**
* @param {object} query
@@ -99,28 +100,24 @@ async function edit(_id, $set) {
if ($set.title) validator.checkTitle($set.title);
if ($set.content) validator.checkContent($set.content);
await coll.findOneAndUpdate({ _id }, { $set });
let pdoc = await getById(_id);
const pdoc = await getById(_id);
if (!pdoc) throw new ProblemNotFoundError(_id);
return pdoc;
}
/**
* @param {object} query
* @returns {number}
*/
async function count(query) {
return await coll.find(query).count();
function count(query) {
return coll.find(query).count();
}
async function random(query) {
let pdocs = coll.find(query);
let pcount = await pdocs.count();
const pdocs = coll.find(query);
const pcount = await pdocs.count();
if (pcount) {
let pdoc = await pdocs.skip(Math.floor(Math.random() * pcount)).limit(1).toArray()[0];
const pdoc = await pdocs.skip(Math.floor(Math.random() * pcount)).limit(1).toArray()[0];
return pdoc.pid;
} else return null;
} return null;
}
async function getList(pids) {
let r = {};
for (let pid of pids) r[pid] = await get(pid);
const r = {};
for (const pid of pids) r[pid] = await get(pid); // eslint-disable-line no-await-in-loop
return r;
}
@@ -133,5 +130,5 @@ module.exports = {
random,
getById,
getMulti,
getList
};
getList,
};
+27 -26
View File
@@ -1,13 +1,13 @@
const
_ = require('lodash'),
{ ObjectID } = require('bson'),
{ RecordNotFoundError } = require('../error'),
{ STATUS_WAITING } = require('../model/builtin').STATUS,
db = require('../service/db.js'),
coll = db.collection('record');
const _ = require('lodash');
const { ObjectID } = require('bson');
const { RecordNotFoundError } = require('../error');
const { STATUS_WAITING } = require('./builtin').STATUS;
const db = require('../service/db.js');
const coll = db.collection('record');
/**
* @param {import('../interface').Record} data
* @param {import('../interface').Record} data
*/
async function add(data) {
_.defaults(data, {
@@ -20,33 +20,34 @@ async function add(data) {
compilerTexts: [],
testCases: [],
judger: null,
judgeAt: null
judgeAt: null,
});
let res = await coll.insertOne(data);
const res = await coll.insertOne(data);
return res.insertedId;
}
/**
* @param {string} rid
* @param {string} rid
* @returns {import('../interface').Record}
*/
async function get(rid) {
let _id = new ObjectID(rid);
let rdoc = await coll.findOne({ _id });
const _id = new ObjectID(rid);
const rdoc = await coll.findOne({ _id });
if (!rdoc) throw new RecordNotFoundError(rid);
return rdoc;
}
async function getMany(query, sort, page, limit) {
return await coll.find(query).sort(sort).skip((page - 1) * limit).limit(limit).toArray();
function getMany(query, sort, page, limit) {
return coll.find(query).sort(sort).skip((page - 1) * limit).limit(limit)
.toArray();
}
async function update(rid, $set) {
let _id = new ObjectID(rid);
const _id = new ObjectID(rid);
await coll.findOneAndUpdate({ _id }, { $set });
let rdoc = await coll.findOne({ _id });
const rdoc = await coll.findOne({ _id });
if (!rdoc) throw new RecordNotFoundError(rid);
return rdoc;
}
async function reset(rid) {
return await update(rid, {
function reset(rid) {
return update(rid, {
score: 0,
status: STATUS_WAITING,
time: 0,
@@ -56,15 +57,15 @@ async function reset(rid) {
compilerTexts: [],
judgeAt: null,
judger: null,
rejudged: true
rejudged: true,
});
}
async function count(query) {
return await coll.find(query).count();
function count(query) {
return coll.find(query).count();
}
async function getList(rids) {
let r = {};
for (let rid of rids) r[rid] = await get(rid);
const r = {};
for (const rid of rids) r[rid] = await get(rid); // eslint-disable-line no-await-in-loop
return r;
}
function getUserInProblemMulti(uid, pid) {
@@ -79,5 +80,5 @@ module.exports = {
count,
reset,
getList,
getUserInProblemMulti
};
getUserInProblemMulti,
};
+46 -45
View File
@@ -1,93 +1,94 @@
const
{ ObjectID } = require('bson'),
{ SolutionNotFoundError, AlreadyVotedError } = require('../error'),
validator = require('../lib/validator'),
db = require('../service/db.js'),
coll = db.collection('solution'),
coll_status = db.collection('solution.status');
const { ObjectID } = require('bson');
const { SolutionNotFoundError } = require('../error');
const validator = require('../lib/validator');
const db = require('../service/db.js');
/**
const coll = db.collection('solution');
const collStatus = db.collection('solution.status');
/**
* @param {string} pid
* @param {number} owner
* @param {string} content
* @param {number} owner
* @param {string} content
*/
async function add(pid, owner, content) {
validator.checkContent(content);
pid = new ObjectID(pid);
let res = await coll.insertOne({ content, owner, pid, reply: [], vote: 0 });
const res = await coll.insertOne({
content, owner, pid, reply: [], vote: 0,
});
return res.insertedId;
}
async function get(psid) {
psid = new ObjectID(psid);
let psdoc = await coll.findOne({ _id: psid });
const psdoc = await coll.findOne({ _id: psid });
if (!psdoc) throw new SolutionNotFoundError();
return psdoc;
}
async function getMany(query, sort, page, limit) {
return await coll.find(query).sort(sort).skip((page - 1) * limit).limit(limit).toArray();
function getMany(query, sort, page, limit) {
return coll.find(query).sort(sort).skip((page - 1) * limit).limit(limit)
.toArray();
}
async function edit(_id, content) {
validator.checkContent(content);
await coll.findOneAndUpdate({ _id }, { $set: { content } });
let psdoc = await get(_id);
const psdoc = await get(_id);
if (!psdoc) throw new SolutionNotFoundError(_id);
return psdoc;
}
async function del(psid) {
psid = new ObjectID(psid);
return await coll.deleteOne({ _id: psid });
function del(psid) {
return coll.deleteOne({ _id: psid });
}
async function count(query) {
return await coll.find(query).count();
function count(query) {
return coll.find(query).count();
}
function getMulti(pid) {
return coll.find({ pid }).sort({ vote: -1 });
}
async function reply(psid, owner, content) {
psid = new ObjectID(psid);
function reply(psid, owner, content) {
validator.checkContent(content);
return await coll.findOneAndUpdate({ _id: psid }, { $push: { reply: { content, owner, _id: new ObjectID() } } });
return coll.findOneAndUpdate(
{ _id: psid },
{ $push: { reply: { content, owner, _id: new ObjectID() } } },
);
}
async function getReply(psid, psrid) {
psid = new ObjectID(psid);
psrid = new ObjectID(psrid);
let psdoc = await coll.findOne({ _id: psid, reply: { $elemMatch: { _id: psrid } } });
const psdoc = await coll.findOne({ _id: psid, reply: { $elemMatch: { _id: psrid } } });
if (!psdoc) return [null, null];
for (let psrdoc of psdoc)
if (psrdoc._id == psrid)
return [psdoc, psrdoc];
for (const psrdoc of psdoc) if (psrdoc._id === psrid) return [psdoc, psrdoc];
return [psdoc, null];
}
async function editReply(psid, psrid, content) {
validator.checkContent(content);
psid = new ObjectID(psid);
psrid = new ObjectID(psrid);
let psdoc = await coll.findOne({ _id: psid, reply: { $elemMatch: { _id: psrid } } });
let reply = psdoc.reply;
for (let i in reply)
if (reply[i]._id == psrid) {
const psdoc = await coll.findOne({ _id: psid, reply: { $elemMatch: { _id: psrid } } });
const { reply } = psdoc;
for (const i in reply) {
if (reply[i]._id === psrid) {
reply[i].content = content;
break;
}
}
return await coll.findOneAndUpdate({ _id: psdoc._id }, { $set: { reply } });
}
async function delReply(psid, psrid) {
return await coll.findOneAndUpdate({ _id: psid }, { $pull: { reply: { _id: psrid } } });
function delReply(psid, psrid) {
return coll.findOneAndUpdate({ _id: psid }, { $pull: { reply: { _id: psrid } } });
}
async function vote(psid, uid, value) {
let pssdoc = await coll_status.findOne({ psid, uid });
if (pssdoc) await coll_status.deleteOne({ psid, uid });
await coll_status.insertOne({ psid, uid, vote: value });
let pssdoc = await collStatus.findOne({ psid, uid });
if (pssdoc) await collStatus.deleteOne({ psid, uid });
await collStatus.insertOne({ psid, uid, vote: value });
if (pssdoc) value += -pssdoc.vote;
await coll.findOneAndUpdate({ _id: psid }, { $inc: { vote: value } });
pssdoc = await coll_status.findOne({ psid, uid });
let psdoc = await coll.findOne({ _id: psid });
pssdoc = await collStatus.findOne({ psid, uid });
const psdoc = await coll.findOne({ _id: psid });
return [psdoc, pssdoc];
}
async function getListStatus(list, uid) {
let result = {};
let res = await coll_status.find({ uid, psid: { $in: list } }).toArray();
for (let i of res) result[i.psid] = i;
const result = {};
const res = await collStatus.find({ uid, psid: { $in: list } }).toArray();
for (const i of res) result[i.psid] = i;
return result;
}
module.exports = {
@@ -103,5 +104,5 @@ module.exports = {
editReply,
delReply,
vote,
getListStatus
};
getListStatus,
};
+15 -82
View File
@@ -1,42 +1,43 @@
const
db = require('../service/db.js'),
coll = db.collection('system');
const db = require('../service/db.js');
const coll = db.collection('system');
async function get(_id) {
let doc = await coll.findOne({ _id });
const doc = await coll.findOne({ _id });
if (doc) return doc.value;
return null;
}
async function update(_id, update, config) {
await coll.findOneAndUpdate({ _id }, update, config);
return this.get(_id);
return get(_id);
}
/**
* Increments the user counter.
* @returns {number} Integer value after increment.
*/
async function incUserCounter() {
return await this.update('userCounter', { $inc: { value: 1 } }, { upsert: true });
function incUserCounter() {
return update('userCounter', { $inc: { value: 1 } }, { upsert: true });
}
/**
* Increments the problem ID counter.
* @returns {number} Integer value before increment.
*/
async function incPidCounter() {
return await this.update('userCounter', { $inc: { value: 1 } }, { upsert: true });
function incPidCounter() {
return update('userCounter', { $inc: { value: 1 } }, { upsert: true });
}
async function acquireLock(name) {
let value = Math.floor(Math.random() * 0xFFFFFFFF);
const value = Math.floor(Math.random() * 0xFFFFFFFF);
try {
await coll.updateOne({ _id: 'lock_' + name, value: 0 }, { $set: { value } }, { upsert: true });
await coll.updateOne({ _id: `lock_${name}`, value: 0 }, { $set: { value } }, { upsert: true });
} catch (e) { return null; }
return value;
}
async function releaseLock(name, value) {
let result = await coll.updateOne({ _id: 'lock_' + name, value }, { $set: { value: 0 } });
const result = await coll.updateOne({ _id: `lock_${name}`, value }, { $set: { value: 0 } });
return !!result.matchedCount;
}
async function releaseLockAnyway(name) {
await coll.updateOne({ _id: 'lock_' + name }, { $set: { value: 0 } });
await coll.updateOne({ _id: `lock_${name}` }, { $set: { value: 0 } });
return true;
}
module.exports = {
@@ -46,73 +47,5 @@ module.exports = {
incUserCounter,
acquireLock,
releaseLock,
releaseLockAnyway
releaseLockAnyway,
};
/*
EXPECTED_DB_VERSION = 1
async def acquire_upgrade_lock():
lock = await acquire_lock('upgrade')
if not lock:
raise error.UpgradeLockAcquireError()
return lock
async def release_upgrade_lock(lock: int):
success = await release_lock('upgrade', lock)
if not success:
raise error.UpgradeLockReleaseError()
return True
@argmethod.wrap
async def release_upgrade_lock_anyway():
return await release_lock_anyway('upgrade')
@argmethod.wrap
async def get_db_version():
coll = db.coll('system')
doc = await coll.find_one({'_id': 'db_version'})
if doc is None:
return 0
else:
return doc['value']
async def set_db_version(version: int):
coll = db.coll('system')
result = await coll.update_one(filter={'_id': 'db_version'},
update={'$set': {'value': version}},
upsert=True)
return result.modified_count
async def ensure_db_version(allowed_version=None):
if allowed_version is None:
allowed_version = EXPECTED_DB_VERSION
current_version = await get_db_version()
if current_version != allowed_version:
raise error.DatabaseVersionMismatchError(current_version, allowed_version)
@argmethod.wrap
async def setup():
"""
Set up for fresh install
"""
coll = db.coll('system')
fdoc = await coll.find_one({'_id': 'user_counter'})
if fdoc:
# skip if not fresh install
return
await set_db_version(EXPECTED_DB_VERSION)
if __name__ == '__main__':
argmethod.invoke_by_args()
*/
+20 -17
View File
@@ -1,6 +1,6 @@
const
db = require('../service/db.js'),
coll = db.collection('token');
const db = require('../service/db.js');
const coll = db.collection('token');
module.exports = {
TYPE_SESSION: 0,
@@ -14,15 +14,16 @@ module.exports = {
* @returns {Array} token ID, token data
*/
async add(tokenType, expireSeconds, data) {
let now = new Date();
let str = String.random(32);
let res = await coll.insertOne(Object.assign({}, data, {
const now = new Date();
const str = String.random(32);
const res = await coll.insertOne({
...data,
_id: str,
tokenType,
createAt: now,
updateAt: now,
expireAt: new Date(now.getTime() + expireSeconds * 1000)
}));
expireAt: new Date(now.getTime() + expireSeconds * 1000),
});
return [str, res.ops];
},
@@ -45,12 +46,14 @@ module.exports = {
* @returns {object} The token document, or null.
*/
async update(tokenId, tokenType, expireSeconds, data) {
let now = new Date();
let res = await coll.findOneAndUpdate({ _id: tokenId, tokenType }, {
$set: Object.assign({
const now = new Date();
const res = await coll.findOneAndUpdate({ _id: tokenId, tokenType }, {
$set: {
updateAt: now,
expireAt: new Date(now.getTime() + expireSeconds * 1000)
}, data, { tokenType })
expireAt: new Date(now.getTime() + expireSeconds * 1000),
...data,
tokenType,
},
});
return res.value;
},
@@ -62,10 +65,10 @@ module.exports = {
* @returns {boolean} true if deleted, or false.
*/
async delete(tokenId, tokenType) {
let result = await coll.deleteOne({ _id: tokenId, tokenType });
const result = await coll.deleteOne({ _id: tokenId, tokenType });
return !!result.deletedCount;
},
async getMostRecentSessionByUid(uid) {
return await coll.findOne({ uid, token_type: this.TYPE_SESSION }, { sort: { updateAt: -1 } });
}
getMostRecentSessionByUid(uid) {
return coll.findOne({ uid, token_type: this.TYPE_SESSION }, { sort: { updateAt: -1 } });
},
};
+25 -27
View File
@@ -1,26 +1,27 @@
const
validator = require('../lib/validator'),
{ ValidationError, TrainingNotFoundError } = require('../error'),
db = require('../service/db.js'),
coll = db.collection('traning'),
coll_status = db.collection('training.status');
const validator = require('../lib/validator');
const { ValidationError, TrainingNotFoundError } = require('../error');
const db = require('../service/db.js');
const coll = db.collection('traning');
const collStatus = db.collection('training.status');
module.exports = {
SETTING_DIFFICULTY_ALGORITHM: 0,
SETTING_DIFFICULTY_ADMIN: 1,
SETTING_DIFFICULTY_AVERAGE: 2,
async add(title, content, owner_uid, dag = [], desc = '') {
validator.check_title(title);
validator.check_intro(content);
validator.check_description(desc);
for (let node of dag)
for (let nid of node['require_nids'])
if (nid >= node._id)
throw new ValidationError('dag');
async add(title, content, owner, dag = [], desc = '') {
validator.checkTitle(title);
validator.checkIntro(content);
validator.checkDescription(desc);
for (const node of dag) { for (const nid of node.require_nids) if (nid >= node._id) throw new ValidationError('dag'); }
return await coll.insertOne({
content, owner_uid,
dag, title, desc, enroll: 0
content,
owner,
dag,
title,
desc,
enroll: 0,
});
},
count: (query) => coll.find(query).count(),
@@ -28,24 +29,21 @@ module.exports = {
if ($set.title) validator.check_title($set.title);
if ($set.content) validator.check_intro($set.content);
if ($set.desc) validator.check_description($set.desc);
if ($set.dag)
for (let node of $set.dag)
for (let nid of node.require_nids)
if (nid >= node._id) throw new ValidationError('dag');
if ($set.dag) { for (const node of $set.dag) for (const nid of node.require_nids) if (nid >= node._id) throw new ValidationError('dag'); }
await coll.findOneAndUpdate({ tid }, { $set });
let tdoc = await coll.findOne({ tid });
const tdoc = await coll.findOne({ tid });
if (!tdoc) throw new TrainingNotFoundError(tid);
return tdoc;
},
async get(tid) {
let tdoc = await coll.findOne({ tid });
const tdoc = await coll.findOne({ tid });
if (!tdoc) throw new TrainingNotFoundError(tid);
return tdoc;
},
get_multi: (query) => coll.find( query),
get_multi_status: (query) => coll_status.find(query),
get_status: (tid, uid) => coll_status.findOne({ tid, uid }),
set_status: (tid, uid, $set) => coll_status.findOneAndUpdate({ tid, uid }, { $set })
get_multi: (query) => coll.find(query),
get_multi_status: (query) => collStatus.find(query),
get_status: (tid, uid) => collStatus.findOne({ tid, uid }),
set_status: (tid, uid, $set) => collStatus.findOneAndUpdate({ tid, uid }, { $set }),
};
@@ -66,4 +64,4 @@ async def get_dict(domainId, tids, *, fields=None):
fields=fields):
result[tdoc['doc_id']] = tdoc
return result
*/
*/
+51 -42
View File
@@ -1,11 +1,11 @@
const
system = require('./system'),
{ UserNotFoundError, UserAlreadyExistError } = require('../error'),
pwhash = require('../lib/pwhash'),
validator = require('../lib/validator'),
db = require('../service/db'),
coll = db.collection('user'),
coll_role = db.collection('role');
const system = require('./system');
const { UserNotFoundError, UserAlreadyExistError } = require('../error');
const pwhash = require('../lib/pwhash');
const validator = require('../lib/validator');
const db = require('../service/db');
const coll = db.collection('user');
const collRole = db.collection('role');
class USER {
constructor(user) {
@@ -24,75 +24,78 @@ class USER {
this.nAccept = user.nAccept || 0;
this.nSubmit = user.nSubmit || 0;
}
hasPerm(perm) {
return this.perm == '-' || (this.perm || '').includes(perm);
return this.perm === '-' || (this.perm || '').includes(perm);
}
checkPassword(password) {
return pwhash.check(password, this.salt, this.hash);
}
}
async function getById(_id) {
let udoc = await coll.findOne({ _id });
const udoc = await coll.findOne({ _id });
if (!udoc) throw new UserNotFoundError(_id);
let role = await coll_role.findOne({ _id: udoc.role || 'default' });
const role = await collRole.findOne({ _id: udoc.role || 'default' });
udoc.perm = role.perm;
return new USER(udoc);
}
async function getList(uids) {
let r = {};
for (let uid of uids)
r[uid] = await getById(uid);
const r = {};
for (const uid of uids) r[uid] = await getById(uid); // eslint-disable-line no-await-in-loop
return r;
}
async function getByUname(uname) {
let unameLower = uname.trim().toLowerCase();
let udoc = await coll.findOne({ unameLower });
const unameLower = uname.trim().toLowerCase();
const udoc = await coll.findOne({ unameLower });
if (!udoc) throw new UserNotFoundError(uname);
let role = await coll_role.findOne({ _id: udoc.role || 'default' });
const role = await collRole.findOne({ _id: udoc.role || 'default' });
udoc.perm = role.perm;
return new USER(udoc);
}
async function getByEmail(mail, ignoreMissing = false) {
let mailLower = mail.trim().toLowerCase();
let udoc = await coll.findOne({ mailLower });
const mailLower = mail.trim().toLowerCase();
const udoc = await coll.findOne({ mailLower });
if (!udoc) {
if (ignoreMissing) return null;
else throw new UserNotFoundError(mail);
throw new UserNotFoundError(mail);
}
let role = await coll_role.findOne({ _id: udoc.role || 'default' });
const role = await collRole.findOne({ _id: udoc.role || 'default' });
udoc.perm = role.perm;
return new USER(udoc);
}
async function setPassword(uid, password) {
function setPassword(uid, password) {
validator.checkPassword(password);
let salt = pwhash.salt();
return await coll.findOneAndUpdate({ _id: uid }, {
$set: { salt, hash: pwhash.hash(password, salt) }
const salt = pwhash.salt();
return coll.findOneAndUpdate({ _id: uid }, {
$set: { salt, hash: pwhash.hash(password, salt) },
});
}
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 });
}
function setEmail(uid, mail) {
validator.checkEmail(mail);
return setById(uid, { mail, mailLower: mail.trim().toLowerCase() });
}
async function changePassword(uid, currentPassword, newPassword) {
validator.checkPassword(newPassword);
let udoc = await getById(uid);
const udoc = await getById(uid);
udoc.checkPassword(currentPassword);
let salt = pwhash.salt();
const salt = pwhash.salt();
return await coll.findOneAndUpdate({
_id: udoc._id
_id: udoc._id,
}, {
$set: { salt, hash: pwhash.hash(newPassword, salt) }
$set: { salt, hash: pwhash.hash(newPassword, salt) },
});
}
async function create({ uid, mail, 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(mail);
let salt = pwhash.salt();
const salt = pwhash.salt();
if (!uid) uid = system.incUserCounter();
try {
await coll.insertOne({
@@ -108,10 +111,10 @@ async function create({ uid, mail, uname, password, regip = '127.0.0.1', role =
loginat: new Date(),
loginip: regip,
role,
gravatar: mail
gravatar: mail,
});
} catch (e) {
throw new UserAlreadyExistError([uid, uname, email]);
throw new UserAlreadyExistError([uid, uname, mail]);
}
}
@@ -120,8 +123,14 @@ function getMany(params) {
}
module.exports = {
changePassword, create, getByEmail,
getById, getByUname, getMany,
setById, setEmail,
setPassword, getList
};
changePassword,
create,
getByEmail,
getById,
getByUname,
getMany,
setById,
setEmail,
setPassword,
getList,
};