mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-19 17:38:19 +08:00
模块化,添加webpack支持
This commit is contained in:
1 parent
4ec2a46877
commit
75ebc1a746
30 files changed
+622
-459
No files matched your search
@@ -267,11 +267,25 @@ class ContestCreateHandler extends ContestHandler {
|
||||
}
|
||||
}
|
||||
|
||||
Route('/c', ContestListHandler);
|
||||
Route('/c/:tid', ContestDetailHandler);
|
||||
Route('/c/:tid/edit', ContestEditHandler);
|
||||
Route('/c/:tid/scoreboard', ContestScoreboardHandler);
|
||||
Route('/c/:tid/export/:ext', ContestScoreboardDownloadHandler);
|
||||
Route('/c/:tid/p/:pid', ContestProblemHandler);
|
||||
Route('/c/:tid/p/:pid/submit', ContestProblemSubmitHandler);
|
||||
Route('/contest/create', ContestCreateHandler);
|
||||
function apply() {
|
||||
Route('/c', module.exports.ContestListHandler);
|
||||
Route('/c/:tid', module.exports.ContestDetailHandler);
|
||||
Route('/c/:tid/edit', module.exports.ContestEditHandler);
|
||||
Route('/c/:tid/scoreboard', module.exports.ContestScoreboardHandler);
|
||||
Route('/c/:tid/export/:ext', module.exports.ContestScoreboardDownloadHandler);
|
||||
Route('/c/:tid/p/:pid', module.exports.ContestProblemHandler);
|
||||
Route('/c/:tid/p/:pid/submit', module.exports.ContestProblemSubmitHandler);
|
||||
Route('/contest/create', module.exports.ContestCreateHandler);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
ContestListHandler,
|
||||
ContestDetailHandler,
|
||||
ContestEditHandler,
|
||||
ContestScoreboardHandler,
|
||||
ContestScoreboardDownloadHandler,
|
||||
ContestProblemHandler,
|
||||
ContestProblemSubmitHandler,
|
||||
ContestCreateHandler,
|
||||
apply,
|
||||
};
|
||||
@@ -257,11 +257,25 @@ class DiscussionEditHandler extends DiscussionHandler {
|
||||
}
|
||||
}
|
||||
|
||||
Route('/discuss', DiscussionMainHandler);
|
||||
Route('/discuss/:did', DiscussionDetailHandler);
|
||||
Route('/discuss/:did/edit', DiscussionEditHandler);
|
||||
Route('/discuss/:did/raw', DiscussionDetailRawHandler);
|
||||
Route('/discuss/:did/:drid/raw', DiscussionReplyRawHandler);
|
||||
Route('/discuss/:did/:drid/:drrid/raw', DiscussionTailReplyRawHandler);
|
||||
Route('/discuss/:type/:docId', DiscussionNodeHandler);
|
||||
Route('/discuss/:type/:docId/create', DiscussionCreateHandler);
|
||||
function apply() {
|
||||
Route('/discuss', module.exports.DiscussionMainHandler);
|
||||
Route('/discuss/:did', module.exports.DiscussionDetailHandler);
|
||||
Route('/discuss/:did/edit', module.exports.DiscussionEditHandler);
|
||||
Route('/discuss/:did/raw', module.exports.DiscussionDetailRawHandler);
|
||||
Route('/discuss/:did/:drid/raw', module.exports.DiscussionReplyRawHandler);
|
||||
Route('/discuss/:did/:drid/:drrid/raw', module.exports.DiscussionTailReplyRawHandler);
|
||||
Route('/discuss/:type/:docId', module.exports.DiscussionNodeHandler);
|
||||
Route('/discuss/:type/:docId/create', module.exports.DiscussionCreateHandler);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
DiscussionMainHandler,
|
||||
DiscussionDetailHandler,
|
||||
DiscussionEditHandler,
|
||||
DiscussionDetailRawHandler,
|
||||
DiscussionReplyRawHandler,
|
||||
DiscussionTailReplyRawHandler,
|
||||
DiscussionNodeHandler,
|
||||
DiscussionCreateHandler,
|
||||
apply,
|
||||
};
|
||||
+228
-1
@@ -1,8 +1,19 @@
|
||||
const {
|
||||
VerifyPasswordError, UserAlreadyExistError, InvalidTokenError,
|
||||
NotFoundError,
|
||||
} = require('../error');
|
||||
const options = require('../options');
|
||||
const { Route, Handler } = require('../service/server');
|
||||
const md5 = require('../lib/md5');
|
||||
const contest = require('../model/contest');
|
||||
const user = require('../model/user');
|
||||
const setting = require('../model/setting');
|
||||
const token = require('../model/token');
|
||||
const training = require('../model/training');
|
||||
const { PERM_VIEW_TRAINING, PERM_VIEW_CONTEST, PERM_VIEW_DISCUSSION } = require('../permission');
|
||||
const {
|
||||
PERM_VIEW_TRAINING, PERM_VIEW_CONTEST, PERM_VIEW_DISCUSSION,
|
||||
PERM_LOGGEDIN,
|
||||
} = require('../permission');
|
||||
const { CONTESTS_ON_MAIN, TRAININGS_ON_MAIN, DISCUSSIONS_ON_MAIN } = require('../options').constants;
|
||||
|
||||
class HomeHandler extends Handler {
|
||||
@@ -57,4 +68,220 @@ class HomeHandler extends Handler {
|
||||
}
|
||||
}
|
||||
|
||||
class HomeSecurityHandler extends Handler {
|
||||
async prepare() {
|
||||
this.checkPerm(PERM_LOGGEDIN);
|
||||
}
|
||||
|
||||
async get() {
|
||||
// TODO(iceboy): pagination? or limit session count for uid?
|
||||
const sessions = await token.getSessionListByUid(this.user._id);
|
||||
const parsed = sessions.map((session) => ({
|
||||
...session,
|
||||
updateUa: useragent.parse(session.updateUa || session.createUa || ''),
|
||||
updateGeoip: geoip.ip2geo(session.updateIp || session.createIp, this.user.viewLang),
|
||||
_id: md5(session._id),
|
||||
isCurrent: session._id === this.session._id,
|
||||
}));
|
||||
this.response.template = 'home_security.html';
|
||||
this.response.body = { sessions: parsed };
|
||||
}
|
||||
|
||||
async postChangePassword({ current, password, verifyPassword }) {
|
||||
if (password !== verifyPassword) throw new VerifyPasswordError();
|
||||
await user.changePassword(this.user._id, current, password);
|
||||
this.back();
|
||||
}
|
||||
|
||||
async postChangeMail({ currentPassword, mail }) {
|
||||
this.limitRate('send_mail', 3600, 30);
|
||||
this.user.checkPassword(currentPassword);
|
||||
const udoc = await user.getByMail(mail);
|
||||
if (udoc) throw new UserAlreadyExistError(mail);
|
||||
const [rid] = await token.add(
|
||||
token.TYPE_CHANGEMAIL,
|
||||
options.changemail_token_expire_seconds,
|
||||
{ uid: this.udoc._id, mail },
|
||||
);
|
||||
await mail.sendMail(mail, 'Change Email', 'user_changemail_mail.html', {
|
||||
url: `/changeMail/${rid}`, uname: this.udoc.uname,
|
||||
});
|
||||
this.response.template = 'user_changemail_mail_sent.html';
|
||||
}
|
||||
|
||||
async postDeleteToken({ tokenDigest }) {
|
||||
const sessions = await token.getSessionListByUid(this.user._id);
|
||||
for (const session in sessions) {
|
||||
if (tokenDigest === md5(session._id)) {
|
||||
await token.delete(session._id, token.TYPE_SESSION);
|
||||
return this.back();
|
||||
}
|
||||
}
|
||||
throw new InvalidTokenError(tokenDigest);
|
||||
}
|
||||
|
||||
async postDeleteAllTokens() {
|
||||
await token.deleteByUid(this.user._id);
|
||||
this.back();
|
||||
}
|
||||
}
|
||||
|
||||
class HomeSettingsHandler extends Handler {
|
||||
async prepare() {
|
||||
this.checkPerm(PERM_LOGGEDIN);
|
||||
}
|
||||
|
||||
async get({ category }) {
|
||||
this.response.template = 'home_settings.html';
|
||||
if (category === 'preference') {
|
||||
this.response.body = {
|
||||
category,
|
||||
page_name: `home_${category}`,
|
||||
settings: setting.PREFERENCE_SETTINGS,
|
||||
};
|
||||
} else if (category === 'account') {
|
||||
this.response.body = {
|
||||
category,
|
||||
page_name: `home_${category}`,
|
||||
settings: setting.ACCOUNT_SETTINGS,
|
||||
};
|
||||
} else throw new NotFoundError();
|
||||
}
|
||||
|
||||
async post(args) {
|
||||
// FIXME validation
|
||||
await user.setById(this.user._id, args);
|
||||
this.back();
|
||||
}
|
||||
}
|
||||
|
||||
class UserChangemailWithCodeHandler extends Handler {
|
||||
async get({ code }) {
|
||||
const tdoc = await token.get(code, token.TYPE_CHANGEMAIL);
|
||||
if (!tdoc || tdoc.uid !== this.user._id) {
|
||||
throw new InvalidTokenError(code);
|
||||
}
|
||||
const udoc = await user.getByEmail(tdoc.mail);
|
||||
if (udoc) throw new UserAlreadyExistError(tdoc.mail);
|
||||
// TODO(twd2): Ensure mail is unique.
|
||||
await user.set_mail(this.user._id, tdoc.mail);
|
||||
await token.delete(code, token.TYPE_CHANGEMAIL);
|
||||
this.response.body = {};
|
||||
this.response.redirect = '/home/security';
|
||||
}
|
||||
}
|
||||
|
||||
Route('/', HomeHandler);
|
||||
Route('/home/security', HomeSecurityHandler);
|
||||
Route('/home/changeMail/:code', UserChangemailWithCodeHandler);
|
||||
Route('/home/settings/:category', HomeSettingsHandler);
|
||||
|
||||
/*
|
||||
@app.route('/home/messages', 'home_messages', global_route=True)
|
||||
class HomeMessagesHandler(base.OperationHandler):
|
||||
def modify_udoc(this, udict, key):
|
||||
udoc = udict.get(key)
|
||||
if not udoc:
|
||||
return
|
||||
gravatar_url = misc.gravatar_url(udoc.get('gravatar'))
|
||||
if 'gravatar' in udoc and udoc['gravatar']:
|
||||
udict[key] = {**udoc,
|
||||
'gravatar_url': gravatar_url,
|
||||
'gravatar': ''}
|
||||
|
||||
@base.require_priv(builtin.PRIV_USER_PROFILE)
|
||||
async def get(this):
|
||||
# TODO(iceboy): projection, pagination.
|
||||
mdocs = await message.get_multi(this.user['_id']).sort([('_id', -1)]).limit(50).to_list()
|
||||
udict = await user.get_dict(
|
||||
itertools.chain.from_iterable((mdoc['sender_uid'], mdoc['sendee_uid']) for mdoc in mdocs),
|
||||
fields=user.PROJECTION_PUBLIC)
|
||||
# TODO(twd2): improve here:
|
||||
for mdoc in mdocs:
|
||||
this.modify_udoc(udict, mdoc['sender_uid'])
|
||||
this.modify_udoc(udict, mdoc['sendee_uid'])
|
||||
this.json_or_render('home_messages.html', messages=mdocs, udict=udict)
|
||||
|
||||
@base.require_priv(builtin.PRIV_USER_PROFILE)
|
||||
@base.require_csrf_token
|
||||
@base.sanitize
|
||||
async def post_send_message(this, *, uid: int, content: str):
|
||||
udoc = await user.get_by_uid(uid, user.PROJECTION_PUBLIC)
|
||||
if not udoc:
|
||||
raise error.UserNotFoundError(uid)
|
||||
mdoc = await message.add(this.user['_id'], udoc['_id'], content)
|
||||
# TODO(twd2): improve here:
|
||||
# projection
|
||||
sender_udoc = await user.get_by_uid(this.user['_id'], user.PROJECTION_PUBLIC)
|
||||
mdoc['sender_udoc'] = sender_udoc
|
||||
this.modify_udoc(mdoc, 'sender_udoc')
|
||||
mdoc['sendee_udoc'] = udoc
|
||||
this.modify_udoc(mdoc, 'sendee_udoc')
|
||||
if this.user['_id'] != uid:
|
||||
await bus.publish('message_received-' + str(uid), {'type': 'new', 'data': mdoc})
|
||||
this.json_or_redirect(this.url, mdoc=mdoc)
|
||||
|
||||
@base.require_priv(builtin.PRIV_USER_PROFILE)
|
||||
@base.require_csrf_token
|
||||
@base.sanitize
|
||||
async def post_reply_message(this, *, message_id: objectid.ObjectId, content: str):
|
||||
mdoc, reply = await message.add_reply(message_id, this.user['_id'], content)
|
||||
if not mdoc:
|
||||
return error.MessageNotFoundError(message_id)
|
||||
if mdoc['sender_uid'] != mdoc['sendee_uid']:
|
||||
if mdoc['sender_uid'] == this.user['_id']:
|
||||
other_uid = mdoc['sendee_uid']
|
||||
else:
|
||||
other_uid = mdoc['sender_uid']
|
||||
mdoc['reply'] = [reply]
|
||||
await bus.publish('message_received-' + str(other_uid), {'type': 'reply', 'data': mdoc})
|
||||
this.json_or_redirect(this.url, reply=reply)
|
||||
|
||||
@base.require_priv(builtin.PRIV_USER_PROFILE)
|
||||
@base.require_csrf_token
|
||||
@base.sanitize
|
||||
async def post_delete_message(this, *, message_id: objectid.ObjectId):
|
||||
await message.delete(message_id, this.user['_id'])
|
||||
this.back();
|
||||
|
||||
|
||||
@app.connection_route('/home/messages-conn', 'home_messages-conn', global_route=True)
|
||||
class HomeMessagesConnection(base.Connection):
|
||||
@base.require_priv(builtin.PRIV_USER_PROFILE)
|
||||
async def on_open(this):
|
||||
await super(HomeMessagesConnection, this).on_open()
|
||||
bus.subscribe(this.on_message_received, ['message_received-' + str(this.user['_id'])])
|
||||
|
||||
async def on_message_received(this, e):
|
||||
this.send(**e['value'])
|
||||
|
||||
async def on_close(this):
|
||||
bus.unsubscribe(this.on_message_received)
|
||||
|
||||
|
||||
@app.route('/home/file', 'home_file', global_route=True)
|
||||
class HomeFileHandler(base.OperationHandler):
|
||||
def file_url(this, fdoc):
|
||||
return options.cdn_prefix.rstrip('/') + \
|
||||
this.reverse_url('fs_get', domain_id=builtin.DOMAIN_ID_SYSTEM,
|
||||
secret=fdoc['metadata']['secret'])
|
||||
|
||||
@base.require_priv(builtin.PRIV_USER_PROFILE)
|
||||
async def get(this):
|
||||
ufdocs = await userfile.get_multi(owner_uid=this.user['_id']).to_list()
|
||||
fdict = await fs.get_meta_dict(ufdoc.get('file_id') for ufdoc in ufdocs)
|
||||
this.render('home_file.html', ufdocs=ufdocs, fdict=fdict)
|
||||
|
||||
@base.require_priv(builtin.PRIV_USER_PROFILE)
|
||||
@base.post_argument
|
||||
@base.require_csrf_token
|
||||
@base.sanitize
|
||||
async def post_delete(this, *, ufid: document.convert_doc_id):
|
||||
ufdoc = await userfile.get(ufid)
|
||||
if not this.own(ufdoc, priv=builtin.PRIV_DELETE_FILE_this):
|
||||
this.check_priv(builtin.PRIV_DELETE_FILE)
|
||||
result = await userfile.delete(ufdoc['doc_id'])
|
||||
if result:
|
||||
await userfile.dec_usage(this.user['_id'], ufdoc['length'])
|
||||
this.redirect(this.referer_or_main)
|
||||
*/
|
||||
+9
-50
@@ -1,57 +1,10 @@
|
||||
const assert = require('assert');
|
||||
const download = require('../lib/download');
|
||||
|
||||
const problem = require('../model/problem');
|
||||
const { Route, Handler } = require('../service/server');
|
||||
const { PERM_CREATE_PROBLEM } = require('../permission');
|
||||
const axios = require('../lib/axios');
|
||||
const { ValidationError, RemoteOnlineJudgeError } = require('../error');
|
||||
const { ValidationError } = require('../error');
|
||||
|
||||
class ProblemImportHandler extends Handler {
|
||||
async syzoj(url) {
|
||||
const RE_SYZOJ = /https?:\/\/([a-zA-Z0-9.]+)\/problem\/([0-9]+)\/?/i;
|
||||
assert(url.match(RE_SYZOJ), new ValidationError('url'));
|
||||
if (!url.endsWith('/')) url += '/';
|
||||
const [, host, pid] = RE_SYZOJ.exec(url);
|
||||
const res = await axios.get(`${url}export`);
|
||||
assert(res.status === 200, new RemoteOnlineJudgeError('Cannot connect to target server'));
|
||||
assert(res.data.success, new RemoteOnlineJudgeError((res.data.error || {}).message));
|
||||
const p = res.data.obj;
|
||||
const content = [
|
||||
this.translate('problem.import.problem_description'),
|
||||
p.description,
|
||||
this.translate('problem.import.input_format'),
|
||||
p.input_format,
|
||||
this.translate('problem.import.output_format'),
|
||||
p.output_format,
|
||||
this.translate('problem.import.hint'),
|
||||
p.hint,
|
||||
this.translate('problem.import.limit_and_hint'),
|
||||
p.limit_and_hint,
|
||||
];
|
||||
if (p.have_additional_file) {
|
||||
content.push(
|
||||
this.translate('problem.import.additional_file'),
|
||||
`${url}download/additional_file`,
|
||||
);
|
||||
}
|
||||
const pdoc = {
|
||||
title: p.title,
|
||||
content: content.join(' \n'),
|
||||
owner: this.user._id,
|
||||
from: url,
|
||||
pid: `${host}_${pid}`,
|
||||
config: {
|
||||
time: p.time_limit,
|
||||
memory: p.memory_limit * 1024,
|
||||
filename: p.file_io_input_name,
|
||||
type: p.type,
|
||||
tags: p.tags,
|
||||
},
|
||||
};
|
||||
const r = await download(`${url}testdata/download`);
|
||||
return [pdoc, r];
|
||||
}
|
||||
|
||||
async prepare() {
|
||||
this.checkPerm(PERM_CREATE_PROBLEM);
|
||||
}
|
||||
@@ -82,4 +35,10 @@ class ProblemImportHandler extends Handler {
|
||||
}
|
||||
}
|
||||
|
||||
Route('/problem/import', ProblemImportHandler);
|
||||
async function apply() {
|
||||
Route('/problem/import', module.exports.ProblemImportHandler);
|
||||
}
|
||||
|
||||
global.Hydro['handler.import'] = module.exports = {
|
||||
ProblemImportHandler, apply,
|
||||
};
|
||||
Reference in new issue
Block a user