diff --git a/.eslintrc.js b/.eslintrc.js index f08ae2e9..fe18c02f 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,4 +1,5 @@ module.exports = { + root: true, env: { commonjs: true, node: true, @@ -14,19 +15,19 @@ module.exports = { ecmaVersion: 2018, }, rules: { - "indent": ["warn", 4], - "no-plusplus": "off", - "no-console": "off", - "no-extend-native": "off", - "no-underscore-dangle": "off", - "no-restricted-syntax": "off", - "max-classes-per-file": "off", - "radix": "off", - "guard-for-in": "off", - "no-param-reassign": "off", - "global-require": "off", - "no-nested-ternary": "off", - "no-multi-assign": "off", - "no-return-await": "off", + indent: ['warn', 4], + 'no-plusplus': 'off', + 'no-console': 'off', + 'no-extend-native': 'off', + 'no-underscore-dangle': 'off', + 'no-restricted-syntax': 'off', + 'max-classes-per-file': 'off', + radix: 'off', + 'guard-for-in': 'off', + 'no-param-reassign': 'off', + 'global-require': 'off', + 'no-nested-ternary': 'off', + 'no-multi-assign': 'off', + 'no-return-await': 'off', }, }; diff --git a/README.md b/README.md index 98d92505..69917635 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ Hydro是一个高效的信息学在线测评系统。 Hydro 的界面基于 Vijos 二次开发。 +如果您认为本项目有价值,欢迎 star 。 + ## 鸣谢 排名不分先后,按照链接字典序 diff --git a/hydro/error.js b/hydro/error.js index 6a050911..e1c4cf51 100644 --- a/hydro/error.js +++ b/hydro/error.js @@ -274,96 +274,55 @@ class FileTooLongError(ValidationError): def message(self): return 'The uploaded file is too long.' - class FileTypeNotAllowedError(ValidationError): @property def message(self): return 'This type of files are not allowed to be uploaded.' - -class UnknownFieldError(ForbiddenError): - @property - def message(self): - return 'Unknown field {0}.' - class CsrfTokenError(ForbiddenError): pass - class InvalidOperationError(ForbiddenError): pass - class InvalidTokenDigestError(ForbiddenError): pass - class CurrentPasswordError(ForbiddenError): @property def message(self): return "Current password doesn't match." - class DiscussionCategoryAlreadyExistError(ForbiddenError): @property def message(self): return 'Discussion category {1} already exists.' - class DiscussionCategoryNotFoundError(NotFoundError): @property def message(self): return 'Discussion category {1} not found.' - class DiscussionNodeAlreadyExistError(ForbiddenError): @property def message(self): return 'Discussion node {1} already exists.' - class TrainingRequirementNotSatisfiedError(ForbiddenError): @property def message(self): return 'Training requirement is not satisfied.' - class UsageExceededError(ForbiddenError): @property def message(self): return 'Usage exceeded.' - class InvalidArgumentError(BadRequestError): @property def message(self): return 'Argument {0} is invalid.' - -class BatchCopyLimitExceededError(ForbiddenError): - @property - def message(self): - return 'Only {0} problems can be copied in one request, got {1}.' - - -class UpgradeLockAcquireError(Error): - @property - def message(self): - return 'Failed to acquire the upgrade lock. There may be another ongoing upgrade process, or a previous process is exited unexpectedly.' - - -class UpgradeLockReleaseError(Error): - @property - def message(self): - return 'Failed to release the upgrade lock. The database is malformed during the upgrade.' - - -class DatabaseVersionMismatchError(Error): - @property - def message(self): - return 'Database version mismatch, got {0}, expect {1}. You need to invoke database upgrades.' - - class SendMailError(UserFacingError): @property def message(self): diff --git a/hydro/handler/discussion.js b/hydro/handler/discussion.js index 74d3c788..0626a08b 100644 --- a/hydro/handler/discussion.js +++ b/hydro/handler/discussion.js @@ -31,13 +31,15 @@ class DiscussionHandler extends Handler { type = discussion.typeDisplay[this.ddoc.parentType]; name = this.ddoc.parentId; if (drid) { - this.drdoc = await discussion.getReply(domainId, drid, did); + this.drdoc = await discussion.getReply(domainId, drid); if (!this.drdoc) throw new DiscussionNotFoundError(drid); - if (this.drdoc.parent !== this.ddoc._id) throw new DocumentNotFoundError(drid); + if (!this.drdoc.parentId.equals(this.ddoc._id)) { + throw new DocumentNotFoundError(drid); + } if (drrid) { [, this.drrdoc] = await discussion.getTailReply(domainId, drid, drrid); if (!this.drrdoc) throw new DiscussionNotFoundError(drrid); - if (this.drrdoc.parent !== this.drdoc._id) { + if (!this.drrdoc.parentId.equals(this.drdoc._id)) { throw new DocumentNotFoundError(drid); } } @@ -196,7 +198,7 @@ class DiscussionDetailHandler extends DiscussionHandler { async postDeleteReply({ domainId, drid }) { if (this.drdoc.owner !== this.user._id) this.checkPerm(PERM_DELETE_DISCUSSION_REPLY); - await discussion.deleteReply(domainId, drid); + await discussion.delReply(domainId, drid); this.back(); } @@ -210,20 +212,18 @@ class DiscussionDetailHandler extends DiscussionHandler { async postDeleteTailReply({ domainId, drid, drrid }) { if (this.drrdoc.owner !== this.user._id) this.checkPerm(PERM_DELETE_DISCUSSION_REPLY); - await discussion.deleteTailReply(domainId, drid, drrid); + await discussion.delTailReply(domainId, drid, drrid); this.back(); } - async postStar({ domainId, did, star }) { + async postStar({ domainId, did }) { await discussion.setStar(domainId, did, this.user._id, true); - this.response.body = { star }; - this.response.direct = this.request.path; + this.back({ star: true }); } - async postUnstar({ domainId, did, star }) { + async postUnstar({ domainId, did }) { await discussion.setStar(domainId, did, this.user._id, false); - this.response.body = { star }; - this.response.direct = this.request.path; + this.back({ star: false }); } } @@ -241,7 +241,6 @@ class DiscussionReplyRawHandler extends DiscussionHandler { } } - class DiscussionTailReplyRawHandler extends DiscussionHandler { async get() { this.response.type = 'text/markdown'; @@ -263,22 +262,24 @@ class DiscussionEditHandler extends DiscussionHandler { this.response.body = { ddoc: this.ddoc, path }; } - async post({ - domainId, did, title, content, highlight, operation, + async postUpdate({ + domainId, did, title, content, highlight, }) { - if (operation) return; if (this.ddoc.owner !== this.user._id) this.checkPerm(PERM_EDIT_DISCUSSION); if (highlight && !this.ddoc.highlight) this.checkPerm(PERM_HIGHLIGHT_DISCUSSION); await discussion.edit(domainId, did, title, content, highlight); this.response.body = { did }; - this.response.redirect = `/discuss/${did}`; + this.response.redirect = this.url('discussion_detail', { did }); } async postDelete({ domainId, did }) { if (this.ddoc.owner !== this.user._id) this.checkPerm(PERM_DELETE_DISCUSSION); - await discussion.delete(domainId, did); + await discussion.del(domainId, did); this.response.body = { type: this.ddoc.parentType, parent: this.ddoc.parentId }; - this.response.redirect = `/discuss/${this.ddoc.parentType}/${this.ddoc.parentId}`; + this.response.redirect = this.url('discussion_node', { + type: discussion.typeDisplay[this.ddoc.parentType], + name: this.ddoc.parentId, + }); } } @@ -287,7 +288,7 @@ async function apply() { Route('discussion_detail', '/discuss/:did', DiscussionDetailHandler); Route('discussion_edit', '/discuss/:did/edit', DiscussionEditHandler); Route('discussion_detail', '/discuss/:did/raw', DiscussionDetailRawHandler); - Route('discission_reply_raw', '/discuss/:did/:drid/raw', DiscussionReplyRawHandler); + Route('discussion_reply_raw', '/discuss/:did/:drid/raw', DiscussionReplyRawHandler); Route('discussion_tail_reply_raw', '/discuss/:did/:drid/:drrid/raw', DiscussionTailReplyRawHandler); Route('discussion_node', '/discuss/:type/:name', DiscussionNodeHandler); Route('discussion_create', '/discuss/:type/:name/create', DiscussionCreateHandler); diff --git a/hydro/handler/manage.js b/hydro/handler/manage.js index 94b87a06..b7996491 100644 --- a/hydro/handler/manage.js +++ b/hydro/handler/manage.js @@ -14,7 +14,6 @@ class SystemHandler extends Handler { this.response.body = { path: [ ['Hydro', 'homepage'], - ['manage_main', null], ], }; } @@ -118,6 +117,8 @@ class SystemSettingHandler extends SystemHandler { this.response.body.current = {}; this.response.body.settings = setting.SYSTEM_SETTINGS; for (const s of this.response.body.settings) { + // FIXME no-await-in-loop + // eslint-disable-next-line no-await-in-loop this.response.body.current[s.key] = await system.get(s.key); } } diff --git a/hydro/handler/misc.js b/hydro/handler/misc.js index abb83726..801af120 100644 --- a/hydro/handler/misc.js +++ b/hydro/handler/misc.js @@ -1,4 +1,3 @@ - const { PERM_JUDGE, PERM_LOGGEDIN } = require('../permission'); const file = require('../model/file'); const user = require('../model/user'); diff --git a/hydro/handler/problem.js b/hydro/handler/problem.js index c7f71b43..9cb35811 100644 --- a/hydro/handler/problem.js +++ b/hydro/handler/problem.js @@ -1,4 +1,3 @@ -const fs = require('fs'); const paginate = require('../lib/paginate'); const validator = require('../lib/validator'); const file = require('../model/file'); diff --git a/hydro/handler/user.js b/hydro/handler/user.js index a87f7edb..e105fdff 100644 --- a/hydro/handler/user.js +++ b/hydro/handler/user.js @@ -61,7 +61,8 @@ class UserRegisterHandler extends Handler { ); if (await system.get('smtp.user')) { const m = await this.renderHTML('user_register_mail.html', { - url: `${await system.get('server.url')}/register/${t[0]}`, + path: `register/${t[0]}`, + url_prefix: await system.get('server.url'), }); await sendMail(mail, 'Sign Up', 'user_register_mail', m); this.response.template = 'user_register_mail_sent.html'; diff --git a/hydro/lib/validator.js b/hydro/lib/validator.js index 2fc6b050..c4d23376 100644 --- a/hydro/lib/validator.js +++ b/hydro/lib/validator.js @@ -59,39 +59,31 @@ ID_RE = re.compile(r'[^\\/\s\u3000]([^\\/\n\r]*[^\\/\s\u3000])?') def is_id(s): return bool(ID_RE.fullmatch(s)) - def check_category_name(s): if not is_id(s): raise error.ValidationError('category_name') - def check_node_name(s): if not is_id(s): raise error.ValidationError('node_name') - def is_intro(s): return isinstance(s, str) and 0 < len(s.strip()) < 500 - def check_intro(s): if not is_intro(s): raise error.ValidationError('intro') - def is_description(s): return isinstance(s, str) and len(s) < 65536 - def check_description(s): if not is_description(s): raise error.ValidationError('description') - def is_lang(i): return i in constant.language.LANG_TEXTS - def check_lang(i): if not is_lang(i): raise error.ValidationError('lang') diff --git a/hydro/loader.js b/hydro/loader.js index f4a6554d..b0f2ed5d 100644 --- a/hydro/loader.js +++ b/hydro/loader.js @@ -96,7 +96,10 @@ async function load() { process.stdin.setEncoding('utf8'); process.stdin.on('data', (input) => { if (input[0] === '@') { - cluster.workers[1].send({ event: 'run', command: input.substr(1, input.length - 1) }); + for (const i in cluster.workers) { + cluster.workers[i].send({ event: 'run', command: input.substr(1, input.length - 1) }); + break; + } } else { executeCommand(input); } diff --git a/hydro/model/contest.js b/hydro/model/contest.js index c0bac69d..d310988b 100644 --- a/hydro/model/contest.js +++ b/hydro/model/contest.js @@ -186,7 +186,6 @@ const oi = { rank: (tdocs) => ranked(tdocs, (a, b) => a.score === b.score), }; - const RULES = { acm, oi, }; diff --git a/hydro/model/user.js b/hydro/model/user.js index 59351ebd..ed58396a 100644 --- a/hydro/model/user.js +++ b/hydro/model/user.js @@ -98,7 +98,7 @@ async function getByEmail(domainId, mail, ignoreMissing = false) { function setPassword(uid, password) { const salt = String.random(); return coll.findOneAndUpdate({ _id: uid }, { - $set: { salt, hash: pwhash.hash(password, salt), hashType: 'hydro' }, + $set: { salt, hash: pwhash(password, salt), hashType: 'hydro' }, }); } diff --git a/hydro/service/server.js b/hydro/service/server.js index e8bb403e..fca5c395 100644 --- a/hydro/service/server.js +++ b/hydro/service/server.js @@ -194,7 +194,7 @@ class Handler { } back(body) { - if (body) this.response.body = body; + this.response.body = body || this.response.body || {}; this.response.redirect = this.request.headers.referer || '/'; } @@ -225,6 +225,7 @@ class Handler { if (anchor) return `${res}#${anchor}`; } catch (e) { console.error(e.message); + console.log(name, kwargs); } return res; } @@ -284,16 +285,15 @@ class Handler { async putResponse() { if (this.response.disposition) this.ctx.set('Content-Disposition', this.response.disposition); - if (this.response.redirect) { - if (!this.preferJson) { - this.ctx.response.type = 'application/octet-stream'; - this.ctx.response.status = 302; - this.ctx.redirect(this.response.redirect); - } else { + if (this.response.redirect && !this.preferJson) { + this.ctx.response.type = 'application/octet-stream'; + this.ctx.response.status = 302; + this.ctx.redirect(this.response.redirect); + } else { + if (this.response.body.redirect) { this.response.body = this.response.body || {}; this.response.body.url = this.response.redirect; } - } else { if (this.response.body != null) { this.ctx.response.body = this.response.body; this.ctx.response.status = this.response.status || 200; diff --git a/hydro/utils.js b/hydro/utils.js index 4206b9c4..717a252e 100644 --- a/hydro/utils.js +++ b/hydro/utils.js @@ -1,8 +1,6 @@ -const { hasIn } = require("lodash"); - const dict = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'; -String.random = function random(digit) { +String.random = function random(digit = 32) { let str = ''; for (let i = 1; i <= digit; i++) str += dict[Math.floor(Math.random() * 62)]; return str; diff --git a/locales/en.yaml b/locales/en.yaml index 752fb1c1..3ea82366 100644 --- a/locales/en.yaml +++ b/locales/en.yaml @@ -10,7 +10,14 @@ discussion_edit: Discussion Edit discussion_main: Discussion discussion_node: Discussion display_name: Display Name +domain_dashboard: Dashboard +domain_discussion: Discussion Nodes +domain_edit: Edit Info +domain_join_applications: Join Applications domain_main: Main +domain_permission: System Permission +domain_role: System Role +domain_user: System User fs_upload: File Upload home_account: Account Settings home_domain_account: Profile @ Domain @@ -20,6 +27,7 @@ home_file: My Files home_messages: Messages home_preference: Preference home_security: Security +homepage: Home homework_create: Homework Create homework_detail_problem_submit: Submit Homework Problem homework_edit: Homework Edit @@ -27,15 +35,9 @@ homework_main: Homework homework_scoreboard: Scoreboard judge_playground: Judge Playground main: Home -manage_dashboard: Dashboard -manage_discussion: Discussion Nodes -manage_edit: Edit Info -manage_join_applications: Join Applications manage_module: Module Management -manage_permission: System Permission -manage_role: System Role +manage_script: Scripts manage_setting: System Settings -manage_user: System User manage: System Manage no_translation_warn:
This part of content is under translation.
page.problem_detail.sidebar.show_category: Click to Show @@ -53,6 +55,7 @@ perm_record: Records perm_training: Trainings problem_create: Problem Create problem_edit: Problem Edit +problem_import: Import Problem problem_main: Problem Set problem_settings: Problem Settings problem_solution: Problem Solution @@ -60,6 +63,7 @@ problem_statistics: Problem Statistics problem_submit: Problem Submit problem-category-delim: '|' problem-subcategory-delim: ', ' +ranking: Ranking record_detail: Record Detail record_main: Judging Queue setting_customize: Customize @@ -70,7 +74,7 @@ setting_info: Personal Info setting_preference: Preference setting_privacy: Privacy setting_usage: Usage Preference -timeago_locale: en +timeago_locale: en_US training_create: Training Create training_edit: Training Edit training_main: Training diff --git a/locales/zh_CN.yaml b/locales/zh_CN.yaml index f5316a85..2997af22 100644 --- a/locales/zh_CN.yaml +++ b/locales/zh_CN.yaml @@ -506,6 +506,7 @@ Problem Solution: 题解列表 Problem Tags Visibility: 题目标签可见性 problem_create: 创建题目 problem_edit: 编辑题目 +problem_import: 导入题目 problem_main: 题库 problem_settings: 题目设置 problem_solution: 题解 @@ -762,6 +763,7 @@ WeChat Visibility: 微信可见性 WeChat: 微信 Week: 周 What is domain?: 什么是域? +What is this?: 这是什么? What's domain?: 什么是域? What's script?: 什么是脚本? What's this?: 这是什么? diff --git a/package.json b/package.json index 9df8ac83..5bc90165 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "axios": "^0.19.0", "bson": "^4.0.2", "detect-browser": "^5.1.1", + "eslint": "7.2.0", "js-yaml": "^3.13.1", "koa": "^2.12.1", "koa-body": "^4.2.0", @@ -36,7 +37,6 @@ "yargs": "^15.3.1" }, "devDependencies": { - "eslint": "^7.2.0", "eslint-config-airbnb-base": "^14.2.0", "eslint-plugin-import": "^2.20.2", "friendly-errors-webpack-plugin": "^1.7.0", @@ -59,4 +59,4 @@ ".build/module/**" ] } -} \ No newline at end of file +} diff --git a/templates/components/comments_discussion.html b/templates/components/comments_discussion.html index cd1ef801..3b3bf170 100644 --- a/templates/components/comments_discussion.html +++ b/templates/components/comments_discussion.html @@ -14,7 +14,7 @@ {{ reply_operations(doc, rdoc) }} -
+
{{ rdoc['content']|markdown|safe }}
@@ -33,12 +33,12 @@ {% endif %} {% if doc.owner == handler.state.user._id or handler.hasPerm(reply_edit_perm) %} {% endif %} {% if doc.owner == handler.state.user._id or handler.hasPerm(reply_delete_perm) %} {% endif %}
@@ -92,7 +92,7 @@
- +