优化结构

This commit is contained in:
undefined committed 2020-04-30 18:07:35 +08:00
1 parent 75ebc1a746
commit 2554cf660a
34 files changed
+393 -142

No files matched your search

+1
View File
@@ -26,5 +26,6 @@ module.exports = {
"no-param-reassign": "off", "no-param-reassign": "off",
"global-require": "off", "global-require": "off",
"no-nested-ternary": "off", "no-nested-ternary": "off",
"no-multi-assign": "off",
}, },
}; };
+2 -4
View File
@@ -14,8 +14,7 @@ jobs:
node_version: '13.x' node_version: '13.x'
- run: | - run: |
yarn yarn
yarn add webpack webpack-cli -D yarn build:production
yarn build
- uses: actions/upload-artifact@v1 - uses: actions/upload-artifact@v1
with: with:
name: hydro name: hydro
@@ -23,8 +22,7 @@ jobs:
- run: | - run: |
cd ui cd ui
yarn yarn
yarn add webpack webpack-cli -D yarn build:production
yarn build
cd .. cd ..
- uses: actions/upload-artifact@v1 - uses: actions/upload-artifact@v1
with: with:
+2 -2
View File
@@ -1,7 +1,7 @@
FROM mhart/alpine-node:slim-10 FROM mhart/alpine-node:slim-10
WORKDIR /app WORKDIR /app
ADD .uibuild dist locales templates /app/ ADD .uibuild .build /app/
RUN mkdir /config RUN mkdir /config
VOLUME [ "/config" ] VOLUME [ "/config" ]
EXPOSE 8888 EXPOSE 8888
CMD ["node", "dist/development.js"] CMD ["node", ".build/development.js"]
+10 -8
View File
@@ -1,7 +1,9 @@
/* eslint-disable import/no-extraneous-dependencies */
const fs = require('fs'); const fs = require('fs');
const webpack = require('webpack'); const webpack = require('webpack');
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin'); const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin');
const root = require('./root'); const root = require('./root');
const exist = (name) => { const exist = (name) => {
try { try {
fs.statSync(root(name)); fs.statSync(root(name));
@@ -9,24 +11,24 @@ const exist = (name) => {
return false; return false;
} }
return true; return true;
} };
const build = async () => { const build = async (type) => {
const modules = fs.readdirSync(root('module')); const modules = fs.readdirSync(root('module'));
const config = { const config = {
mode: 'production', mode: type,
entry: {}, entry: {},
output: { output: {
filename: 'module/[name].js', filename: 'module/[name].js',
path: root('.build') path: root('.build'),
}, },
target: 'node', target: 'node',
module: {}, module: {},
plugins: [ plugins: [
new webpack.ProgressPlugin(), new webpack.ProgressPlugin(),
new FriendlyErrorsPlugin(), new FriendlyErrorsPlugin(),
] ],
}; };
for (let i of modules) { for (const i of modules) {
if (!i.startsWith('.')) { if (!i.startsWith('.')) {
if (exist(`module/${i}/model.js`)) { if (exist(`module/${i}/model.js`)) {
config.entry[`${i}/model`] = root(`module/${i}/model.js`); config.entry[`${i}/model`] = root(`module/${i}/model.js`);
@@ -47,7 +49,7 @@ const build = async () => {
if (stats.hasErrors()) process.exitCode = 1; if (stats.hasErrors()) process.exitCode = 1;
resolve(); resolve();
}); });
}) });
} };
module.exports = build; module.exports = build;
+45
View File
@@ -0,0 +1,45 @@
const fs = require('fs');
const yaml = require('js-yaml');
const root = require('./root');
function getFiles(folder) {
const files = [];
const f = fs.readdirSync(root(folder));
for (const i of f) {
if (!i.startsWith('.')) {
if (fs.statSync(root(`${folder}/${i}`)).isDirectory()) {
const g = getFiles(`${folder}/${i}`);
for (const j of g) files.push(`${i}/${j}`);
} else files.push(i);
}
}
return files;
}
const build = async () => {
const files = getFiles('templates');
const exclude = ['partials/training_default.json', 'partials/problem_default.md'];
const templates = {};
for (const i of files) {
let template = fs.readFileSync(root(`templates/${i}`)).toString();
if (!exclude.includes(i)) {
template = template
.trim()
.replace(/ *\n */gmi, ' ')
.replace(/, /gmi, ',')
.replace(/%} {%/gmi, '%}{%')
.replace(/ %}/gmi, '%}')
.replace(/{% /gmi, '{%')
.replace(/> </gmi, '><')
.replace(/}} </gmi, '}}<')
.replace(/> {{/gmi, '>{{')
.replace(/{{ /gmi, '{{')
.replace(/ }}/gmi, '}}')
.trim();
}
templates[i] = template;
}
fs.writeFileSync(root('.build/template.yaml'), yaml.safeDump(templates));
};
module.exports = build;
+3
View File
@@ -0,0 +1,3 @@
require('./index.js')('development').catch((e) => {
console.error(e);
});
+21 -6
View File
@@ -1,9 +1,24 @@
const fs = require('fs'); const fs = require('fs');
const root = require('./root'); const root = require('./root');
try {
async function build(type) {
if (!['development', 'production'].includes(type)) throw new Error('Unknown type: ' + type);
try {
fs.rmdirSync(root('.build')); fs.rmdirSync(root('.build'));
} catch (e) { } } catch (e) { }
fs.mkdirSync(root('.build')); fs.mkdirSync(root('.build'));
require('./locales')(); await Promise.all([
require('./buildModule')(); require('./locales')(type),
require('./webpack')(); require('./buildModule')(type),
require('./buildTemplate')(type),
require('./webpack')(type),
]);
}
module.exports = build;
if (!module.parent) {
build('development').catch((e) => {
console.error(e);
});
}
+5 -4
View File
@@ -1,13 +1,14 @@
const yaml = require('js-yaml'); const yaml = require('js-yaml');
const fs = require('fs'); const fs = require('fs');
const root = require('./root'); const root = require('./root');
const build = async () => { const build = async () => {
let langs = fs.readdirSync(root('locales')); const langs = fs.readdirSync(root('locales'));
let lang = {}; const lang = {};
for (let i of langs) { for (const i of langs) {
const content = fs.readFileSync(root(`locales/${i}`)).toString(); const content = fs.readFileSync(root(`locales/${i}`)).toString();
lang[i.split('.')[0]] = yaml.safeLoad(content); lang[i.split('.')[0]] = yaml.safeLoad(content);
} }
fs.writeFileSync(root('.build/locales.json'), JSON.stringify(lang)); fs.writeFileSync(root('.build/locales.json'), JSON.stringify(lang));
} };
module.exports = build; module.exports = build;
+3
View File
@@ -0,0 +1,3 @@
require('./index.js')('production').catch((e) => {
console.error(e);
});
+1
View File
@@ -1,4 +1,5 @@
const path = require('path'); const path = require('path');
function root(name) { function root(name) {
return path.resolve(__dirname, '..', name); return path.resolve(__dirname, '..', name);
} }
+11 -9
View File
@@ -1,11 +1,13 @@
/* eslint-disable import/no-extraneous-dependencies */
const fs = require('fs'); const fs = require('fs');
const webpack = require('webpack'); const webpack = require('webpack');
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin'); const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin');
const root = require('./root'); const root = require('./root');
const build = async () => {
const build = async (type) => {
function hackNodeModuleFormidable() { function hackNodeModuleFormidable() {
const tasks = ['incoming_form', 'file', 'json_parser', 'querystring_parser']; const tasks = ['incoming_form', 'file', 'json_parser', 'querystring_parser'];
for (let task of tasks) { for (const task of tasks) {
let file = fs.readFileSync(root(`node_modules/formidable/lib/${task}.js`)).toString(); let file = fs.readFileSync(root(`node_modules/formidable/lib/${task}.js`)).toString();
if (file.startsWith('if (global.GENTLY) require = GENTLY.hijack(require);')) { if (file.startsWith('if (global.GENTLY) require = GENTLY.hijack(require);')) {
file = file.split('\n'); file = file.split('\n');
@@ -18,22 +20,22 @@ const build = async () => {
hackNodeModuleFormidable(); hackNodeModuleFormidable();
const config = { const config = {
mode: 'development', mode: type,
entry: { entry: {
development: root('hydro/development.js'), development: root('hydro/development.js'),
install: root('hydro/install.js'), install: root('hydro/install.js'),
uninstall: root('hydro/uninstall.js') uninstall: root('hydro/uninstall.js'),
}, },
output: { output: {
filename: '[name].js', filename: '[name].js',
path: root('.build') path: root('.build'),
}, },
target: 'node', target: 'node',
module: {}, module: {},
plugins: [ plugins: [
new webpack.ProgressPlugin(), new webpack.ProgressPlugin(),
new FriendlyErrorsPlugin(), new FriendlyErrorsPlugin(),
] ],
}; };
const compiler = webpack(config); const compiler = webpack(config);
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
@@ -41,12 +43,12 @@ const build = async () => {
if (err) { if (err) {
console.error(err.stack || err); console.error(err.stack || err);
if (err.details) console.error(err.details); if (err.details) console.error(err.details);
reject(1); reject();
} }
if (stats.hasErrors()) process.exitCode = 1; if (stats.hasErrors()) process.exitCode = 1;
resolve(); resolve();
}); });
}) });
} };
module.exports = build; module.exports = build;
+14 -14
View File
@@ -9,7 +9,10 @@ process.stdin.on('data', async (input) => {
} }
}); });
global.Hydro = {}; global.Hydro = {
handler: {},
template: {}
};
require('./lib/i18n'); require('./lib/i18n');
require('./utils'); require('./utils');
@@ -31,21 +34,18 @@ async function run() {
require('./service/gridfs'); require('./service/gridfs');
require('./service/queue'); require('./service/queue');
const server = require('./service/server'); const server = require('./service/server');
const HandlerHome = require('./handler/home'); const builtinHandler = [
const HandlerProblem = require('./handler/problem'); 'home', 'problem', 'record', 'judge', 'user',
const HandlerRecord = require('./handler/record'); 'contest', 'training', 'discussion', 'manage', 'import',
const HandlerJudge = require('./handler/judge'); ];
const HandlerUser = require('./handler/user'); for (const i of builtinHandler) require(`./handler/${i}`);
const HandlerContest = require('./handler/contest');
const HandlerTraining = require('./handler/training');
const HandlerDiscussion = require('./handler/discussion');
const HandlerManage = require('./handler/manage');
const HandlerImport = require('./handler/import');
await loader.model(); await loader.model();
await loader.handler(); await loader.handler();
HandlerContest.apply(); for (const i in global.Hydro.handler) {
HandlerDiscussion.apply(); await global.Hydro.handler[i].apply();
HandlerImport.apply(); }
const notfound = require(`./handler/notfound`);
await notfound.apply();
server.start(); server.start();
} }
run().catch((e) => { run().catch((e) => {
+2 -2
View File
@@ -267,7 +267,7 @@ class ContestCreateHandler extends ContestHandler {
} }
} }
function apply() { async function apply() {
Route('/c', module.exports.ContestListHandler); Route('/c', module.exports.ContestListHandler);
Route('/c/:tid', module.exports.ContestDetailHandler); Route('/c/:tid', module.exports.ContestDetailHandler);
Route('/c/:tid/edit', module.exports.ContestEditHandler); Route('/c/:tid/edit', module.exports.ContestEditHandler);
@@ -278,7 +278,7 @@ function apply() {
Route('/contest/create', module.exports.ContestCreateHandler); Route('/contest/create', module.exports.ContestCreateHandler);
} }
module.exports = { global.Hydro.handler.contest = module.exports = {
ContestListHandler, ContestListHandler,
ContestDetailHandler, ContestDetailHandler,
ContestEditHandler, ContestEditHandler,
+15 -10
View File
@@ -34,7 +34,9 @@ class DiscussionHandler extends Handler {
if (drrid) { if (drrid) {
[, this.drrdoc] = await discussion.getTailReply(drid, drrid); [, this.drrdoc] = await discussion.getTailReply(drid, drrid);
if (!this.drrdoc) throw new DiscussionNotFoundError(drrid); if (!this.drrdoc) throw new DiscussionNotFoundError(drrid);
if (this.drrdoc.parent !== this.drdoc._id) throw new DocumentNotFoundError(drid); if (this.drrdoc.parent !== this.drdoc._id) {
throw new DocumentNotFoundError(drid);
}
} }
} }
} }
@@ -60,7 +62,11 @@ class DiscussionHandler extends Handler {
class DiscussionMainHandler extends DiscussionHandler { class DiscussionMainHandler extends DiscussionHandler {
async get({ page = 1 }) { async get({ page = 1 }) {
const [ddocs, dpcount] = await paginate(discussion.getMulti(), page, constants.DISCUSSION_PER_PAGE); const [ddocs, dpcount] = await paginate(
discussion.getMulti(),
page,
constants.DISCUSSION_PER_PAGE,
);
const udict = await user.getList(ddocs.map((ddoc) => ddoc.owner)); const udict = await user.getList(ddocs.map((ddoc) => ddoc.owner));
this.response.template = 'discussion_main_or_node.html'; this.response.template = 'discussion_main_or_node.html';
this.response.body = { this.response.body = {
@@ -114,12 +120,11 @@ class DiscussionCreateHandler extends DiscussionHandler {
type, docId, title, content, highlight, type, docId, title, content, highlight,
}) { }) {
this.limitRate('add_discussion', 3600, 30); this.limitRate('add_discussion', 3600, 30);
const flags = {}; if (highlight) this.checkPerm(PERM_HIGHLIGHT_DISCUSSION);
if (highlight) { const did = await discussion.add(
this.checkPerm(PERM_HIGHLIGHT_DISCUSSION); type, docId, this.user._id,
flags.highlight = true; title, content, this.request.ip, highlight,
} );
const did = await discussion.add(type, docId, this.user._id, title, content, this.request.ip, flags);
this.response.body = { did }; this.response.body = { did };
this.response.redirect = `/discuss/${did}`; this.response.redirect = `/discuss/${did}`;
} }
@@ -257,7 +262,7 @@ class DiscussionEditHandler extends DiscussionHandler {
} }
} }
function apply() { async function apply() {
Route('/discuss', module.exports.DiscussionMainHandler); Route('/discuss', module.exports.DiscussionMainHandler);
Route('/discuss/:did', module.exports.DiscussionDetailHandler); Route('/discuss/:did', module.exports.DiscussionDetailHandler);
Route('/discuss/:did/edit', module.exports.DiscussionEditHandler); Route('/discuss/:did/edit', module.exports.DiscussionEditHandler);
@@ -268,7 +273,7 @@ function apply() {
Route('/discuss/:type/:docId/create', module.exports.DiscussionCreateHandler); Route('/discuss/:type/:docId/create', module.exports.DiscussionCreateHandler);
} }
module.exports = { global.Hydro.handler.discussion = module.exports = {
DiscussionMainHandler, DiscussionMainHandler,
DiscussionDetailHandler, DiscussionDetailHandler,
DiscussionEditHandler, DiscussionEditHandler,
+9 -4
View File
@@ -171,11 +171,16 @@ class UserChangemailWithCodeHandler extends Handler {
} }
} }
Route('/', HomeHandler); async function apply() {
Route('/home/security', HomeSecurityHandler); Route('/', module.exports.HomeHandler);
Route('/home/changeMail/:code', UserChangemailWithCodeHandler); Route('/home/security', module.exports.HomeSecurityHandler);
Route('/home/settings/:category', HomeSettingsHandler); Route('/home/changeMail/:code', module.exports.UserChangemailWithCodeHandler);
Route('/home/settings/:category', module.exports.HomeSettingsHandler);
}
global.Hydro.handler.home = module.exports = {
HomeHandler, HomeSecurityHandler, HomeSettingsHandler, UserChangemailWithCodeHandler, apply,
};
/* /*
@app.route('/home/messages', 'home_messages', global_route=True) @app.route('/home/messages', 'home_messages', global_route=True)
class HomeMessagesHandler(base.OperationHandler): class HomeMessagesHandler(base.OperationHandler):
+5 -4
View File
@@ -1,4 +1,3 @@
const problem = require('../model/problem'); const problem = require('../model/problem');
const { Route, Handler } = require('../service/server'); const { Route, Handler } = require('../service/server');
const { PERM_CREATE_PROBLEM } = require('../permission'); const { PERM_CREATE_PROBLEM } = require('../permission');
@@ -23,8 +22,10 @@ class ProblemImportHandler extends Handler {
async post({ async post({
url, pid, hidden, remoteType, url, pid, hidden, remoteType,
}) { }) {
if (remoteType !== 'syzoj') throw new ValidationError('remoteType'); if (typeof this[`from_${remoteType}`] !== 'function') {
const [pdoc, testdata] = await this.syzoj(url); throw new ValidationError('remoteType');
}
const [pdoc, testdata] = await this[`from_${remoteType}`](url);
if (pid) pdoc.pid = pid; if (pid) pdoc.pid = pid;
if (hidden) pdoc.hidden = true; if (hidden) pdoc.hidden = true;
const _id = await problem.add(pdoc); const _id = await problem.add(pdoc);
@@ -39,6 +40,6 @@ async function apply() {
Route('/problem/import', module.exports.ProblemImportHandler); Route('/problem/import', module.exports.ProblemImportHandler);
} }
global.Hydro['handler.import'] = module.exports = { global.Hydro.handler.import = module.exports = {
ProblemImportHandler, apply, ProblemImportHandler, apply,
}; };
+8 -2
View File
@@ -183,5 +183,11 @@ class JudgeConnectionHandler extends ConnectionHandler {
} }
} }
Route('/judge', JudgeHandler); async function apply() {
Connection('/judge/conn', JudgeConnectionHandler); Route('/judge', module.exports.JudgeHandler);
Connection('/judge/conn', module.exports.JudgeConnectionHandler);
}
global.Hydro.handler.judge = module.exports = {
JudgeHandler, JudgeConnectionHandler, apply,
};
+18 -6
View File
@@ -120,9 +120,21 @@ class ManageRoleHandler extends ManageHandler {
} }
} }
Route('/manage', ManageMainHandler); async function apply() {
Route('/manage/dashboard', ManageDashboardHandler); Route('/manage', module.exports.ManageMainHandler);
Route('/manage/edit', ManageEditHandler); Route('/manage/dashboard', module.exports.ManageDashboardHandler);
Route('/manage/user', ManageUserHandler); Route('/manage/edit', module.exports.ManageEditHandler);
Route('/manage/permission', ManagePermissionHandler); Route('/manage/user', module.exports.ManageUserHandler);
Route('/manage/role', ManageRoleHandler); Route('/manage/permission', module.exports.ManagePermissionHandler);
Route('/manage/role', module.exports.ManageRoleHandler);
}
global.Hydro.handler.manage = module.exports = {
ManageMainHandler,
ManageDashboardHandler,
ManageEditHandler,
ManageUserHandler,
ManagePermissionHandler,
ManageRoleHandler,
apply,
};
+19
View File
@@ -0,0 +1,19 @@
const { Route, Handler } = require('../service/server');
const { NotFoundError } = require('../error');
class NotFoundHandler extends Handler {
async prepare() {
throw new NotFoundError();
}
}
async function apply() {
Route('/:param1', module.exports.NotFoundHandler);
Route('/:param1/:param2', module.exports.NotFoundHandler);
Route('/:param1/:param2/:param3', module.exports.NotFoundHandler);
Route('/:param1/:param2/:param3/:param4', module.exports.NotFoundHandler);
}
global.Hydro.handler.notfound = module.exports = {
NotFoundHandler, apply,
};
+39 -16
View File
@@ -71,7 +71,9 @@ class ProblemDetailHandler extends ProblemHandler {
this.uid = this.user._id; this.uid = this.user._id;
this.pid = pid; this.pid = pid;
if (pid) this.pdoc = await problem.get(pid, this.uid); if (pid) this.pdoc = await problem.get(pid, this.uid);
if (this.pdoc.hidden && this.pdoc.owner !== this.uid) this.checkPerm(PERM_VIEW_PROBLEM_HIDDEN); if (this.pdoc.hidden && this.pdoc.owner !== this.uid) {
this.checkPerm(PERM_VIEW_PROBLEM_HIDDEN);
}
if (this.pdoc) this.udoc = await user.getById(this.pdoc.owner); if (this.pdoc) this.udoc = await user.getById(this.pdoc.owner);
this.response.body = { this.response.body = {
pdoc: this.pdoc, pdoc: this.pdoc,
@@ -376,18 +378,39 @@ class ProblemCreateHandler extends Handler {
} }
} }
Route('/p', ProblemHandler); async function apply() {
Route('/problem/random', ProblemRandomHandler); Route('/p', module.exports.ProblemHandler);
Route('/p/:pid', ProblemDetailHandler); Route('/problem/random', module.exports.ProblemRandomHandler);
Route('/p/:pid/submit', ProblemSubmitHandler); Route('/p/:pid', module.exports.ProblemDetailHandler);
Route('/p/:pid/pretest', ProblemPretestHandler); Route('/p/:pid/submit', module.exports.ProblemSubmitHandler);
Route('/p/:pid/settings', ProblemSettingsHandler); Route('/p/:pid/pretest', module.exports.ProblemPretestHandler);
Route('/p/:pid/statistics', ProblemStatisticsHandler); Route('/p/:pid/settings', module.exports.ProblemSettingsHandler);
Route('/p/:pid/edit', ProblemEditHandler); Route('/p/:pid/statistics', module.exports.ProblemStatisticsHandler);
Route('/p/:pid/upload', ProblemDataUploadHandler); Route('/p/:pid/edit', module.exports.ProblemEditHandler);
Route('/p/:pid/data', ProblemDataDownloadHandler); Route('/p/:pid/upload', module.exports.ProblemDataUploadHandler);
Route('/p/:pid/solution', ProblemSolutionHandler); Route('/p/:pid/data', module.exports.ProblemDataDownloadHandler);
Route('/p/:pid/solution/:psid/raw', ProblemSolutionRawHandler); Route('/p/:pid/solution', module.exports.ProblemSolutionHandler);
Route('/p/:pid/solution/:psid/:psrid/raw', ProblemSolutionReplyRawHandler); Route('/p/:pid/solution/:psid/raw', module.exports.ProblemSolutionRawHandler);
Route('/problem/create', ProblemCreateHandler); Route('/p/:pid/solution/:psid/:psrid/raw', module.exports.ProblemSolutionReplyRawHandler);
Connection('/p/:pid/pretest-conn', ProblemPretestConnectionHandler); Route('/problem/create', module.exports.ProblemCreateHandler);
Connection('/p/:pid/pretest-conn', module.exports.ProblemPretestConnectionHandler);
}
global.Hydro.handler.problem = module.exports = {
ProblemHandler,
ProblemRandomHandler,
ProblemDetailHandler,
ProblemSubmitHandler,
ProblemPretestHandler,
ProblemSettingsHandler,
ProblemStatisticsHandler,
ProblemEditHandler,
ProblemDataUploadHandler,
ProblemDataDownloadHandler,
ProblemSolutionHandler,
ProblemSolutionRawHandler,
ProblemSolutionReplyRawHandler,
ProblemCreateHandler,
ProblemPretestConnectionHandler,
apply,
};
+24 -12
View File
@@ -34,18 +34,16 @@ class RecordListHandler extends Handler {
for (const pdoc of plist) { for (const pdoc of plist) {
pdict[pdoc._id] = pdoc; pdict[pdoc._id] = pdoc;
} }
this.response.body = { const path = [
path: [
['Hydro', '/'], ['Hydro', '/'],
['record_main', null], ['record_main', null],
], ];
page, this.response.body = {
rdocs, path, page, rdocs, pdict, udict,
pdict,
udict,
}; };
} }
} }
class RecordDetailHandler extends Handler { class RecordDetailHandler extends Handler {
async get({ rid }) { async get({ rid }) {
this.response.template = 'record_detail.html'; this.response.template = 'record_detail.html';
@@ -66,6 +64,7 @@ class RecordDetailHandler extends Handler {
}; };
} }
} }
class RecordRejudgeHandler extends Handler { class RecordRejudgeHandler extends Handler {
async post({ rid }) { async post({ rid }) {
this.checkPerm(PERM_REJUDGE); this.checkPerm(PERM_REJUDGE);
@@ -77,6 +76,7 @@ class RecordRejudgeHandler extends Handler {
this.back(); this.back();
} }
} }
class RecordConnectionHandler extends ConnectionHandler { class RecordConnectionHandler extends ConnectionHandler {
async prepare() { async prepare() {
bus.subscribe(['record_change'], this.onRecordChange); bus.subscribe(['record_change'], this.onRecordChange);
@@ -102,6 +102,7 @@ class RecordConnectionHandler extends ConnectionHandler {
this.send({ html: await this.renderHTML('record_main_tr.html', { rdoc, udoc, pdoc }) }); this.send({ html: await this.renderHTML('record_main_tr.html', { rdoc, udoc, pdoc }) });
} }
} }
class RecordDetailConnectionHandler extends contest.ContestHandlerMixin(ConnectionHandler) { class RecordDetailConnectionHandler extends contest.ContestHandlerMixin(ConnectionHandler) {
async prepare({ rid }) { async prepare({ rid }) {
const rdoc = await record.get(rid); const rdoc = await record.get(rid);
@@ -131,8 +132,19 @@ class RecordDetailConnectionHandler extends contest.ContestHandlerMixin(Connecti
} }
} }
Route('/r', RecordListHandler); async function apply() {
Route('/r/:rid', RecordDetailHandler); Route('/r', module.exports.RecordListHandler);
Route('/r/:rid/rejudge', RecordRejudgeHandler); Route('/r/:rid', module.exports.RecordDetailHandler);
Connection('/record-conn', RecordConnectionHandler); Route('/r/:rid/rejudge', module.exports.RecordRejudgeHandler);
Connection('/record-detail-conn', RecordDetailConnectionHandler); Connection('/record-conn', module.exports.RecordConnectionHandler);
Connection('/record-detail-conn', module.exports.RecordDetailConnectionHandler);
}
global.Hydro.handler.record = module.exports = {
RecordListHandler,
RecordDetailHandler,
RecordRejudgeHandler,
RecordConnectionHandler,
RecordDetailConnectionHandler,
apply,
};
+14 -4
View File
@@ -237,7 +237,17 @@ class TrainingEditHandler extends TrainingHandler {
} }
} }
Route('/t', TrainingMainHandler); async function apply() {
Route('/t/:tid', TrainingDetailHandler); Route('/t', module.exports.TrainingMainHandler);
Route('/t/:tid/edit', TrainingEditHandler); Route('/t/:tid', module.exports.TrainingDetailHandler);
Route('/training/create', TrainingCreateHandler); Route('/t/:tid/edit', module.exports.TrainingEditHandler);
Route('/training/create', module.exports.TrainingCreateHandler);
}
global.Hydro.handler.training = module.exports = {
TrainingMainHandler,
TrainingDetailHandler,
TrainingEditHandler,
TrainingCreateHandler,
apply,
};
+22 -8
View File
@@ -181,11 +181,25 @@ class UserSearchHandler extends Handler {
} }
} }
Route('/login', UserLoginHandler); async function apply() {
Route('/register', UserRegisterHandler); Route('/login', module.exports.UserLoginHandler);
Route('/register/:code', UserRegisterWithCodeHandler); Route('/register', module.exports.UserRegisterHandler);
Route('/logout', UserLogoutHandler); Route('/register/:code', module.exports.UserRegisterWithCodeHandler);
Route('/lostpass', UserLostPassHandler); Route('/logout', module.exports.UserLogoutHandler);
Route('/lostpass/:code', UserLostPassWithCodeHandler); Route('/lostpass', module.exports.UserLostPassHandler);
Route('/user/search', UserSearchHandler); Route('/lostpass/:code', module.exports.UserLostPassWithCodeHandler);
Route('/user/:uid', UserDetailHandler); Route('/user/search', module.exports.UserSearchHandler);
Route('/user/:uid', module.exports.UserDetailHandler);
}
global.Hydro.handler.user = module.exports = {
UserLoginHandler,
UserRegisterHandler,
UserRegisterWithCodeHandler,
UserLogoutHandler,
UserLostPassHandler,
UserLostPassWithCodeHandler,
UserSearchHandler,
UserDetailHandler,
apply,
};
+11 -5
View File
@@ -14,9 +14,15 @@ function exist(name) {
return true; return true;
} }
const superRequire = (name) => (__non_webpack_require__ const superRequire = (name) => {
? __non_webpack_require__(root(name)) let m;
: require(root(name))); try {
m = require(root(name)); // eslint-disable-line import/no-dynamic-require
} catch (e) {
m = __non_webpack_require__(root(name)); // eslint-disable-line no-undef
}
return m;
};
async function prepare() { async function prepare() {
installed = fs.readdirSync(root('.build/module')); installed = fs.readdirSync(root('.build/module'));
@@ -26,18 +32,18 @@ async function handler() {
for (const i of installed) { for (const i of installed) {
if (exist(`.build/module/${i}/handler.js`)) { if (exist(`.build/module/${i}/handler.js`)) {
superRequire(`.build/module/${i}/handler.js`); superRequire(`.build/module/${i}/handler.js`);
}
console.log(`Handler init: ${i}`); console.log(`Handler init: ${i}`);
} }
}
} }
async function model() { async function model() {
for (const i of installed) { for (const i of installed) {
if (exist(`.build/module/${i}/model.js`)) { if (exist(`.build/module/${i}/model.js`)) {
superRequire(`.build/module/${i}/model.js`); superRequire(`.build/module/${i}/model.js`);
}
console.log(`Model init: ${i}`); console.log(`Model init: ${i}`);
} }
}
} }
global.Hydro['lib.loader'] = module.exports = { prepare, handler, model }; global.Hydro['lib.loader'] = module.exports = { prepare, handler, model };
+7 -2
View File
@@ -51,7 +51,11 @@ async function edit(tid, $set) {
if ($set.title) validator.checkTitle($set.title); if ($set.title) validator.checkTitle($set.title);
if ($set.content) validator.checkIntro($set.content); if ($set.content) validator.checkIntro($set.content);
if ($set.rule) { if (!this.RULES[$set.rule]) throw new ValidationError('rule'); } 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'); if ($set.beginAt && $set.endAt) {
if ($set.beginAt >= $set.endAt) {
throw new ValidationError('beginAt', 'endAt');
}
}
const tdoc = await coll.findOne({ tid }); const tdoc = await coll.findOne({ tid });
if (!tdoc) throw new ContestNotFoundError(tid); if (!tdoc) throw new ContestNotFoundError(tid);
this.RULES[$set.rule || tdoc.rule].check(Object.assign(tdoc, $set)); this.RULES[$set.rule || tdoc.rule].check(Object.assign(tdoc, $set));
@@ -84,7 +88,8 @@ function getStatus(tid, uid) {
} }
async function getListStatus(uid, tids) { async function getListStatus(uid, tids) {
const r = {}; const r = {};
for (const tid of tids) r[tid] = await getStatus(tid, uid); // eslint-disable-line no-await-in-loop // eslint-disable-next-line no-await-in-loop
for (const tid of tids) r[tid] = await getStatus(tid, uid);
return r; return r;
} }
async function attend(tid, uid) { async function attend(tid, uid) {
+2 -1
View File
@@ -65,7 +65,7 @@ function editReply(drid, content) {
async function delReply(drid) { async function delReply(drid) {
const drdoc = await getReply(drid); const drdoc = await getReply(drid);
if (!drdoc) throw new DocumentNotFoundError(drid); if (!drdoc) throw new DocumentNotFoundError(drid);
return await Promise.all([ return await Promise.all([ // eslint-disable-line no-return-await
collReply.deleteOne({ _id: drid }), collReply.deleteOne({ _id: drid }),
coll.updateOne({ _id: drdoc.did }, { $inc: { nReply: -1 } }), coll.updateOne({ _id: drdoc.did }, { $inc: { nReply: -1 } }),
]); ]);
@@ -112,6 +112,7 @@ async function editTailReply(drid, drrid, content) {
break; break;
} }
} }
// eslint-disable-next-line no-return-await
return await collReply.findOneAndUpdate({ _id: drdoc._id }, { $set: { reply } }); return await collReply.findOneAndUpdate({ _id: drdoc._id }, { $set: { reply } });
} }
+1 -1
View File
@@ -157,7 +157,7 @@ async function setTestdata(_id, readStream) {
f.once('error', reject); f.once('error', reject);
}); });
if (pdoc.data && typeof pdoc.data === 'object') gridfs.delete(this.pdoc.data); if (pdoc.data && typeof pdoc.data === 'object') gridfs.delete(this.pdoc.data);
return await edit(_id, { data: f.id }); return await edit(_id, { data: f.id }); // eslint-disable-line no-return-await
} }
module.exports = { module.exports = {
+6 -4
View File
@@ -28,13 +28,14 @@ async function get(psid) {
} }
function getMany(query, sort, page, limit) { function getMany(query, sort, page, limit) {
return coll.find(query).sort(sort).skip((page - 1) * limit).limit(limit) return coll.find(query).sort(sort)
.skip((page - 1) * limit).limit(limit)
.toArray(); .toArray();
} }
async function edit(_id, content) { async function edit(_id, content) {
validator.checkContent(content); validator.checkContent(content);
await coll.findOneAndUpdate({ _id }, { $set: { content } }); await coll.updateOne({ _id }, { $set: { content } });
const psdoc = await get(_id); const psdoc = await get(_id);
if (!psdoc) throw new SolutionNotFoundError(_id); if (!psdoc) throw new SolutionNotFoundError(_id);
return psdoc; return psdoc;
@@ -79,7 +80,8 @@ async function editReply(psid, psrid, content) {
break; break;
} }
} }
return await coll.findOneAndUpdate({ _id: psdoc._id }, { $set: { reply } }); // eslint-disable-next-line no-return-await
return await coll.updateOne({ _id: psdoc._id }, { $set: { reply } });
} }
function delReply(psid, psrid) { function delReply(psid, psrid) {
@@ -91,7 +93,7 @@ async function vote(psid, uid, value) {
if (pssdoc) await collStatus.deleteOne({ psid, uid }); if (pssdoc) await collStatus.deleteOne({ psid, uid });
await collStatus.insertOne({ psid, uid, vote: value }); await collStatus.insertOne({ psid, uid, vote: value });
if (pssdoc) value += -pssdoc.vote; if (pssdoc) value += -pssdoc.vote;
await coll.findOneAndUpdate({ _id: psid }, { $inc: { vote: value } }); await coll.updateOne({ _id: psid }, { $inc: { vote: value } });
pssdoc = await collStatus.findOne({ psid, uid }); pssdoc = await collStatus.findOne({ psid, uid });
const psdoc = await coll.findOne({ _id: psid }); const psdoc = await coll.findOne({ _id: psid });
return [psdoc, pssdoc]; return [psdoc, pssdoc];
+3 -3
View File
@@ -27,12 +27,12 @@ async function enroll(tid, uid) {
} catch (e) { } catch (e) {
throw new TrainingAlreadyEnrollError(tid, uid); throw new TrainingAlreadyEnrollError(tid, uid);
} }
await coll.findOneAndUpdate({ _id: tid }, { $inc: { enroll: 1 } }); await coll.updateOne({ _id: tid }, { $inc: { enroll: 1 } });
} }
async function setStatus(tid, uid, $set) { async function setStatus(tid, uid, $set) {
await collStatus.findOneAndUpdate({ tid, uid }, { $set }); await collStatus.updateOne({ tid, uid }, { $set });
return await collStatus.findOne({ tid, uid }); return await collStatus.findOne({ tid, uid }); // eslint-disable-line no-return-await
} }
module.exports = { module.exports = {
+54
View File
@@ -0,0 +1,54 @@
const { defaults } = require('lodash');
const db = require('../service/db');
const builtin = require('../model/builtin');
const pwhash = require('../lib/pwhash');
const { udoc } = require('../interface');
async function run() {
const collUser = db.collection('user');
const collRole = db.collection('role');
const collBlacklist = db.collection('blacklist');
const collToken = db.collection('token');
async function createUser() {
const salt = pwhash.salt();
await collUser.insertMany([
defaults({
_id: 0,
uname: 'Hydro',
unameLower: 'hydro',
mail: 'hydro@hydro',
mailLower: 'hydro@hydro',
role: 'guest',
}, udoc),
defaults({
_id: 1,
mail: 'guest@hydro',
mailLower: 'guest@hydro',
uname: 'Guest',
unameLower: 'guest',
role: 'guest',
}, udoc),
defaults({
_id: -1,
mail: 'root@hydro',
mailLower: 'root@hydro',
uname: 'Root',
unameLower: 'root',
hash: pwhash.hash('rootroot', salt),
salt,
gravatar: 'root@hydro',
role: 'root',
}, udoc),
]);
}
await collUser.createIndex('unameLower', { unique: true });
await collUser.createIndex('mailLower', { sparse: true });
await collRole.insertMany(builtin.BUILTIN_ROLES);
await collBlacklist.createIndex('expireAt', { expireAfterSeconds: 0 });
await collToken.createIndex([{ uid: 1 }, { tokenType: 1 }, { updateAt: -1 }], { sparse: true });
await collToken.createIndex('expireAt', { expireAfterSeconds: 0 });
await createUser();
console.log('Installed');
}
module.exports = { run };
+8
View File
@@ -0,0 +1,8 @@
const db = require('../service/db.js');
async function run() {
await db.dropDatabase();
console.log('Dropped');
}
module.exports = { run };
+2 -8
View File
@@ -17,7 +17,7 @@ const token = require('../model/token');
const opcount = require('../model/opcount'); const opcount = require('../model/opcount');
const { const {
UserNotFoundError, BlacklistedError, PermissionError, UserNotFoundError, BlacklistedError, PermissionError,
NotFoundError, UserFacingError, ValidationError, UserFacingError, ValidationError,
} = require('../error'); } = require('../error');
const app = new Koa(); const app = new Koa();
@@ -166,12 +166,7 @@ class Handler {
} }
async renderBody() { async renderBody() {
if (!this.response.redirect) { if (!this.response.redirect && !this.preferJson) {
if (!(this.response.body || this.response.template)) {
this.response.body = { error: new NotFoundError() };
this.response.template = 'error.html';
}
if (!this.preferJson) {
if (this.response.body || this.response.template) { if (this.response.body || this.response.template) {
if (this.request.query.noTemplate || this.preferJson) return; if (this.request.query.noTemplate || this.preferJson) return;
const templateName = this.request.query.template || this.response.template; const templateName = this.request.query.template || this.response.template;
@@ -182,7 +177,6 @@ class Handler {
} }
} }
} }
}
async putResponse() { async putResponse() {
if (this.response.disposition) this.ctx.set('Content-Disposition', this.response.disposition); if (this.response.disposition) this.ctx.set('Content-Disposition', this.response.disposition);
+6 -2
View File
@@ -26,12 +26,16 @@
"devDependencies": { "devDependencies": {
"eslint": "^6.8.0", "eslint": "^6.8.0",
"eslint-config-airbnb-base": "^14.1.0", "eslint-config-airbnb-base": "^14.1.0",
"eslint-plugin-import": "^2.20.2" "eslint-plugin-import": "^2.20.2",
"friendly-errors-webpack-plugin": "^1.7.0",
"webpack": "^4.42.1"
}, },
"scripts": { "scripts": {
"build": "node build/index.js", "build": "node build/index.js",
"build:development": "node build/development.js",
"build:production": "node build/production.js",
"pack": "pkg .", "pack": "pkg .",
"lint": "eslint hydro module --fix" "lint": "eslint build hydro module --fix"
}, },
"pkg": { "pkg": {
"scripts": [ "scripts": [
-1
View File
@@ -1 +0,0 @@