module.add(user,problem,record)

This commit is contained in:
masnn committed 2020-04-01 18:13:04 +08:00
commit d5b1f623f8
43 files changed
+5965

No files matched your search

+98
View File
@@ -0,0 +1,98 @@
const
{ MIDDLEWARE } = require('../service/server.js'),
user = require('../model/user'),
domain = require('../model/domain'),
blacklist = require('../model/blacklist'),
token = require('../model/token'),
options = require('../options'),
{
UserNotFoundError,
BlacklistedError,
PermissionError,
PrivilegeError
} = require('../error');
MIDDLEWARE(async (ctx, next) => {
try {
ctx.state.domainId = 'system';
let sid = ctx.cookies.get('sid');
let save = ctx.cookies.get('save');
let tokenType, expireSeconds;
if (save) {
tokenType = token.TYPE_SAVED_SESSION;
expireSeconds = options.session.saved_expire_seconds;
} else {
tokenType = token.TYPE_UNSAVED_SESSION;
expireSeconds = options.session.unsaved_expire_seconds;
}
ctx.session = sid ?
await token.update(sid, tokenType, expireSeconds, Object.assign({
update_ip: ctx.request.ip,
update_ua: ctx.request.headers['user-agent'] || ''
})) : { uid: 1 };
if (!ctx.session) ctx.session = { uid: 1 };
let bdoc = await blacklist.get(ctx.request.ip);
if (bdoc) throw new BlacklistedError(ctx.request.ip);
console.log(ctx.session.uid);
[ctx.state.user, ctx.state.domain] = await Promise.all([
user.getById(ctx.session.uid, ctx.state.domainId),
domain.get(ctx.state.domainId)
]);
if (!ctx.state.user) throw new UserNotFoundError(ctx.session.uid);
ctx.checkPerm = function (perm) {
for (let i in arguments) {
if (arguments[i] instanceof Array) {
let p = false;
for (let j in arguments)
if (ctx.state.user.hasPerm(arguments[i][j])) {
p = true;
break;
}
if (!p) throw new PermissionError([arguments[i]]);
} else {
if (ctx.state.user.hasPerm(arguments[i])) continue;
else throw new PermissionError([[arguments[i]]]);
}
}
};
ctx.checkPriv = function (priv) {
for (let i in arguments) {
if (arguments[i] instanceof Array) {
let p = false;
for (let j in arguments)
if (ctx.state.user.hasPriv(arguments[i][j])) {
p = true;
break;
}
if (!p) throw new PrivilegeError([arguments[i]]);
} else {
if (ctx.state.user.hasPriv(arguments[i])) continue;
else throw new PrivilegeError([[arguments[i]]]);
}
}
};
await next();
if (ctx.session.sid)
await token.update(ctx.session.sid, tokenType, expireSeconds, Object.assign({
updateIp: ctx.request.ip,
updateUa: ctx.request.headers['user-agent'] || ''
}));
else
[ctx.session.sid] = await token.add(tokenType, expireSeconds, Object.assign({
create_ip: ctx.request.ip,
create_ua: ctx.request.headers['user-agent'] || '',
update_ip: ctx.request.ip,
update_ua: ctx.request.headers['user-agent'] || ''
}, ctx.session));
let cookie = { domain: options.session.domain, secure: options.session.secure, httponly: true };
if (save) {
cookie.expires = ctx.session.expireAt, cookie.maxAge = expireSeconds;
ctx.cookies.set('save', 'true', cookie);
}
ctx.cookies.set('sid', ctx.session.sid, cookie);
} catch (e) {
console.error(e.message, e.params);
console.error(e.stack);
ctx.body = { error: { message: e.message, params: e.params, stack: e.stack } };
}
});
+5
View File
@@ -0,0 +1,5 @@
const { GET } = require('../service/server');
GET('/', async ctx => {
ctx.body = {};
});
+434
View File
@@ -0,0 +1,434 @@
const
{ GET, SOCKET } = require('../service/server'),
{ assertQueue, consume } = require('../service/queue'),
{ requirePriv } = require('./tools'),
record = require('../model/record'),
token = require('../model/token'),
user = require('../model/user'),
{ PRIV_JUDGE } = require('../privilege');
assertQueue('judge');
GET('/judge/noop', requirePriv(PRIV_JUDGE), async ctx => {
ctx.body = {};
});
SOCKET('/judge/conn', async conn => {
let sid = await new Promise((resolve, reject) => {
setTimeout(reject, 5000);
conn.once('data', async (data) => {
console.log(data);
resolve(data);
});
});
let session = await token.get(sid, token.TYPE_SESSION);
let u = await user.getById(session.uid);
if (!u.hasPriv(PRIV_JUDGE)) conn.close(1000, 'PrivilegeError');
let h = 1;
let interval = setInterval(() => {
if (!h) h.close();
h = 0;
conn.write('h');
}, 30000);
conn.on('data', (data) => {
data = JSON.parse(data);
h++;
if (data.event == 'next') {
// TODO (masnn)
} else if (data.event == 'end') {
// TODO (masnn)
}
});
consume('judge', async rid => {
rid = rid.toString();
let rdoc = await record.get(rid);
let data = {
event: 'judge',
rid, type: 0,
domainId: rdoc.domainId,
pid: rdoc.pid,
data: rdoc.data,
lang: rdoc.lang,
code: rdoc.code
};
conn.write(JSON.stringify(data));
});
conn.on('close', () => {
clearInterval(interval);
});
});
/*
@app.route('/p/category/{category:[^/]*}/random', 'problem_category_random')
class ProblemCategoryRandomHandler(base.Handler):
@base.requirePerm(builtin.PERM_VIEW_PROBLEM)
@base.get_argument
@base.route_argument
@base.sanitize
async def get(self, *, category: str):
if not self.has_perm(builtin.PERM_VIEW_PROBLEM_HIDDEN):
f = {'hidden': False}
else:
f = {}
query = ProblemCategoryHandler.build_query(category)
pid = await problem.get_random_id(self.domainId, **query, **f)
if pid:
self.json_or_redirect(self.reverse_url('problem_detail', pid=pid))
else:
self.json_or_redirect(self.referer_or_main)
@app.route('/p/{pid}/submit', 'problem_submit')
class ProblemSubmitHandler(base.Handler):
@base.requirePerm(builtin.PERM_SUBMIT_PROBLEM)
@base.route_argument
@base.sanitize
async def get(self, *, pid: document.convert_doc_id):
# TODO(twd2): check status, eg. test, hidden problem, ...
uid = self.user['_id'] if self.has_priv(builtin.PRIV_USER_PROFILE) else None
pdoc = await problem.get(self.domainId, pid, uid)
if pdoc.get('hidden', False):
self.check_perm(builtin.PERM_VIEW_PROBLEM_HIDDEN)
udoc, dudoc = await asyncio.gather(user.get_by_uid(pdoc['owner_uid']),
domain.get_user(self.domainId, pdoc['owner_uid']))
if uid == None:
rdocs = []
else:
# TODO(iceboy): needs to be in sync with contest_detail_problem_submit
rdocs = await record \
.get_user_in_problem_multi(uid, self.domainId, pdoc['doc_id']) \
.sort([('_id', -1)]) \
.limit(10) \
.to_list()
if not self.prefer_json:
path_components = self.build_path(
(self.translate('problem_main'), self.reverse_url('problem_main')),
(pdoc['title'], self.reverse_url('problem_detail', pid=pdoc['doc_id'])),
(self.translate('problem_submit'), None))
self.render('problem_submit.html', pdoc=pdoc, udoc=udoc, rdocs=rdocs, dudoc=dudoc,
page_title=pdoc['title'], path_components=path_components)
else:
self.json({'rdocs': rdocs})
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.requirePerm(builtin.PERM_SUBMIT_PROBLEM)
@base.route_argument
@base.post_argument
@base.require_csrf_token
@base.sanitize
@base.limitRate('add_record', 60, 100)
async def post(self, *, pid: document.convert_doc_id, lang: str, code: str):
# TODO(twd2): check status, eg. test, hidden problem, ...
pdoc = await problem.get(self.domainId, pid)
if pdoc.get('hidden', False):
self.check_perm(builtin.PERM_VIEW_PROBLEM_HIDDEN)
rid = await record.add(self.domainId, pdoc['doc_id'], constant.record.TYPE_SUBMISSION,
self.user['_id'], lang, code)
self.json_or_redirect(self.reverse_url('record_detail', rid=rid))
@app.route('/p/{pid}/pretest', 'problem_pretest')
class ProblemPretestHandler(base.Handler):
@base.requirePerm(builtin.PERM_SUBMIT_PROBLEM)
@base.route_argument
@base.post_argument
@base.require_csrf_token
@base.sanitize
@base.limitRate('add_record', 60, 100)
async def post(self, *, pid: document.convert_doc_id, lang: str, code: str,
data_input: str, data_output: str):
pdoc = await problem.get(self.domainId, pid)
# don't need to check hidden status
# create zip file, TODO(twd2): check file size
post = await self.request.post()
content = list(zip(post.getall('data_input'), post.getall('data_output')))
output_buffer = io.BytesIO()
zip_file = zipfile.ZipFile(output_buffer, 'a', zipfile.ZIP_DEFLATED)
config_content = str(len(content)) + '\n'
for i, (data_input, data_output) in enumerate(content):
input_file = 'input{0}.txt'.format(i)
output_file = 'output{0}.txt'.format(i)
config_content += '{0}|{1}|1|10|262144\n'.format(input_file, output_file)
zip_file.writestr('Input/{0}'.format(input_file), data_input)
zip_file.writestr('Output/{0}'.format(output_file), data_output)
zip_file.writestr('Config.ini', config_content)
# mark all files as created in Windows :p
for zfile in zip_file.filelist:
zfile.create_system = 0
zip_file.close()
fid = await fs.add_data('application/zip', output_buffer.getvalue())
output_buffer.close()
rid = await record.add(self.domainId, pdoc['doc_id'], constant.record.TYPE_PRETEST,
self.user['_id'], lang, code, fid)
self.json_or_redirect(self.reverse_url('record_detail', rid=rid))
@app.connection_route('/p/{pid}/pretest-conn', 'problem_pretest-conn')
class ProblemPretestConnection(record_handler.RecordVisibilityMixin, base.Connection):
async def on_open(self):
await super(ProblemPretestConnection, self).on_open()
self.pid = document.convert_doc_id(self.request.match_info['pid'])
bus.subscribe(self.on_record_change, ['record_change'])
async def on_record_change(self, e):
rdoc = e['value']
if rdoc['uid'] != self.user['_id'] or \
rdoc['domainId'] != self.domainId or rdoc['pid'] != self.pid:
return
# check permission for visibility: contest
if rdoc['tid']:
show_status, tdoc = await self.rdoc_contest_visible(rdoc)
if not show_status:
return
self.send(rdoc=rdoc)
async def on_close(self):
bus.unsubscribe(self.on_record_change)
@app.route('/p/{pid}/data', 'problem_data')
class ProblemDataHandler(base.Handler):
@base.route_argument
@base.sanitize
async def get(self, *, pid: document.convert_doc_id):
# Judges will have PRIV_READ_PROBLEM_DATA,
# domain administrators will have PERM_READ_PROBLEM_DATA,
# problem owner will have PERM_READ_PROBLEM_DATA_SELF.
pdoc = await problem.get(self.domainId, pid)
if type(pdoc['data']) is dict:
return self.redirect(self.reverse_url('problem_data',
domainId=pdoc['data']['domain'],
pid=pdoc['data']['pid']))
if (not self.own(pdoc, builtin.PERM_READ_PROBLEM_DATA_SELF)
and not self.has_perm(builtin.PERM_READ_PROBLEM_DATA)):
self.check_priv(builtin.PRIV_READ_PROBLEM_DATA)
fdoc = await problem.get_data(pdoc)
if not fdoc:
raise error.ProblemDataNotFoundError(self.domainId, pid)
self.redirect(options.cdn_prefix.rstrip('/') + \
self.reverse_url('fs_get', domainId=builtin.domainId_SYSTEM,
secret=fdoc['metadata']['secret']))
@app.route('/p/copy', 'problem_copy')
class ProblemCopyHandler(base.Handler):
MAX_PROBLEMS_PER_REQUEST = 20
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.requirePerm(builtin.PERM_CREATE_PROBLEM)
async def get(self):
self.render('problem_copy.html')
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.requirePerm(builtin.PERM_CREATE_PROBLEM)
@base.post_argument
@base.require_csrf_token
@base.sanitize
@base.limitRate('copy_problems', 30, 10)
async def post(self, *, src_domainId: str, src_pids: str,
numeric_pid: bool=False, hidden: bool=False):
src_ddoc, src_dudoc = await asyncio.gather(domain.get(src_domainId),
domain.get_user(src_domainId, self.user['_id']))
if not src_dudoc:
src_dudoc = {}
if not self.dudoc_has_perm(ddoc=src_ddoc, dudoc=src_dudoc, udoc=self.user,
perm=builtin.PERM_VIEW_PROBLEM):
# TODO: This is the source domain's PermissionError.
raise error.PermissionError(builtin.PERM_VIEW_PROBLEM)
src_pids = misc.dedupe(map(document.convert_doc_id, src_pids.replace('\r\n', '\n').split('\n')))
if len(src_pids) > self.MAX_PROBLEMS_PER_REQUEST:
raise error.BatchCopyLimitExceededError(self.MAX_PROBLEMS_PER_REQUEST, len(src_pids))
pdocs = await problem.get_multi(domainId=src_domainId, doc_id={'$in': src_pids}) \
.sort('doc_id', 1) \
.to_list()
exist_pids = [pdoc['doc_id'] for pdoc in pdocs]
if len(src_pids) != len(exist_pids):
for pid in src_pids:
if pid not in exist_pids:
raise error.ProblemNotFoundError(src_domainId, pid)
for pdoc in pdocs:
if pdoc.get('hidden', False):
if not self.dudoc_has_perm(ddoc=src_ddoc, dudoc=src_dudoc, udoc=self.user,
perm=builtin.PERM_VIEW_PROBLEM_HIDDEN):
# TODO: This is the source domain's PermissionError.
raise error.PermissionError(builtin.PERM_VIEW_PROBLEM_HIDDEN)
for pdoc in pdocs:
pid = None
if numeric_pid:
pid = await domain.inc_pid_counter(self.domainId)
await problem.copy(pdoc, self.domainId, self.user['_id'], pid, hidden)
self.redirect(self.reverse_url('problem_main'))
@app.route('/p/{pid}/edit', 'problem_edit')
class ProblemEditHandler(base.Handler):
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.route_argument
@base.sanitize
async def get(self, *, pid: document.convert_doc_id):
uid = self.user['_id'] if self.has_priv(builtin.PRIV_USER_PROFILE) else None
pdoc = await problem.get(self.domainId, pid, uid)
if not self.own(pdoc, builtin.PERM_EDIT_PROBLEM_SELF):
self.check_perm(builtin.PERM_EDIT_PROBLEM)
udoc, dudoc = await asyncio.gather(user.get_by_uid(pdoc['owner_uid']),
domain.get_user(self.domainId, pdoc['owner_uid']))
path_components = self.build_path(
(self.translate('problem_main'), self.reverse_url('problem_main')),
(pdoc['title'], self.reverse_url('problem_detail', pid=pdoc['doc_id'])),
(self.translate('problem_edit'), None))
self.render('problem_edit.html', pdoc=pdoc, udoc=udoc, dudoc=dudoc,
page_title=pdoc['title'], path_components=path_components)
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.route_argument
@base.post_argument
@base.require_csrf_token
@base.sanitize
async def post(self, *, pid: document.convert_doc_id, title: str, content: str):
pdoc = await problem.get(self.domainId, pid)
if not self.own(pdoc, builtin.PERM_EDIT_PROBLEM_SELF):
self.check_perm(builtin.PERM_EDIT_PROBLEM)
await problem.edit(self.domainId, pdoc['doc_id'], title=title, content=content)
self.json_or_redirect(self.reverse_url('problem_detail', pid=pid))
@app.route('/p/{pid}/settings', 'problem_settings')
class ProblemSettingsHandler(base.Handler):
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.route_argument
@base.sanitize
async def get(self, *, pid: document.convert_doc_id):
uid = self.user['_id'] if self.has_priv(builtin.PRIV_USER_PROFILE) else None
pdoc = await problem.get(self.domainId, pid, uid)
if not self.own(pdoc, builtin.PERM_EDIT_PROBLEM_SELF):
self.check_perm(builtin.PERM_EDIT_PROBLEM)
udoc, dudoc = await asyncio.gather(user.get_by_uid(pdoc['owner_uid']),
domain.get_user(self.domainId, pdoc['owner_uid']))
path_components = self.build_path(
(self.translate('problem_main'), self.reverse_url('problem_main')),
(pdoc['title'], self.reverse_url('problem_detail', pid=pdoc['doc_id'])),
(self.translate('problem_settings'), None))
self.render('problem_settings.html', pdoc=pdoc, udoc=udoc, dudoc=dudoc,
categories=problem.get_categories(),
page_title=pdoc['title'], path_components=path_components)
def split_tags(self, s):
s = s.replace(',', ',') # Chinese ', '
return list(filter(lambda _: _ != '', map(lambda _: _.strip(), s.split(','))))
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.route_argument
@base.post_argument
@base.require_csrf_token
@base.sanitize
async def post(self, *, pid: document.convert_doc_id, hidden: bool=False,
category: str, tag: str,
difficulty_setting: int, difficulty_admin: str=''):
pdoc = await problem.get(self.domainId, pid)
if not self.own(pdoc, builtin.PERM_EDIT_PROBLEM_SELF):
self.check_perm(builtin.PERM_EDIT_PROBLEM)
category = self.split_tags(category)
tag = self.split_tags(tag)
for c in category:
if not (c in builtin.PROBLEM_CATEGORIES
or c in builtin.PROBLEM_SUB_CATEGORIES):
raise error.ValidationError('category')
if difficulty_setting not in problem.SETTING_DIFFICULTY_RANGE:
raise error.ValidationError('difficulty_setting')
if difficulty_admin:
try:
difficulty_admin = int(difficulty_admin)
except ValueError:
raise error.ValidationError('difficulty_admin')
else:
difficulty_admin = None
await problem.edit(self.domainId, pdoc['doc_id'], hidden=hidden,
category=category, tag=tag,
difficulty_setting=difficulty_setting, difficulty_admin=difficulty_admin)
await job.difficulty.update_problem(self.domainId, pdoc['doc_id'])
self.json_or_redirect(self.reverse_url('problem_detail', pid=pid))
@app.route('/p/{pid}/upload', 'problem_upload')
class ProblemUploadHandler(base.Handler):
def get_content_type(self, filename):
if os.path.splitext(filename)[1].lower() != '.zip':
raise error.FileTypeNotAllowedError(filename)
return 'application/zip'
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.route_argument
@base.sanitize
async def get(self, *, pid: document.convert_doc_id):
pdoc = await problem.get(self.domainId, pid)
if not self.own(pdoc, builtin.PERM_EDIT_PROBLEM_SELF):
self.check_perm(builtin.PERM_EDIT_PROBLEM)
if (not self.own(pdoc, builtin.PERM_READ_PROBLEM_DATA_SELF)
and not self.has_perm(builtin.PERM_READ_PROBLEM_DATA)):
self.check_priv(builtin.PRIV_READ_PROBLEM_DATA)
md5 = await fs.get_md5(await problem.get_data(pdoc))
self.render('problem_upload.html', pdoc=pdoc, md5=md5)
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.route_argument
@base.multipart_argument
@base.require_csrf_token
@base.sanitize
async def post(self, *, pid: document.convert_doc_id, file: objectid.ObjectId):
pdoc = await problem.get(self.domainId, pid)
if not self.own(pdoc, builtin.PERM_EDIT_PROBLEM_SELF):
self.check_perm(builtin.PERM_EDIT_PROBLEM)
if (not self.own(pdoc, builtin.PERM_READ_PROBLEM_DATA_SELF)
and not self.has_perm(builtin.PERM_READ_PROBLEM_DATA)):
self.check_priv(builtin.PRIV_READ_PROBLEM_DATA)
if pdoc.get('data') and type(pdoc['data']) is objectid.ObjectId:
await fs.unlink(pdoc['data'])
await problem.set_data(self.domainId, pid, file)
self.json_or_redirect(self.url)
@app.route('/p/{pid}/statistics', 'problem_statistics')
class ProblemStatisticsHandler(base.Handler):
@base.route_argument
@base.sanitize
async def get(self, *, pid: document.convert_doc_id):
# TODO(twd2)
uid = self.user['_id'] if self.has_priv(builtin.PRIV_USER_PROFILE) else None
pdoc = await problem.get(self.domainId, pid, uid)
if pdoc.get('hidden', False):
self.check_perm(builtin.PERM_VIEW_PROBLEM_HIDDEN)
udoc, dudoc = await asyncio.gather(user.get_by_uid(pdoc['owner_uid']),
domain.get_user(self.domainId, pdoc['owner_uid']))
path_components = self.build_path(
(self.translate('problem_main'), self.reverse_url('problem_main')),
(pdoc['title'], self.reverse_url('problem_detail', pid=pdoc['doc_id'])),
(self.translate('problem_statistics'), None))
self.render('problem_statistics.html', pdoc=pdoc, udoc=udoc, dudoc=dudoc,
page_title=pdoc['title'], path_components=path_components)
@app.route('/p/search', 'problem_search')
class ProblemSearchHandler(base.Handler):
@base.get_argument
@base.route_argument
@base.sanitize
async def get(self, *, q: str):
q = q.strip()
if not q:
self.json_or_redirect(self.referer_or_main)
return
try:
pdoc = await problem.get(self.domainId, document.convert_doc_id(q))
except error.ProblemNotFoundError:
pdoc = None
if pdoc:
self.redirect(self.reverse_url('problem_detail', pid=pdoc['doc_id']))
return
self.redirect('http://cn.bing.com/search?q={0}+site%3A{1}' \
.format(parse.quote(q), parse.quote(options.url_prefix)))
*/
+448
View File
@@ -0,0 +1,448 @@
const
{ GET, POST } = require('../service/server'),
{ assertQueue, publish } = require('../service/queue'),
{ requirePerm, requirePriv } = require('./tools'),
validator = require('../lib/validator'),
problem = require('../model/problem'),
training = require('../model/training'),
contest = require('../model/contest'),
record = require('../model/record'),
domain = require('../model/domain'),
user = require('../model/user'),
{ NoProblemError } = require('../error'),
{ PRIV_USER_PROFILE } = require('../privilege'),
{
PERM_VIEW_PROBLEM,
PERM_VIEW_PROBLEM_HIDDEN,
PERM_VIEW_TRAINING,
PERM_VIEW_CONTEST,
PERM_VIEW_HOMEWORK,
PERM_SUBMIT_PROBLEM,
PERM_CREATE_PROBLEM
} = require('../permission');
const PROBLEMS_PER_PAGE = 100;
assertQueue('judge');
GET('/p', requirePerm(PERM_VIEW_PROBLEM), async ctx => {
let q = { domainId: ctx.state.domainId },
page = ctx.query.page || 1;
if (!ctx.state.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN)) q.hidden = false;
let pdocs = await problem.getMany(q, { pid: 1 }, page, PROBLEMS_PER_PAGE);
ctx.body = { code: 0, page, pdocs, category: '' };
});
GET('/problem/random', requirePerm(PERM_VIEW_PROBLEM), async ctx => {
let q = { domainId: ctx.state.domainId };
if (!ctx.state.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN)) q.hidden = false;
let pid = await problem.random(q);
if (!pid) throw new NoProblemError();
ctx.body = { pid };
});
POST('/p/:pid/submit', requirePerm(PERM_SUBMIT_PROBLEM), async ctx => {
let rid = await record.add({
creator: ctx.state.user._id,
lang: ctx.request.body.language,
code: ctx.request.body.code,
domainId: ctx.state.domainId,
pid: ctx.params.pid
});
await publish('judge', rid);
ctx.state.user.nSubmit++;
ctx.body = { rid };
});
GET('/p/:pid', requirePerm(PERM_VIEW_PROBLEM), async ctx => {
let uid = null,
pid = Number(ctx.params.pid);
if (ctx.state.user.hasPriv(PRIV_USER_PROFILE)) uid = ctx.state.user._id;
let pdoc = await problem.get({ domainId: ctx.state.domainId, pid, uid });
if (pdoc.hidden) ctx.checkPerm(PERM_VIEW_PROBLEM_HIDDEN);
let udoc = await user.getById(pdoc.owner);
ctx.body = { pdoc, udoc, title: pdoc.title };
});
POST('/problem/create',
requirePriv(PRIV_USER_PROFILE),
requirePerm(PERM_CREATE_PROBLEM),
async ctx => {
let { title, pid, content, hidden } = ctx.request.body;
validator.checkPid(pid);
pid = pid || await domain.incPidCounter(ctx.state.domainId);
await problem.add({ domainId: ctx.state.domainId, title, content, owner: ctx.state.user._id, pid, hidden });
ctx.body = { pid };
}
);
/*
@app.route('/p/category/{category:[^/]*}/random', 'problem_category_random')
class ProblemCategoryRandomHandler(base.Handler):
@base.requirePerm(builtin.PERM_VIEW_PROBLEM)
@base.get_argument
@base.route_argument
@base.sanitize
async def get(self, *, category: str):
if not self.has_perm(builtin.PERM_VIEW_PROBLEM_HIDDEN):
f = {'hidden': False}
else:
f = {}
query = ProblemCategoryHandler.build_query(category)
pid = await problem.get_random_id(self.domainId, **query, **f)
if pid:
self.json_or_redirect(self.reverse_url('problem_detail', pid=pid))
else:
self.json_or_redirect(self.referer_or_main)
@app.route('/p/{pid}/submit', 'problem_submit')
class ProblemSubmitHandler(base.Handler):
@base.requirePerm(builtin.PERM_SUBMIT_PROBLEM)
@base.route_argument
@base.sanitize
async def get(self, *, pid: document.convert_doc_id):
# TODO(twd2): check status, eg. test, hidden problem, ...
uid = self.user['_id'] if self.has_priv(builtin.PRIV_USER_PROFILE) else None
pdoc = await problem.get(self.domainId, pid, uid)
if pdoc.get('hidden', False):
self.check_perm(builtin.PERM_VIEW_PROBLEM_HIDDEN)
udoc, dudoc = await asyncio.gather(user.get_by_uid(pdoc['owner_uid']),
domain.get_user(self.domainId, pdoc['owner_uid']))
if uid == None:
rdocs = []
else:
# TODO(iceboy): needs to be in sync with contest_detail_problem_submit
rdocs = await record \
.get_user_in_problem_multi(uid, self.domainId, pdoc['doc_id']) \
.sort([('_id', -1)]) \
.limit(10) \
.to_list()
if not self.prefer_json:
path_components = self.build_path(
(self.translate('problem_main'), self.reverse_url('problem_main')),
(pdoc['title'], self.reverse_url('problem_detail', pid=pdoc['doc_id'])),
(self.translate('problem_submit'), None))
self.render('problem_submit.html', pdoc=pdoc, udoc=udoc, rdocs=rdocs, dudoc=dudoc,
page_title=pdoc['title'], path_components=path_components)
else:
self.json({'rdocs': rdocs})
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.requirePerm(builtin.PERM_SUBMIT_PROBLEM)
@base.route_argument
@base.post_argument
@base.require_csrf_token
@base.sanitize
@base.limitRate('add_record', 60, 100)
async def post(self, *, pid: document.convert_doc_id, lang: str, code: str):
# TODO(twd2): check status, eg. test, hidden problem, ...
pdoc = await problem.get(self.domainId, pid)
if pdoc.get('hidden', False):
self.check_perm(builtin.PERM_VIEW_PROBLEM_HIDDEN)
rid = await record.add(self.domainId, pdoc['doc_id'], constant.record.TYPE_SUBMISSION,
self.user['_id'], lang, code)
self.json_or_redirect(self.reverse_url('record_detail', rid=rid))
@app.route('/p/{pid}/pretest', 'problem_pretest')
class ProblemPretestHandler(base.Handler):
@base.requirePerm(builtin.PERM_SUBMIT_PROBLEM)
@base.route_argument
@base.post_argument
@base.require_csrf_token
@base.sanitize
@base.limitRate('add_record', 60, 100)
async def post(self, *, pid: document.convert_doc_id, lang: str, code: str,
data_input: str, data_output: str):
pdoc = await problem.get(self.domainId, pid)
# don't need to check hidden status
# create zip file, TODO(twd2): check file size
post = await self.request.post()
content = list(zip(post.getall('data_input'), post.getall('data_output')))
output_buffer = io.BytesIO()
zip_file = zipfile.ZipFile(output_buffer, 'a', zipfile.ZIP_DEFLATED)
config_content = str(len(content)) + '\n'
for i, (data_input, data_output) in enumerate(content):
input_file = 'input{0}.txt'.format(i)
output_file = 'output{0}.txt'.format(i)
config_content += '{0}|{1}|1|10|262144\n'.format(input_file, output_file)
zip_file.writestr('Input/{0}'.format(input_file), data_input)
zip_file.writestr('Output/{0}'.format(output_file), data_output)
zip_file.writestr('Config.ini', config_content)
# mark all files as created in Windows :p
for zfile in zip_file.filelist:
zfile.create_system = 0
zip_file.close()
fid = await fs.add_data('application/zip', output_buffer.getvalue())
output_buffer.close()
rid = await record.add(self.domainId, pdoc['doc_id'], constant.record.TYPE_PRETEST,
self.user['_id'], lang, code, fid)
self.json_or_redirect(self.reverse_url('record_detail', rid=rid))
@app.connection_route('/p/{pid}/pretest-conn', 'problem_pretest-conn')
class ProblemPretestConnection(record_handler.RecordVisibilityMixin, base.Connection):
async def on_open(self):
await super(ProblemPretestConnection, self).on_open()
self.pid = document.convert_doc_id(self.request.match_info['pid'])
bus.subscribe(self.on_record_change, ['record_change'])
async def on_record_change(self, e):
rdoc = e['value']
if rdoc['uid'] != self.user['_id'] or \
rdoc['domainId'] != self.domainId or rdoc['pid'] != self.pid:
return
# check permission for visibility: contest
if rdoc['tid']:
show_status, tdoc = await self.rdoc_contest_visible(rdoc)
if not show_status:
return
self.send(rdoc=rdoc)
async def on_close(self):
bus.unsubscribe(self.on_record_change)
@app.route('/p/{pid}/data', 'problem_data')
class ProblemDataHandler(base.Handler):
@base.route_argument
@base.sanitize
async def get(self, *, pid: document.convert_doc_id):
# Judges will have PRIV_READ_PROBLEM_DATA,
# domain administrators will have PERM_READ_PROBLEM_DATA,
# problem owner will have PERM_READ_PROBLEM_DATA_SELF.
pdoc = await problem.get(self.domainId, pid)
if type(pdoc['data']) is dict:
return self.redirect(self.reverse_url('problem_data',
domainId=pdoc['data']['domain'],
pid=pdoc['data']['pid']))
if (not self.own(pdoc, builtin.PERM_READ_PROBLEM_DATA_SELF)
and not self.has_perm(builtin.PERM_READ_PROBLEM_DATA)):
self.check_priv(builtin.PRIV_READ_PROBLEM_DATA)
fdoc = await problem.get_data(pdoc)
if not fdoc:
raise error.ProblemDataNotFoundError(self.domainId, pid)
self.redirect(options.cdn_prefix.rstrip('/') + \
self.reverse_url('fs_get', domainId=builtin.domainId_SYSTEM,
secret=fdoc['metadata']['secret']))
@app.route('/p/copy', 'problem_copy')
class ProblemCopyHandler(base.Handler):
MAX_PROBLEMS_PER_REQUEST = 20
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.requirePerm(builtin.PERM_CREATE_PROBLEM)
async def get(self):
self.render('problem_copy.html')
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.requirePerm(builtin.PERM_CREATE_PROBLEM)
@base.post_argument
@base.require_csrf_token
@base.sanitize
@base.limitRate('copy_problems', 30, 10)
async def post(self, *, src_domainId: str, src_pids: str,
numeric_pid: bool=False, hidden: bool=False):
src_ddoc, src_dudoc = await asyncio.gather(domain.get(src_domainId),
domain.get_user(src_domainId, self.user['_id']))
if not src_dudoc:
src_dudoc = {}
if not self.dudoc_has_perm(ddoc=src_ddoc, dudoc=src_dudoc, udoc=self.user,
perm=builtin.PERM_VIEW_PROBLEM):
# TODO: This is the source domain's PermissionError.
raise error.PermissionError(builtin.PERM_VIEW_PROBLEM)
src_pids = misc.dedupe(map(document.convert_doc_id, src_pids.replace('\r\n', '\n').split('\n')))
if len(src_pids) > self.MAX_PROBLEMS_PER_REQUEST:
raise error.BatchCopyLimitExceededError(self.MAX_PROBLEMS_PER_REQUEST, len(src_pids))
pdocs = await problem.get_multi(domainId=src_domainId, doc_id={'$in': src_pids}) \
.sort('doc_id', 1) \
.to_list()
exist_pids = [pdoc['doc_id'] for pdoc in pdocs]
if len(src_pids) != len(exist_pids):
for pid in src_pids:
if pid not in exist_pids:
raise error.ProblemNotFoundError(src_domainId, pid)
for pdoc in pdocs:
if pdoc.get('hidden', False):
if not self.dudoc_has_perm(ddoc=src_ddoc, dudoc=src_dudoc, udoc=self.user,
perm=builtin.PERM_VIEW_PROBLEM_HIDDEN):
# TODO: This is the source domain's PermissionError.
raise error.PermissionError(builtin.PERM_VIEW_PROBLEM_HIDDEN)
for pdoc in pdocs:
pid = None
if numeric_pid:
pid = await domain.inc_pid_counter(self.domainId)
await problem.copy(pdoc, self.domainId, self.user['_id'], pid, hidden)
self.redirect(self.reverse_url('problem_main'))
@app.route('/p/{pid}/edit', 'problem_edit')
class ProblemEditHandler(base.Handler):
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.route_argument
@base.sanitize
async def get(self, *, pid: document.convert_doc_id):
uid = self.user['_id'] if self.has_priv(builtin.PRIV_USER_PROFILE) else None
pdoc = await problem.get(self.domainId, pid, uid)
if not self.own(pdoc, builtin.PERM_EDIT_PROBLEM_SELF):
self.check_perm(builtin.PERM_EDIT_PROBLEM)
udoc, dudoc = await asyncio.gather(user.get_by_uid(pdoc['owner_uid']),
domain.get_user(self.domainId, pdoc['owner_uid']))
path_components = self.build_path(
(self.translate('problem_main'), self.reverse_url('problem_main')),
(pdoc['title'], self.reverse_url('problem_detail', pid=pdoc['doc_id'])),
(self.translate('problem_edit'), None))
self.render('problem_edit.html', pdoc=pdoc, udoc=udoc, dudoc=dudoc,
page_title=pdoc['title'], path_components=path_components)
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.route_argument
@base.post_argument
@base.require_csrf_token
@base.sanitize
async def post(self, *, pid: document.convert_doc_id, title: str, content: str):
pdoc = await problem.get(self.domainId, pid)
if not self.own(pdoc, builtin.PERM_EDIT_PROBLEM_SELF):
self.check_perm(builtin.PERM_EDIT_PROBLEM)
await problem.edit(self.domainId, pdoc['doc_id'], title=title, content=content)
self.json_or_redirect(self.reverse_url('problem_detail', pid=pid))
@app.route('/p/{pid}/settings', 'problem_settings')
class ProblemSettingsHandler(base.Handler):
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.route_argument
@base.sanitize
async def get(self, *, pid: document.convert_doc_id):
uid = self.user['_id'] if self.has_priv(builtin.PRIV_USER_PROFILE) else None
pdoc = await problem.get(self.domainId, pid, uid)
if not self.own(pdoc, builtin.PERM_EDIT_PROBLEM_SELF):
self.check_perm(builtin.PERM_EDIT_PROBLEM)
udoc, dudoc = await asyncio.gather(user.get_by_uid(pdoc['owner_uid']),
domain.get_user(self.domainId, pdoc['owner_uid']))
path_components = self.build_path(
(self.translate('problem_main'), self.reverse_url('problem_main')),
(pdoc['title'], self.reverse_url('problem_detail', pid=pdoc['doc_id'])),
(self.translate('problem_settings'), None))
self.render('problem_settings.html', pdoc=pdoc, udoc=udoc, dudoc=dudoc,
categories=problem.get_categories(),
page_title=pdoc['title'], path_components=path_components)
def split_tags(self, s):
s = s.replace(',', ',') # Chinese ', '
return list(filter(lambda _: _ != '', map(lambda _: _.strip(), s.split(','))))
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.route_argument
@base.post_argument
@base.require_csrf_token
@base.sanitize
async def post(self, *, pid: document.convert_doc_id, hidden: bool=False,
category: str, tag: str,
difficulty_setting: int, difficulty_admin: str=''):
pdoc = await problem.get(self.domainId, pid)
if not self.own(pdoc, builtin.PERM_EDIT_PROBLEM_SELF):
self.check_perm(builtin.PERM_EDIT_PROBLEM)
category = self.split_tags(category)
tag = self.split_tags(tag)
for c in category:
if not (c in builtin.PROBLEM_CATEGORIES
or c in builtin.PROBLEM_SUB_CATEGORIES):
raise error.ValidationError('category')
if difficulty_setting not in problem.SETTING_DIFFICULTY_RANGE:
raise error.ValidationError('difficulty_setting')
if difficulty_admin:
try:
difficulty_admin = int(difficulty_admin)
except ValueError:
raise error.ValidationError('difficulty_admin')
else:
difficulty_admin = None
await problem.edit(self.domainId, pdoc['doc_id'], hidden=hidden,
category=category, tag=tag,
difficulty_setting=difficulty_setting, difficulty_admin=difficulty_admin)
await job.difficulty.update_problem(self.domainId, pdoc['doc_id'])
self.json_or_redirect(self.reverse_url('problem_detail', pid=pid))
@app.route('/p/{pid}/upload', 'problem_upload')
class ProblemUploadHandler(base.Handler):
def get_content_type(self, filename):
if os.path.splitext(filename)[1].lower() != '.zip':
raise error.FileTypeNotAllowedError(filename)
return 'application/zip'
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.route_argument
@base.sanitize
async def get(self, *, pid: document.convert_doc_id):
pdoc = await problem.get(self.domainId, pid)
if not self.own(pdoc, builtin.PERM_EDIT_PROBLEM_SELF):
self.check_perm(builtin.PERM_EDIT_PROBLEM)
if (not self.own(pdoc, builtin.PERM_READ_PROBLEM_DATA_SELF)
and not self.has_perm(builtin.PERM_READ_PROBLEM_DATA)):
self.check_priv(builtin.PRIV_READ_PROBLEM_DATA)
md5 = await fs.get_md5(await problem.get_data(pdoc))
self.render('problem_upload.html', pdoc=pdoc, md5=md5)
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.route_argument
@base.multipart_argument
@base.require_csrf_token
@base.sanitize
async def post(self, *, pid: document.convert_doc_id, file: objectid.ObjectId):
pdoc = await problem.get(self.domainId, pid)
if not self.own(pdoc, builtin.PERM_EDIT_PROBLEM_SELF):
self.check_perm(builtin.PERM_EDIT_PROBLEM)
if (not self.own(pdoc, builtin.PERM_READ_PROBLEM_DATA_SELF)
and not self.has_perm(builtin.PERM_READ_PROBLEM_DATA)):
self.check_priv(builtin.PRIV_READ_PROBLEM_DATA)
if pdoc.get('data') and type(pdoc['data']) is objectid.ObjectId:
await fs.unlink(pdoc['data'])
await problem.set_data(self.domainId, pid, file)
self.json_or_redirect(self.url)
@app.route('/p/{pid}/statistics', 'problem_statistics')
class ProblemStatisticsHandler(base.Handler):
@base.route_argument
@base.sanitize
async def get(self, *, pid: document.convert_doc_id):
# TODO(twd2)
uid = self.user['_id'] if self.has_priv(builtin.PRIV_USER_PROFILE) else None
pdoc = await problem.get(self.domainId, pid, uid)
if pdoc.get('hidden', False):
self.check_perm(builtin.PERM_VIEW_PROBLEM_HIDDEN)
udoc, dudoc = await asyncio.gather(user.get_by_uid(pdoc['owner_uid']),
domain.get_user(self.domainId, pdoc['owner_uid']))
path_components = self.build_path(
(self.translate('problem_main'), self.reverse_url('problem_main')),
(pdoc['title'], self.reverse_url('problem_detail', pid=pdoc['doc_id'])),
(self.translate('problem_statistics'), None))
self.render('problem_statistics.html', pdoc=pdoc, udoc=udoc, dudoc=dudoc,
page_title=pdoc['title'], path_components=path_components)
@app.route('/p/search', 'problem_search')
class ProblemSearchHandler(base.Handler):
@base.get_argument
@base.route_argument
@base.sanitize
async def get(self, *, q: str):
q = q.strip()
if not q:
self.json_or_redirect(self.referer_or_main)
return
try:
pdoc = await problem.get(self.domainId, document.convert_doc_id(q))
except error.ProblemNotFoundError:
pdoc = None
if pdoc:
self.redirect(self.reverse_url('problem_detail', pid=pdoc['doc_id']))
return
self.redirect('http://cn.bing.com/search?q={0}+site%3A{1}' \
.format(parse.quote(q), parse.quote(options.url_prefix)))
*/
+403
View File
@@ -0,0 +1,403 @@
const
bson = require('bson'),
{ constants } = require('../options'),
{ GET, POST } = require('../service/server'),
{ publish } = require('../service/queue'),
{ requirePerm, requirePriv } = require('./tools'),
validator = require('../lib/validator'),
problem = require('../model/problem'),
record = require('../model/record'),
domain = require('../model/domain'),
user = require('../model/user'),
{ PRIV_USER_PROFILE } = require('../privilege'),
{ PERM_READ_RECORD_CODE, PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD } = require('../permission');
GET('/r', async ctx => {
let q = { domainId: ctx.state.domainId },
page = ctx.query.page || 1;
let rdocs = await record.getMany(q, { rid: 1 }, page, constants.RECORD_PER_PAGE);
ctx.body = { page, rdocs };
});
GET('/r/:rid', async ctx => {
let uid = ctx.state.user._id, rid = new bson.ObjectID(ctx.params.rid);
let rdoc = await record.get(rid);
if (rdoc.hidden) ctx.checkPerm(PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD);
if (rdoc.creator != uid && !ctx.state.user.hasPerm(PERM_READ_RECORD_CODE)) rdoc.code = null;
ctx.body = { rdoc };
});
/*
@app.route('/p/category/{category:[^/]*}/random', 'problem_category_random')
class ProblemCategoryRandomHandler(base.Handler):
@base.requirePerm(builtin.PERM_VIEW_PROBLEM)
@base.get_argument
@base.route_argument
@base.sanitize
async def get(self, *, category: str):
if not self.has_perm(builtin.PERM_VIEW_PROBLEM_HIDDEN):
f = {'hidden': False}
else:
f = {}
query = ProblemCategoryHandler.build_query(category)
pid = await problem.get_random_id(self.domainId, **query, **f)
if pid:
self.json_or_redirect(self.reverse_url('problem_detail', pid=pid))
else:
self.json_or_redirect(self.referer_or_main)
@app.route('/p/{pid}/submit', 'problem_submit')
class ProblemSubmitHandler(base.Handler):
@base.requirePerm(builtin.PERM_SUBMIT_PROBLEM)
@base.route_argument
@base.sanitize
async def get(self, *, pid: document.convert_doc_id):
# TODO(twd2): check status, eg. test, hidden problem, ...
uid = self.user['_id'] if self.has_priv(builtin.PRIV_USER_PROFILE) else None
pdoc = await problem.get(self.domainId, pid, uid)
if pdoc.get('hidden', False):
self.check_perm(builtin.PERM_VIEW_PROBLEM_HIDDEN)
udoc, dudoc = await asyncio.gather(user.get_by_uid(pdoc['owner_uid']),
domain.get_user(self.domainId, pdoc['owner_uid']))
if uid == None:
rdocs = []
else:
# TODO(iceboy): needs to be in sync with contest_detail_problem_submit
rdocs = await record \
.get_user_in_problem_multi(uid, self.domainId, pdoc['doc_id']) \
.sort([('_id', -1)]) \
.limit(10) \
.to_list()
if not self.prefer_json:
path_components = self.build_path(
(self.translate('problem_main'), self.reverse_url('problem_main')),
(pdoc['title'], self.reverse_url('problem_detail', pid=pdoc['doc_id'])),
(self.translate('problem_submit'), None))
self.render('problem_submit.html', pdoc=pdoc, udoc=udoc, rdocs=rdocs, dudoc=dudoc,
page_title=pdoc['title'], path_components=path_components)
else:
self.json({'rdocs': rdocs})
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.requirePerm(builtin.PERM_SUBMIT_PROBLEM)
@base.route_argument
@base.post_argument
@base.require_csrf_token
@base.sanitize
@base.limitRate('add_record', 60, 100)
async def post(self, *, pid: document.convert_doc_id, lang: str, code: str):
# TODO(twd2): check status, eg. test, hidden problem, ...
pdoc = await problem.get(self.domainId, pid)
if pdoc.get('hidden', False):
self.check_perm(builtin.PERM_VIEW_PROBLEM_HIDDEN)
rid = await record.add(self.domainId, pdoc['doc_id'], constant.record.TYPE_SUBMISSION,
self.user['_id'], lang, code)
self.json_or_redirect(self.reverse_url('record_detail', rid=rid))
@app.route('/p/{pid}/pretest', 'problem_pretest')
class ProblemPretestHandler(base.Handler):
@base.requirePerm(builtin.PERM_SUBMIT_PROBLEM)
@base.route_argument
@base.post_argument
@base.require_csrf_token
@base.sanitize
@base.limitRate('add_record', 60, 100)
async def post(self, *, pid: document.convert_doc_id, lang: str, code: str,
data_input: str, data_output: str):
pdoc = await problem.get(self.domainId, pid)
# don't need to check hidden status
# create zip file, TODO(twd2): check file size
post = await self.request.post()
content = list(zip(post.getall('data_input'), post.getall('data_output')))
output_buffer = io.BytesIO()
zip_file = zipfile.ZipFile(output_buffer, 'a', zipfile.ZIP_DEFLATED)
config_content = str(len(content)) + '\n'
for i, (data_input, data_output) in enumerate(content):
input_file = 'input{0}.txt'.format(i)
output_file = 'output{0}.txt'.format(i)
config_content += '{0}|{1}|1|10|262144\n'.format(input_file, output_file)
zip_file.writestr('Input/{0}'.format(input_file), data_input)
zip_file.writestr('Output/{0}'.format(output_file), data_output)
zip_file.writestr('Config.ini', config_content)
# mark all files as created in Windows :p
for zfile in zip_file.filelist:
zfile.create_system = 0
zip_file.close()
fid = await fs.add_data('application/zip', output_buffer.getvalue())
output_buffer.close()
rid = await record.add(self.domainId, pdoc['doc_id'], constant.record.TYPE_PRETEST,
self.user['_id'], lang, code, fid)
self.json_or_redirect(self.reverse_url('record_detail', rid=rid))
@app.connection_route('/p/{pid}/pretest-conn', 'problem_pretest-conn')
class ProblemPretestConnection(record_handler.RecordVisibilityMixin, base.Connection):
async def on_open(self):
await super(ProblemPretestConnection, self).on_open()
self.pid = document.convert_doc_id(self.request.match_info['pid'])
bus.subscribe(self.on_record_change, ['record_change'])
async def on_record_change(self, e):
rdoc = e['value']
if rdoc['uid'] != self.user['_id'] or \
rdoc['domainId'] != self.domainId or rdoc['pid'] != self.pid:
return
# check permission for visibility: contest
if rdoc['tid']:
show_status, tdoc = await self.rdoc_contest_visible(rdoc)
if not show_status:
return
self.send(rdoc=rdoc)
async def on_close(self):
bus.unsubscribe(self.on_record_change)
@app.route('/p/{pid}/data', 'problem_data')
class ProblemDataHandler(base.Handler):
@base.route_argument
@base.sanitize
async def get(self, *, pid: document.convert_doc_id):
# Judges will have PRIV_READ_PROBLEM_DATA,
# domain administrators will have PERM_READ_PROBLEM_DATA,
# problem owner will have PERM_READ_PROBLEM_DATA_SELF.
pdoc = await problem.get(self.domainId, pid)
if type(pdoc['data']) is dict:
return self.redirect(self.reverse_url('problem_data',
domainId=pdoc['data']['domain'],
pid=pdoc['data']['pid']))
if (not self.own(pdoc, builtin.PERM_READ_PROBLEM_DATA_SELF)
and not self.has_perm(builtin.PERM_READ_PROBLEM_DATA)):
self.check_priv(builtin.PRIV_READ_PROBLEM_DATA)
fdoc = await problem.get_data(pdoc)
if not fdoc:
raise error.ProblemDataNotFoundError(self.domainId, pid)
self.redirect(options.cdn_prefix.rstrip('/') + \
self.reverse_url('fs_get', domainId=builtin.domainId_SYSTEM,
secret=fdoc['metadata']['secret']))
@app.route('/p/copy', 'problem_copy')
class ProblemCopyHandler(base.Handler):
MAX_PROBLEMS_PER_REQUEST = 20
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.requirePerm(builtin.PERM_CREATE_PROBLEM)
async def get(self):
self.render('problem_copy.html')
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.requirePerm(builtin.PERM_CREATE_PROBLEM)
@base.post_argument
@base.require_csrf_token
@base.sanitize
@base.limitRate('copy_problems', 30, 10)
async def post(self, *, src_domainId: str, src_pids: str,
numeric_pid: bool=False, hidden: bool=False):
src_ddoc, src_dudoc = await asyncio.gather(domain.get(src_domainId),
domain.get_user(src_domainId, self.user['_id']))
if not src_dudoc:
src_dudoc = {}
if not self.dudoc_has_perm(ddoc=src_ddoc, dudoc=src_dudoc, udoc=self.user,
perm=builtin.PERM_VIEW_PROBLEM):
# TODO: This is the source domain's PermissionError.
raise error.PermissionError(builtin.PERM_VIEW_PROBLEM)
src_pids = misc.dedupe(map(document.convert_doc_id, src_pids.replace('\r\n', '\n').split('\n')))
if len(src_pids) > self.MAX_PROBLEMS_PER_REQUEST:
raise error.BatchCopyLimitExceededError(self.MAX_PROBLEMS_PER_REQUEST, len(src_pids))
pdocs = await problem.get_multi(domainId=src_domainId, doc_id={'$in': src_pids}) \
.sort('doc_id', 1) \
.to_list()
exist_pids = [pdoc['doc_id'] for pdoc in pdocs]
if len(src_pids) != len(exist_pids):
for pid in src_pids:
if pid not in exist_pids:
raise error.ProblemNotFoundError(src_domainId, pid)
for pdoc in pdocs:
if pdoc.get('hidden', False):
if not self.dudoc_has_perm(ddoc=src_ddoc, dudoc=src_dudoc, udoc=self.user,
perm=builtin.PERM_VIEW_PROBLEM_HIDDEN):
# TODO: This is the source domain's PermissionError.
raise error.PermissionError(builtin.PERM_VIEW_PROBLEM_HIDDEN)
for pdoc in pdocs:
pid = None
if numeric_pid:
pid = await domain.inc_pid_counter(self.domainId)
await problem.copy(pdoc, self.domainId, self.user['_id'], pid, hidden)
self.redirect(self.reverse_url('problem_main'))
@app.route('/p/{pid}/edit', 'problem_edit')
class ProblemEditHandler(base.Handler):
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.route_argument
@base.sanitize
async def get(self, *, pid: document.convert_doc_id):
uid = self.user['_id'] if self.has_priv(builtin.PRIV_USER_PROFILE) else None
pdoc = await problem.get(self.domainId, pid, uid)
if not self.own(pdoc, builtin.PERM_EDIT_PROBLEM_SELF):
self.check_perm(builtin.PERM_EDIT_PROBLEM)
udoc, dudoc = await asyncio.gather(user.get_by_uid(pdoc['owner_uid']),
domain.get_user(self.domainId, pdoc['owner_uid']))
path_components = self.build_path(
(self.translate('problem_main'), self.reverse_url('problem_main')),
(pdoc['title'], self.reverse_url('problem_detail', pid=pdoc['doc_id'])),
(self.translate('problem_edit'), None))
self.render('problem_edit.html', pdoc=pdoc, udoc=udoc, dudoc=dudoc,
page_title=pdoc['title'], path_components=path_components)
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.route_argument
@base.post_argument
@base.require_csrf_token
@base.sanitize
async def post(self, *, pid: document.convert_doc_id, title: str, content: str):
pdoc = await problem.get(self.domainId, pid)
if not self.own(pdoc, builtin.PERM_EDIT_PROBLEM_SELF):
self.check_perm(builtin.PERM_EDIT_PROBLEM)
await problem.edit(self.domainId, pdoc['doc_id'], title=title, content=content)
self.json_or_redirect(self.reverse_url('problem_detail', pid=pid))
@app.route('/p/{pid}/settings', 'problem_settings')
class ProblemSettingsHandler(base.Handler):
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.route_argument
@base.sanitize
async def get(self, *, pid: document.convert_doc_id):
uid = self.user['_id'] if self.has_priv(builtin.PRIV_USER_PROFILE) else None
pdoc = await problem.get(self.domainId, pid, uid)
if not self.own(pdoc, builtin.PERM_EDIT_PROBLEM_SELF):
self.check_perm(builtin.PERM_EDIT_PROBLEM)
udoc, dudoc = await asyncio.gather(user.get_by_uid(pdoc['owner_uid']),
domain.get_user(self.domainId, pdoc['owner_uid']))
path_components = self.build_path(
(self.translate('problem_main'), self.reverse_url('problem_main')),
(pdoc['title'], self.reverse_url('problem_detail', pid=pdoc['doc_id'])),
(self.translate('problem_settings'), None))
self.render('problem_settings.html', pdoc=pdoc, udoc=udoc, dudoc=dudoc,
categories=problem.get_categories(),
page_title=pdoc['title'], path_components=path_components)
def split_tags(self, s):
s = s.replace(',', ',') # Chinese ', '
return list(filter(lambda _: _ != '', map(lambda _: _.strip(), s.split(','))))
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.route_argument
@base.post_argument
@base.require_csrf_token
@base.sanitize
async def post(self, *, pid: document.convert_doc_id, hidden: bool=False,
category: str, tag: str,
difficulty_setting: int, difficulty_admin: str=''):
pdoc = await problem.get(self.domainId, pid)
if not self.own(pdoc, builtin.PERM_EDIT_PROBLEM_SELF):
self.check_perm(builtin.PERM_EDIT_PROBLEM)
category = self.split_tags(category)
tag = self.split_tags(tag)
for c in category:
if not (c in builtin.PROBLEM_CATEGORIES
or c in builtin.PROBLEM_SUB_CATEGORIES):
raise error.ValidationError('category')
if difficulty_setting not in problem.SETTING_DIFFICULTY_RANGE:
raise error.ValidationError('difficulty_setting')
if difficulty_admin:
try:
difficulty_admin = int(difficulty_admin)
except ValueError:
raise error.ValidationError('difficulty_admin')
else:
difficulty_admin = None
await problem.edit(self.domainId, pdoc['doc_id'], hidden=hidden,
category=category, tag=tag,
difficulty_setting=difficulty_setting, difficulty_admin=difficulty_admin)
await job.difficulty.update_problem(self.domainId, pdoc['doc_id'])
self.json_or_redirect(self.reverse_url('problem_detail', pid=pid))
@app.route('/p/{pid}/upload', 'problem_upload')
class ProblemUploadHandler(base.Handler):
def get_content_type(self, filename):
if os.path.splitext(filename)[1].lower() != '.zip':
raise error.FileTypeNotAllowedError(filename)
return 'application/zip'
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.route_argument
@base.sanitize
async def get(self, *, pid: document.convert_doc_id):
pdoc = await problem.get(self.domainId, pid)
if not self.own(pdoc, builtin.PERM_EDIT_PROBLEM_SELF):
self.check_perm(builtin.PERM_EDIT_PROBLEM)
if (not self.own(pdoc, builtin.PERM_READ_PROBLEM_DATA_SELF)
and not self.has_perm(builtin.PERM_READ_PROBLEM_DATA)):
self.check_priv(builtin.PRIV_READ_PROBLEM_DATA)
md5 = await fs.get_md5(await problem.get_data(pdoc))
self.render('problem_upload.html', pdoc=pdoc, md5=md5)
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.route_argument
@base.multipart_argument
@base.require_csrf_token
@base.sanitize
async def post(self, *, pid: document.convert_doc_id, file: objectid.ObjectId):
pdoc = await problem.get(self.domainId, pid)
if not self.own(pdoc, builtin.PERM_EDIT_PROBLEM_SELF):
self.check_perm(builtin.PERM_EDIT_PROBLEM)
if (not self.own(pdoc, builtin.PERM_READ_PROBLEM_DATA_SELF)
and not self.has_perm(builtin.PERM_READ_PROBLEM_DATA)):
self.check_priv(builtin.PRIV_READ_PROBLEM_DATA)
if pdoc.get('data') and type(pdoc['data']) is objectid.ObjectId:
await fs.unlink(pdoc['data'])
await problem.set_data(self.domainId, pid, file)
self.json_or_redirect(self.url)
@app.route('/p/{pid}/statistics', 'problem_statistics')
class ProblemStatisticsHandler(base.Handler):
@base.route_argument
@base.sanitize
async def get(self, *, pid: document.convert_doc_id):
# TODO(twd2)
uid = self.user['_id'] if self.has_priv(builtin.PRIV_USER_PROFILE) else None
pdoc = await problem.get(self.domainId, pid, uid)
if pdoc.get('hidden', False):
self.check_perm(builtin.PERM_VIEW_PROBLEM_HIDDEN)
udoc, dudoc = await asyncio.gather(user.get_by_uid(pdoc['owner_uid']),
domain.get_user(self.domainId, pdoc['owner_uid']))
path_components = self.build_path(
(self.translate('problem_main'), self.reverse_url('problem_main')),
(pdoc['title'], self.reverse_url('problem_detail', pid=pdoc['doc_id'])),
(self.translate('problem_statistics'), None))
self.render('problem_statistics.html', pdoc=pdoc, udoc=udoc, dudoc=dudoc,
page_title=pdoc['title'], path_components=path_components)
@app.route('/p/search', 'problem_search')
class ProblemSearchHandler(base.Handler):
@base.get_argument
@base.route_argument
@base.sanitize
async def get(self, *, q: str):
q = q.strip()
if not q:
self.json_or_redirect(self.referer_or_main)
return
try:
pdoc = await problem.get(self.domainId, document.convert_doc_id(q))
except error.ProblemNotFoundError:
pdoc = None
if pdoc:
self.redirect(self.reverse_url('problem_detail', pid=pdoc['doc_id']))
return
self.redirect('http://cn.bing.com/search?q={0}+site%3A{1}' \
.format(parse.quote(q), parse.quote(options.url_prefix)))
*/
+184
View File
@@ -0,0 +1,184 @@
/*
@app.route('/p/{pid}/solution', 'problem_solution')
class ProblemSolutionHandler(base.OperationHandler):
SOLUTIONS_PER_PAGE = 20
@base.requirePerm(builtin.PERM_VIEW_PROBLEM_SOLUTION)
@base.get_argument
@base.route_argument
@base.sanitize
async def get(self, *, pid: document.convert_doc_id, page: int=1):
uid = self.user['_id'] if self.has_priv(builtin.PRIV_USER_PROFILE) else None
pdoc = await problem.get(self.domainId, pid, uid)
if pdoc.get('hidden', False):
self.check_perm(builtin.PERM_VIEW_PROBLEM_HIDDEN)
psdocs, pcount, pscount = await pagination.paginate(
problem.get_multi_solution(self.domainId, pdoc['doc_id']),
page, self.SOLUTIONS_PER_PAGE)
uids = {pdoc['owner_uid']}
uids.update(psdoc['owner_uid'] for psdoc in psdocs)
for psdoc in psdocs:
if 'reply' in psdoc:
uids.update(psrdoc['owner_uid'] for psrdoc in psdoc['reply'])
udict, dudict, pssdict = await asyncio.gather(
user.get_dict(uids),
domain.get_dict_user_by_uid(self.domainId, uids),
problem.get_dict_solution_status(
self.domainId, (psdoc['doc_id'] for psdoc in psdocs), self.user['_id']))
dudict[self.user['_id']] = self.domain_user
path_components = self.build_path(
(self.translate('problem_main'), self.reverse_url('problem_main')),
(pdoc['title'], self.reverse_url('problem_detail', pid=pdoc['doc_id'])),
(self.translate('problem_solution'), None))
self.render('problem_solution.html', path_components=path_components,
pdoc=pdoc, psdocs=psdocs, page=page, pcount=pcount, pscount=pscount,
udict=udict, dudict=dudict, pssdict=pssdict)
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.requirePerm(builtin.PERM_CREATE_PROBLEM_SOLUTION)
@base.route_argument
@base.require_csrf_token
@base.sanitize
async def post_submit(self, *, pid: document.convert_doc_id, content: str):
pdoc = await problem.get(self.domainId, pid)
if pdoc.get('hidden', False):
self.check_perm(builtin.PERM_VIEW_PROBLEM_HIDDEN)
await problem.add_solution(self.domainId, pdoc['doc_id'], self.user['_id'], content)
self.json_or_redirect(self.url)
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.route_argument
@base.require_csrf_token
@base.sanitize
async def post_edit_solution(self, *, pid: document.convert_doc_id,
psid: document.convert_doc_id, content: str):
pdoc = await problem.get(self.domainId, pid)
if pdoc.get('hidden', False):
self.check_perm(builtin.PERM_VIEW_PROBLEM_HIDDEN)
psdoc = await problem.get_solution(self.domainId, psid, pdoc['doc_id'])
if not self.own(psdoc, builtin.PERM_EDIT_PROBLEM_SOLUTION_SELF):
self.check_perm(builtin.PERM_EDIT_PROBLEM_SOLUTION)
psdoc = await problem.set_solution(self.domainId, psdoc['doc_id'],
content=content)
self.json_or_redirect(self.url)
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.route_argument
@base.require_csrf_token
@base.sanitize
async def post_delete_solution(self, *, pid: document.convert_doc_id,
psid: document.convert_doc_id):
pdoc = await problem.get(self.domainId, pid)
if pdoc.get('hidden', False):
self.check_perm(builtin.PERM_VIEW_PROBLEM_HIDDEN)
psdoc = await problem.get_solution(self.domainId, psid, pdoc['doc_id'])
if not self.own(psdoc, builtin.PERM_DELETE_PROBLEM_SOLUTION_SELF):
self.check_perm(builtin.PERM_DELETE_PROBLEM_SOLUTION)
await oplog.add(self.user['_id'], oplog.TYPE_DELETE_DOCUMENT, doc=psdoc)
await problem.delete_solution(self.domainId, psdoc['doc_id'])
self.json_or_redirect(self.url)
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.route_argument
@base.require_csrf_token
@base.sanitize
async def post_edit_reply(self, *, pid: document.convert_doc_id,
psid: document.convert_doc_id, psrid: document.convert_doc_id,
content: str):
pdoc = await problem.get(self.domainId, pid)
if pdoc.get('hidden', False):
self.check_perm(builtin.PERM_VIEW_PROBLEM_HIDDEN)
psdoc, psrdoc = await problem.get_solution_reply(self.domainId, psid, psrid)
if not psdoc or psdoc['parent_doc_id'] != pdoc['doc_id']:
raise error.DocumentNotFoundError(self.domainId, document.TYPE_PROBLEM_SOLUTION, psid)
if not self.own(psrdoc, builtin.PERM_EDIT_PROBLEM_SOLUTION_REPLY_SELF):
self.check_perm(builtin.PERM_EDIT_PROBLEM_SOLUTION_REPLY)
await problem.edit_solution_reply(self.domainId, psid, psrid, content)
self.json_or_redirect(self.url)
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.route_argument
@base.require_csrf_token
@base.sanitize
async def post_delete_reply(self, *, pid: document.convert_doc_id,
psid: document.convert_doc_id, psrid: document.convert_doc_id):
pdoc = await problem.get(self.domainId, pid)
if pdoc.get('hidden', False):
self.check_perm(builtin.PERM_VIEW_PROBLEM_HIDDEN)
psdoc, psrdoc = await problem.get_solution_reply(self.domainId, psid, psrid)
if not psdoc or psdoc['parent_doc_id'] != pdoc['doc_id']:
raise error.DocumentNotFoundError(self.domainId, document.TYPE_PROBLEM_SOLUTION, psid)
if not self.own(psrdoc, builtin.PERM_DELETE_PROBLEM_SOLUTION_REPLY_SELF):
self.check_perm(builtin.PERM_DELETE_PROBLEM_SOLUTION_REPLY)
await oplog.add(self.user['_id'], oplog.TYPE_DELETE_SUB_DOCUMENT, sub_doc=psrdoc,
doc_type=psdoc['doc_type'], doc_id=psdoc['doc_id'])
await problem.delete_solution_reply(self.domainId, psid, psrid)
self.json_or_redirect(self.url)
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.requirePerm(builtin.PERM_VOTE_PROBLEM_SOLUTION)
@base.route_argument
@base.require_csrf_token
@base.sanitize
async def upvote_downvote(self, *,
pid: document.convert_doc_id,
psid: document.convert_doc_id,
value: int):
pdoc = await problem.get(self.domainId, pid)
if pdoc.get('hidden', False):
self.check_perm(builtin.PERM_VIEW_PROBLEM_HIDDEN)
psdoc = await problem.get_solution(self.domainId, psid, pdoc['doc_id'])
psdoc, pssdoc = await problem.vote_solution(self.domainId, psdoc['doc_id'],
self.user['_id'], value)
self.json_or_redirect(self.url, vote=psdoc['vote'], user_vote=pssdoc['vote'])
post_upvote = functools.partialmethod(upvote_downvote, value=1)
post_downvote = functools.partialmethod(upvote_downvote, value=-1)
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.requirePerm(builtin.PERM_REPLY_PROBLEM_SOLUTION)
@base.route_argument
@base.require_csrf_token
@base.sanitize
async def post_reply(self, *,
pid: document.convert_doc_id,
psid: document.convert_doc_id,
content: str):
pdoc = await problem.get(self.domainId, pid)
if pdoc.get('hidden', False):
self.check_perm(builtin.PERM_VIEW_PROBLEM_HIDDEN)
psdoc = await problem.get_solution(self.domainId, psid, pdoc['doc_id'])
await problem.reply_solution(self.domainId, psdoc['doc_id'], self.user['_id'], content)
self.json_or_redirect(self.url)
@app.route('/p/{pid}/solution/{psid:\w{24}}/raw', 'problem_solution_raw')
class ProblemSolutionRawHandler(base.Handler):
@base.requirePerm(builtin.PERM_VIEW_PROBLEM_SOLUTION)
@base.route_argument
@base.sanitize
async def get(self, *, pid: document.convert_doc_id, psid: document.convert_doc_id):
pdoc = await problem.get(self.domainId, pid)
if pdoc.get('hidden', False):
self.check_perm(builtin.PERM_VIEW_PROBLEM_HIDDEN)
psdoc = await problem.get_solution(self.domainId, psid, pdoc['doc_id'])
self.response.content_type = 'text/markdown'
self.response.text = psdoc['content']
@app.route('/p/{pid}/solution/{psid:\w{24}}/{psrid:\w{24}}/raw', 'problem_solution_reply_raw')
class ProblemSolutionReplyRawHandler(base.Handler):
@base.requirePerm(builtin.PERM_VIEW_PROBLEM_SOLUTION)
@base.route_argument
@base.sanitize
async def get(self, *, pid: document.convert_doc_id, psid: document.convert_doc_id,
psrid: objectid.ObjectId):
pdoc = await problem.get(self.domainId, pid)
if pdoc.get('hidden', False):
self.check_perm(builtin.PERM_VIEW_PROBLEM_HIDDEN)
psdoc, psrdoc = await problem.get_solution_reply(self.domainId, psid, psrid)
if not psdoc or psdoc['parent_doc_id'] != pdoc['doc_id']:
raise error.DocumentNotFoundError(self.domainId, document.TYPE_PROBLEM_SOLUTION, psid)
self.response.content_type = 'text/markdown'
self.response.text = psrdoc['content']
*/
+50
View File
@@ -0,0 +1,50 @@
const
opcount = require('../model/opcount'),
{ PermissionError, PrivilegeError } = require('../error');
module.exports = {
requirePerm(perm) {
return async (ctx, next) => {
for (let i in arguments) {
if (arguments[i] instanceof Array) {
let p = false;
for (let j in arguments)
if (ctx.state.user.hasPerm(arguments[i][j])) {
p = true;
break;
}
if (!p) throw new PermissionError([arguments[i]]);
} else {
if (ctx.state.user.hasPerm(arguments[i])) continue;
else throw new PermissionError([arguments[i]]);
}
}
await next();
};
},
requirePriv(priv) {
return async (ctx, next) => {
for (let i in arguments) {
if (arguments[i] instanceof Array) {
let p = false;
for (let j in arguments)
if (ctx.state.user.hasPriv(arguments[i][j])) {
p = true;
break;
}
if (!p) throw new PrivilegeError([arguments[i]]);
} else {
if (ctx.state.user.hasPriv(arguments[i])) continue;
else throw new PrivilegeError([arguments[i]]);
}
}
await next();
};
},
limitRate(op, period_secs, max_operations) {
return async (ctx, next) => {
await opcount.inc(op, ctx.request.ip, period_secs, max_operations);
await next();
};
}
};
+182
View File
@@ -0,0 +1,182 @@
const
{ GET, POST } = require('../service/server.js'),
user = require('../model/user'),
token = require('../model/token'),
system = require('../model/system'),
mail = require('../lib/mail'),
validator = require('../lib/validator'),
options = require('../options'),
{ PRIV_REGISTER_USER, PRIV_USER_PROFILE } = require('../privilege'),
{ requirePriv, limitRate } = require('./tools'),
{ UserAlreadyExistError, InvalidTokenError, VerifyPasswordError, UserNotFoundError, LoginError } = require('../error');
GET('/user', async ctx => {
let udoc = await user.getById(ctx.session.uid);
ctx.body = { udoc };
});
POST('/login', async ctx => {
let { username, password, rememberme } = ctx.request.body;
let udoc = await user.getByUname(username);
if (!udoc) throw new LoginError(username);
if (udoc) udoc.checkPassword(password);
await user.setById(udoc._id, { loginat: new Date(), loginip: ctx.request.ip });
udoc.salt = '';
udoc.password = '';
console.log(udoc);
ctx.session.uid = udoc._id;
ctx.session.rememberme = rememberme;
ctx.body = { udoc };
});
POST('/logout', requirePriv(PRIV_USER_PROFILE), async ctx => {
ctx.session = { uid: 1 };
ctx.body = {};
});
GET('/register/:code', requirePriv(PRIV_REGISTER_USER), async ctx => {
let code = ctx.request.body.code;
let { mail } = await token.get(code, token.TYPE_REGISTRATION);
if (!mail) throw new InvalidTokenError(token.TYPE_REGISTRATION, code);
ctx.body = { mail };
});
GET('/register/:code', requirePriv(PRIV_REGISTER_USER), async ctx => {
let { code, password, verify_password, uname } = ctx.request.body;
let { mail } = await token.get(code, token.TYPE_REGISTRATION);
if (!mail) throw new InvalidTokenError(token.TYPE_REGISTRATION, code);
if (password != verify_password) throw new VerifyPasswordError();
let uid = await system.incUserCounter();
await user.add(uid, uname, password, mail, ctx.remote_ip);
await token.delete(code, token.TYPE_REGISTRATION);
ctx.session.uid = uid;
ctx.body = {};
});
if (options.smtp.user) {
POST('/register', requirePriv(PRIV_REGISTER_USER), limitRate('send_mail', 3600, 30), async ctx => {
let email = ctx.request.body.email;
validator.check_mail(email);
if (await user.get_by_mail(email)) throw new UserAlreadyExistError(email);
let rid = await token.add(token.TYPE_REGISTRATION, options.registration_token_expire_seconds, { mail: email });
let m = await ctx.render('user_register_mail', { url: `/register/${rid}` }, true);
await mail.send_mail(email, 'Sign Up', 'user_register_mail', m);
ctx.body = {};
});
POST('/lostpass', requirePriv(PRIV_REGISTER_USER), limitRate('send_mail', 3600, 30), async ctx => {
let email = ctx.request.body.mail;
validator.check_mail(email);
let udoc = await user.getByEmail(email);
if (!udoc) throw new UserNotFoundError(email);
let tid = await token.add(
token.TYPE_LOSTPASS,
options.lostpass_token_expire_seconds,
{ uid: udoc._id }
);
let m = await ctx.render('user_lostpass_mail', { url: `/lostpass/${tid}`, uname: udoc.uname }, true);
await mail.send_mail(email, 'Lost Password', 'user_lostpass_mail', m);
ctx.body = {};
});
GET('/lostpass/:code', requirePriv(PRIV_REGISTER_USER), async ctx => {
let code = ctx.params.code;
let tdoc = await token.get(code, token.TYPE_LOSTPASS);
if (!tdoc) throw new InvalidTokenError(token.TYPE_LOSTPASS, code);
let udoc = await user.getById(tdoc.uid);
ctx.body = { uname: udoc.uname };
});
POST('/lostpass/:code', requirePriv(PRIV_REGISTER_USER), async ctx => {
let code = ctx.params.code;
let password = ctx.request.body.password;
let verify_password = ctx.request.body.verify_password;
let tdoc = await token.get(code, token.TYPE_LOSTPASS);
if (!tdoc) throw new InvalidTokenError(token.TYPE_LOSTPASS, code);
if (password != verify_password) throw new VerifyPasswordError();
await user.set_password(tdoc.uid, password);
await token.delete(code, token.TYPE_LOSTPASS);
ctx.redirect('/');
});
} else
POST('/register', requirePriv(PRIV_REGISTER_USER), async ctx => {
let email = ctx.request.body.email;
validator.check_mail(email);
if (await user.get_by_mail(email)) throw new UserAlreadyExistError(email);
let token = await token.add(token.TYPE_REGISTRATION, options.registration_token_expire_seconds, { mail: email });
ctx.body = { token };
});
/*
@app.route('/user/{uid:-?\d+}', 'user_detail')
class UserDetailHandler(base.Handler, UserSettingsMixin):
@base.route_argument
@base.sanitize
async def get(self, *, uid: int):
is_self_profile = self.has_priv(builtin.PRIV_USER_PROFILE) and self.user['_id'] == uid
udoc = await user.get_by_uid(uid)
if not udoc:
raise error.UserNotFoundError(uid)
dudoc, sdoc = await asyncio.gather(domain.get_user(self.domainId, udoc['_id']),
token.get_most_recent_session_by_uid(udoc['_id']))
rdocs = record.get_multi(get_hidden=self.has_priv(builtin.PRIV_VIEW_HIDDEN_RECORD),
uid=uid).sort([('_id', -1)])
rdocs = await rdocs.limit(10).to_list()
pdict = await problem.get_dict_multi_domain((rdoc['domainId'], rdoc['pid']) for rdoc in rdocs)
# check hidden problem
if not self.has_perm(builtin.PERM_VIEW_PROBLEM_HIDDEN):
f = {'hidden': False}
else:
f = {}
pdocs = problem.get_multi(domainId=self.domainId, owner_uid=uid, **f).sort([('_id', -1)])
pcount = await pdocs.count()
pdocs = await pdocs.limit(10).to_list()
psdocs = problem.get_multi_solution_by_uid(self.domainId, uid)
psdocs_hot = problem.get_multi_solution_by_uid(self.domainId, uid)
pscount = await psdocs.count()
psdocs = await psdocs.limit(10).to_list()
psdocs_hot = await psdocs_hot.sort([('vote', -1), ('doc_id', -1)]).limit(10).to_list()
if self.has_perm(builtin.PERM_VIEW_DISCUSSION):
ddocs = discussion.get_multi(self.domainId, owner_uid=uid)
dcount = await ddocs.count()
ddocs = await ddocs.limit(10).to_list()
vndict = await discussion.get_dict_vnodes(self.domainId, map(discussion.node_id, ddocs))
else:
ddocs = []
vndict = {}
dcount = 0
self.render('user_detail.html', is_self_profile=is_self_profile,
udoc=udoc, dudoc=dudoc, sdoc=sdoc,
rdocs=rdocs, pdict=pdict, pdocs=pdocs, pcount=pcount,
psdocs=psdocs, pscount=pscount, psdocs_hot=psdocs_hot,
ddocs=ddocs, dcount=dcount, vndict=vndict)
@app.route('/user/search', 'user_search')
class UserSearchHandler(base.Handler):
def modify_udoc(self, udict, key):
udoc = udict[key]
gravatar_url = misc.gravatar_url(udoc.get('gravatar'))
if 'gravatar' in udoc and udoc['gravatar']:
udict[key] = {**udoc,
'gravatar_url': gravatar_url,
'gravatar': ''}
@base.requirePriv(builtin.PRIV_USER_PROFILE)
@base.get_argument
@base.route_argument
@base.sanitize
async def get(self, *, q: str, exact_match: bool=False):
if exact_match:
udocs = []
else:
udocs = await user.get_prefix_list(q, user.PROJECTION_PUBLIC, 20)
try:
udoc = await user.get_by_uid(int(q), user.PROJECTION_PUBLIC)
if udoc:
udocs.insert(0, udoc)
except ValueError as e:
pass
for i in range(len(udocs)):
self.modify_udoc(udocs, i)
self.json(udocs)
*/