From f9468536debc869e40b2fb1205fa46fb0daf53ad Mon Sep 17 00:00:00 2001 From: masnn Date: Wed, 15 Apr 2020 12:47:55 +0800 Subject: [PATCH] upgrade dep: css-loader 0.28->3.5 del 4.1->5.1 eslint 5.18->6.8 eslint-config-airbnb 17.1->18.1 eslint-loader 2.1->4.0 file-loader 2.0->6.0 fs.extra 8.0->9.0 gulp 2.0->3.0 gulp-nunjucks 4.0->5.0 json-loader 0.5->Removed style-loader 0.23->1.1 uglifyjs-webpack-plugin 1.3->2.2 yargs 13.2->15.3 katex 0.10->0.11 react-monaco-editor 0.35->0.36 timeage-react 1.2->3.0 timeago.js 3.0->4.0 uuid 3.3->7.0 --- README.md | 10 + hydro/error.js | 11 +- hydro/install.js | 1 - hydro/service/server.js | 10 +- templates/layout/html5.html | 2 - ui/.eslintrc.js | 6 +- ui/Entry.js | 4 +- ui/build/config/webpack.js | 25 +- ui/build/plugins/gulpTouch.js | 2 +- .../plugins/webpackStaticManifestPlugin.js | 2 +- ui/build/runGulp.js | 2 +- ui/build/runWebpack.js | 1 - ui/build/utils/mapWebpackUrlPrefix.js | 2 +- ui/components/DOMAttachedObject.js | 6 +- ui/components/calendar/index.js | 16 +- ui/components/emoji/emojify.page.js | 4 +- ui/components/highlighter/highlighter.page.js | 2 +- ui/components/hotkey/hotkey.page.js | 2 +- ui/components/katex/katex.page.js | 6 +- ui/components/katex/katex.styl | 2 +- ui/components/marker/MarkerReactive.js | 2 +- ui/components/menu/menu.page.js | 2 +- .../MessagePadDialogueContentContainer.js | 2 +- .../MessagePadDialogueListContainer.js | 8 +- .../messagepad/MessagePadInputContainer.js | 8 +- ui/components/messagepad/index.js | 2 +- .../messagepad/reducers/dialogues.js | 4 +- ui/components/messagepad/reducers/inputs.js | 2 +- .../messagepad/reducers/isPosting.js | 2 +- .../scratchpad/ScratchpadEditorContainer.js | 8 +- .../scratchpad/ScratchpadPretestContainer.js | 8 +- .../ScratchpadPretestTabPaneContainer.js | 8 +- .../scratchpad/ScratchpadRecordsContainer.js | 2 +- .../ScratchpadRecordsRowContainer.js | 11 +- .../ScratchpadRecordsTableContainer.js | 4 +- .../scratchpad/ScratchpadToolbarContainer.js | 11 +- ui/components/scratchpad/index.js | 12 +- ui/components/scratchpad/reducers/pretest.js | 2 +- ui/components/time/time.page.js | 11 +- ui/components/vote/vote.page.js | 2 +- ui/misc/PageLoader.js | 6 +- ui/package.json | 37 +- ui/pages/domain_manage_role.page.js | 2 +- ui/pages/domain_manage_user.page.js | 4 +- ui/pages/home_messages.page.js | 2 +- ui/pages/homework_main.page.js | 2 +- ui/pages/judge_playground.page.js | 2 +- ui/pages/problem_detail.page.js | 2 +- ui/pages/problem_main.page.js | 8 +- ui/pages/record_detail.page.js | 2 +- ui/pages/record_main.page.js | 2 +- ui/utils/delay.js | 2 +- ui/utils/parseQueryString.js | 2 +- ui/utils/pjax.js | 2 +- ui/yarn.lock | 1506 +++++++---------- 55 files changed, 776 insertions(+), 1032 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 000000000..1851c4ed2 --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# Hydro + +Hydro是一个高效的信息学在线测评系统。 +特点: 易于部署,轻量,功能强大。 + +## 使用方式 + +使用 `docker-compose`(推荐) + +// TODO(WIP) diff --git a/hydro/error.js b/hydro/error.js index 123743b79..555918163 100644 --- a/hydro/error.js +++ b/hydro/error.js @@ -6,6 +6,14 @@ class UserFacingError extends Error { this.params = []; } } +class SystemError extends Error { + constructor(type) { + super(type); + this.code = 500; + // this.stack = ''; + this.params = []; + } +} class BadRequestError extends UserFacingError { constructor(type) { super(type); @@ -154,7 +162,8 @@ module.exports = { ValidationError, ProblemNotFoundError, TrainingNotFoundError, ContestNotFoundError, RecordNotFoundError, SolutionNotFoundError, AlreadyVotedError, ContestNotAttendedError, ContestNotLiveError, - ContestScoreboardHiddenError, ContestAlreadyAttendedError + ContestScoreboardHiddenError, ContestAlreadyAttendedError, UserFacingError, + SystemError }; /* diff --git a/hydro/install.js b/hydro/install.js index 302360e0e..a80762958 100644 --- a/hydro/install.js +++ b/hydro/install.js @@ -52,7 +52,6 @@ async function run() { await coll_user.createIndex('unameLower', { unique: true }); await coll_user.createIndex('mailLower', { sparse: true }); await coll_role.insertMany(builtin.BUILTIN_ROLES); - await coll_user.insertMany(builtin.BUILTIN_USERS); await coll_blacklist.createIndex('expireAt', { expireAfterSeconds: 0 }); await coll_token.createIndex([{ uid: 1 }, { tokenType: 1 }, { updateAt: -1 }], { sparse: true }); await coll_token.createIndex('expireAt', { expireAfterSeconds: 0 }); diff --git a/hydro/service/server.js b/hydro/service/server.js index 571f09981..7626d2670 100644 --- a/hydro/service/server.js +++ b/hydro/service/server.js @@ -1,6 +1,7 @@ const path = require('path'), { ObjectID } = require('bson'), + { MongoError } = require('mongodb'), Koa = require('koa'), morgan = require('koa-morgan'), Body = require('koa-body'), @@ -14,7 +15,7 @@ const token = require('../model/token'), opcount = require('../model/opcount'), { UserNotFoundError, BlacklistedError, PermissionError, - NotFoundError } = require('../error'); + NotFoundError, UserFacingError } = require('../error'); const options = require('../options'); let http = options.listen.https ? require('https') : require('http'); @@ -60,6 +61,7 @@ class Handler { url_prefix: '/' }; this._handler = {}; + this.session = {}; } renderHTML(name, context) { console.time(name); @@ -130,7 +132,7 @@ class Handler { await this.renderBody(); } catch (error) { if (this.preferJson) this.response.body = { error }; - else await this.render('bsod.html', { error }); + else await this.render(error instanceof UserFacingError ? 'error.html' : 'bsod.html', { error }); } await this.putResponse(); await this.saveCookie(); @@ -186,9 +188,9 @@ class Handler { async onerror(error) { console.error(error.message, error.params); console.error(error.stack); - this.response.template = 'error.html'; + this.response.template = error instanceof UserFacingError ? 'error.html' : 'bsod.html'; this.response.body = { error }; - await this.___cleanup(); + await this.___cleanup().catch(() => { }); } } function Route(route, handler) { diff --git a/templates/layout/html5.html b/templates/layout/html5.html index b1d866da2..f5e0ddb16 100644 --- a/templates/layout/html5.html +++ b/templates/layout/html5.html @@ -22,7 +22,6 @@ - {{ handler.render_title(page_title) }} - diff --git a/ui/.eslintrc.js b/ui/.eslintrc.js index 90f69915f..fe3b72412 100644 --- a/ui/.eslintrc.js +++ b/ui/.eslintrc.js @@ -89,6 +89,10 @@ module.exports = { "react/forbid-prop-types": "off", "jsx-a11y/no-static-element-interactions": "off", "jsx-a11y/anchor-is-valid": "off", - "jsx-a11y/click-events-have-key-events": "off" + "jsx-a11y/click-events-have-key-events": "off", + "max-classes-per-file": "off", + "react/jsx-props-no-spreading": "off", + "react/static-property-placement": "off", + "no-param-reassign": "off" } }; diff --git a/ui/Entry.js b/ui/Entry.js index 2d5b4b660..eb5953b86 100644 --- a/ui/Entry.js +++ b/ui/Entry.js @@ -35,8 +35,8 @@ function buildSequence(pages, type) { } } return pages - .filter(p => p && p[`${type}Loading`]) - .map(p => ({ + .filter((p) => p && p[`${type}Loading`]) + .map((p) => ({ page: p, func: p[`${type}Loading`], type, diff --git a/ui/build/config/webpack.js b/ui/build/config/webpack.js index 7e9bb6f69..bba073266 100644 --- a/ui/build/config/webpack.js +++ b/ui/build/config/webpack.js @@ -37,12 +37,21 @@ export default function (env = {}) { return { loader: 'babel-loader', options: { - ...require(root('package.json')).babelForProject, + ...require(root('package.json')).babelForProject, // eslint-disable-line cacheDirectory, }, }; } + function cssLoader() { + return { + loader: 'css-loader', + options: { + importLoaders: 1, + }, + }; + } + function postcssLoader() { return { loader: 'postcss-loader', @@ -64,9 +73,7 @@ export default function (env = {}) { function extractCssLoader() { return { loader: ExtractCssPlugin.loader, - options: { - publicPath: '/public/path/to/', - }, + options: { publicPath: '/' }, }; } @@ -127,12 +134,12 @@ export default function (env = {}) { }, { test: /\.styl$/, - use: [extractCssLoader(), 'css-loader?importLoaders=1', postcssLoader(), 'stylus-loader'] + use: [extractCssLoader(), cssLoader(), postcssLoader(), 'stylus-loader'] , }, { test: /\.css$/, - use: [extractCssLoader(), 'css-loader?importLoaders=1', postcssLoader()] + use: [extractCssLoader(), cssLoader(), postcssLoader()] , }, ], @@ -143,6 +150,8 @@ export default function (env = {}) { }, }, plugins: [ + new webpack.ProgressPlugin(), + new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery', @@ -179,7 +188,7 @@ export default function (env = {}) { default: { preferPathResolver: 'webpack', use: [ - require('rupture')(), + require('rupture')(), // eslint-disable-line global-require ], import: [ '~vj/common/common.inc.styl', @@ -215,7 +224,7 @@ export default function (env = {}) { new webpack.LoaderOptionsPlugin({ options: { context: root(), - customInterpolateName: (url, name, options) => beautifyOutputUrl(url), + customInterpolateName: (url) => beautifyOutputUrl(url), }, }), diff --git a/ui/build/plugins/gulpTouch.js b/ui/build/plugins/gulpTouch.js index bf761c9e0..c1ab2d1b6 100644 --- a/ui/build/plugins/gulpTouch.js +++ b/ui/build/plugins/gulpTouch.js @@ -24,7 +24,7 @@ export default function touch(mtime) { return; } touchFile(file) - .catch(err => this.emit('error', err)) + .catch((err) => this.emit('error', err)) .then(() => { this.push(file); callback(); diff --git a/ui/build/plugins/webpackStaticManifestPlugin.js b/ui/build/plugins/webpackStaticManifestPlugin.js index d9b4d8721..03f477b72 100644 --- a/ui/build/plugins/webpackStaticManifestPlugin.js +++ b/ui/build/plugins/webpackStaticManifestPlugin.js @@ -15,7 +15,7 @@ export default class StaticManifestPlugin { .map((asset) => { const { name } = asset; // Skip files listed in ignore - if (_.some(this.ignore, ignorePattern => name.indexOf(ignorePattern) > -1)) { + if (_.some(this.ignore, (ignorePattern) => name.indexOf(ignorePattern) > -1)) { return null; } // Skip calculating hash for names like ?xxxx diff --git a/ui/build/runGulp.js b/ui/build/runGulp.js index eddd13750..cba99692d 100644 --- a/ui/build/runGulp.js +++ b/ui/build/runGulp.js @@ -21,7 +21,7 @@ export default async function ({ watch, production }) { log(chalk.green('Finished: %s'), chalk.reset(name)); taskList[uid] = false; - if (Object.values(taskList).filter(b => b).length === 0) { + if (Object.values(taskList).filter((b) => b).length === 0) { if (watch) { gulpTasks.watch(); } diff --git a/ui/build/runWebpack.js b/ui/build/runWebpack.js index de5a03ff8..5cc6a7949 100644 --- a/ui/build/runWebpack.js +++ b/ui/build/runWebpack.js @@ -6,7 +6,6 @@ import webpackConfig from './config/webpack'; export default function ({ watch, production }) { const compiler = webpack(webpackConfig({ watch, production })); - compiler.apply(new webpack.ProgressPlugin()); function compilerCallback(err, stats) { if (err) { diff --git a/ui/build/utils/mapWebpackUrlPrefix.js b/ui/build/utils/mapWebpackUrlPrefix.js index ae7fa7241..9b8343b65 100644 --- a/ui/build/utils/mapWebpackUrlPrefix.js +++ b/ui/build/utils/mapWebpackUrlPrefix.js @@ -3,7 +3,7 @@ import _ from 'lodash'; export default function mapWebpackUrlPrefix(mapList) { const rules = mapList.map((mappingRule) => { - const regex = mappingRule.prefix.split('/').map(s => _.escapeRegExp(s)).join('[\\/\\\\]'); + const regex = mappingRule.prefix.split('/').map((s) => _.escapeRegExp(s)).join('[\\/\\\\]'); return { from: new RegExp(`^${regex}`, mappingRule.flag), to: mappingRule.replace, diff --git a/ui/components/DOMAttachedObject.js b/ui/components/DOMAttachedObject.js index bb55e70ad..59e381c14 100644 --- a/ui/components/DOMAttachedObject.js +++ b/ui/components/DOMAttachedObject.js @@ -57,7 +57,7 @@ export default class DOMAttachedObject { .find(this.DOMAttachSelector) .addBack(this.DOMAttachSelector) .get() - .forEach(element => this.getOrConstruct($(element), ...args)); + .forEach((element) => this.getOrConstruct($(element), ...args)); if (process.env.NODE_ENV !== 'production') { console.timeEnd(`DOMAttachedObject[${this.DOMAttachKey}]: attachAll`); } @@ -96,8 +96,8 @@ export default class DOMAttachedObject { throw new Error(`'DOMAttachSelector' should be specified`); } } - $(document).on('vjContentNew', e => this.attachAll(e.target)); - $(document).on('vjContentRemove', e => this.detachAll(e.target)); + $(document).on('vjContentNew', (e) => this.attachAll(e.target)); + $(document).on('vjContentRemove', (e) => this.detachAll(e.target)); if (attach) { this.attachAll(); } diff --git a/ui/components/calendar/index.js b/ui/components/calendar/index.js index 3cd464d1f..9fde50323 100644 --- a/ui/components/calendar/index.js +++ b/ui/components/calendar/index.js @@ -27,7 +27,7 @@ export default class Calendar {
`); - this.events = events.map(ev => ({ + this.events = events.map((ev) => ({ ...ev, beginAt: moment(ev.beginAt), endAt: moment(ev.endAt), @@ -258,11 +258,11 @@ export default class Calendar { // layout banners const layout = bannersByWeek - .map(banners => _ + .map((banners) => _ .sortBy(banners, [ - banner => banner.beginAt.valueOf(), - banner => (banner.beginTrunc ? 0 : 1), // truncated events first - banner => -banner.endAt.valueOf(), // long events first + (banner) => banner.beginAt.valueOf(), + (banner) => (banner.beginTrunc ? 0 : 1), // truncated events first + (banner) => -banner.endAt.valueOf(), // long events first ])) .map((banners) => { const dayBitmap = _ @@ -274,12 +274,12 @@ export default class Calendar { // find available space const vIndexMax = _.max(_ .range(beginDay, endDay + 1) - .map(day => dayBitmap[day].length)); + .map((day) => dayBitmap[day].length)); let vIndex = 0; for (; vIndex < vIndexMax; ++vIndex) { if (_.every(_ .range(beginDay, endDay + 1) - .map(day => !dayBitmap[day][vIndex]) // eslint-disable-line no-loop-func + .map((day) => !dayBitmap[day][vIndex]) // eslint-disable-line no-loop-func )) { // eslint-disable-line function-paren-newline break; } @@ -290,7 +290,7 @@ export default class Calendar { } }); // merge adjacent cells and arrange banners by vertical index - const vMaxLength = _.max(_.range(0, 7).map(day => dayBitmap[day].length)); + const vMaxLength = _.max(_.range(0, 7).map((day) => dayBitmap[day].length)); const weekBanners = _ .fill(new Array(vMaxLength), 1) .map(() => []); diff --git a/ui/components/emoji/emojify.page.js b/ui/components/emoji/emojify.page.js index b911fe28b..5187acef0 100644 --- a/ui/components/emoji/emojify.page.js +++ b/ui/components/emoji/emojify.page.js @@ -7,7 +7,7 @@ function runEmojify($container) { emojify.run($container[0]); return; } - $container.find('[data-emoji-enabled]').get().forEach(element => emojify.run(element)); + $container.find('[data-emoji-enabled]').get().forEach((element) => emojify.run(element)); } const emojifyPage = new AutoloadPage('emojifyPage', () => { @@ -15,7 +15,7 @@ const emojifyPage = new AutoloadPage('emojifyPage', () => { img_dir: `${UiContext.cdn_prefix}img/emoji`, }); runEmojify($('body')); - $(document).on('vjContentNew', e => runEmojify($(e.target))); + $(document).on('vjContentNew', (e) => runEmojify($(e.target))); }); export default emojifyPage; diff --git a/ui/components/highlighter/highlighter.page.js b/ui/components/highlighter/highlighter.page.js index 297003b64..43b693e1d 100644 --- a/ui/components/highlighter/highlighter.page.js +++ b/ui/components/highlighter/highlighter.page.js @@ -7,7 +7,7 @@ const highlighterPage = new AutoloadPage('highlighterPage', () => { prismjs.highlightBlocks($container); } runHighlight($('body')); - $(document).on('vjContentNew', e => runHighlight($(e.target))); + $(document).on('vjContentNew', (e) => runHighlight($(e.target))); }); }); diff --git a/ui/components/hotkey/hotkey.page.js b/ui/components/hotkey/hotkey.page.js index a72341573..ce99a2cb2 100644 --- a/ui/components/hotkey/hotkey.page.js +++ b/ui/components/hotkey/hotkey.page.js @@ -54,7 +54,7 @@ function testElementHotkey(hotkey, $element, attr) { const hotkeyPage = new AutoloadPage('hotkeyPage', () => { $(document).on('keydown', (ev) => { - const hotkey = ['alt', 'ctrl', 'shift'].filter(modifyKey => ev[`${modifyKey}Key`]); + const hotkey = ['alt', 'ctrl', 'shift'].filter((modifyKey) => ev[`${modifyKey}Key`]); if (ev.metaKey && !ev.ctrlKey) { hotkey.push('ctrl'); } diff --git a/ui/components/katex/katex.page.js b/ui/components/katex/katex.page.js index e7dd9a86a..c55a84b5f 100644 --- a/ui/components/katex/katex.page.js +++ b/ui/components/katex/katex.page.js @@ -4,12 +4,12 @@ import 'katex/dist/katex.min.css'; import './katex.styl'; const katexPage = new AutoloadPage('katexPage', () => { - import('katex/dist/contrib/auto-render.min.js').then((renderKatex) => { + import('katex/dist/contrib/auto-render.min.js').then(({ default: katex }) => { function runKatex($containers) { - $containers.get().forEach(container => renderKatex(container)); + $containers.get().forEach((container) => katex(container)); } runKatex($('.typo')); - $(document).on('vjContentNew', e => runKatex($(e.target).find('.typo'))); + $(document).on('vjContentNew', (e) => runKatex($(e.target).find('.typo'))); }); }); diff --git a/ui/components/katex/katex.styl b/ui/components/katex/katex.styl index bcc2a6009..9d6fb1608 100644 --- a/ui/components/katex/katex.styl +++ b/ui/components/katex/katex.styl @@ -1,2 +1,2 @@ .katex - font-size: 1em + font-size: 1.2em diff --git a/ui/components/marker/MarkerReactive.js b/ui/components/marker/MarkerReactive.js index 75968c98e..897c7cca5 100644 --- a/ui/components/marker/MarkerReactive.js +++ b/ui/components/marker/MarkerReactive.js @@ -12,7 +12,7 @@ export default class MarkerReactive extends DOMAttachedObject { } static initAll() { - $('[data-marker-enabled]').get().forEach(dom => MarkerReactive.initFromDOM($(dom))); + $('[data-marker-enabled]').get().forEach((dom) => MarkerReactive.initFromDOM($(dom))); } constructor($target) { diff --git a/ui/components/menu/menu.page.js b/ui/components/menu/menu.page.js index e8885a490..7c781b91f 100644 --- a/ui/components/menu/menu.page.js +++ b/ui/components/menu/menu.page.js @@ -8,7 +8,7 @@ function expandMenu($menu) { async function expandAllMenus() { await delay(200); - $('.menu.collapsed').get().forEach(menu => expandMenu($(menu))); + $('.menu.collapsed').get().forEach((menu) => expandMenu($(menu))); } const menuPage = new AutoloadPage('menuPage', () => { diff --git a/ui/components/messagepad/MessagePadDialogueContentContainer.js b/ui/components/messagepad/MessagePadDialogueContentContainer.js index f8f14a1aa..d682a61ab 100644 --- a/ui/components/messagepad/MessagePadDialogueContentContainer.js +++ b/ui/components/messagepad/MessagePadDialogueContentContainer.js @@ -9,7 +9,7 @@ import 'jquery.easing'; import i18n from 'vj/utils/i18n'; import Message from './MessageComponent'; -const mapStateToProps = state => ({ +const mapStateToProps = (state) => ({ activeId: state.activeId, item: state.activeId !== null ? state.dialogues[state.activeId] diff --git a/ui/components/messagepad/MessagePadDialogueListContainer.js b/ui/components/messagepad/MessagePadDialogueListContainer.js index 4e81af36f..764ecf30a 100644 --- a/ui/components/messagepad/MessagePadDialogueListContainer.js +++ b/ui/components/messagepad/MessagePadDialogueListContainer.js @@ -5,12 +5,12 @@ import 'jquery-scroll-lock'; import ListItem from './DialogueListItemComponent'; -const mapStateToProps = state => ({ +const mapStateToProps = (state) => ({ activeId: state.activeId, dialogues: state.dialogues, }); -const mapDispatchToProps = dispatch => ({ +const mapDispatchToProps = (dispatch) => ({ handleClick(id) { dispatch({ type: 'DIALOGUES_SWITCH_TO', @@ -28,14 +28,14 @@ export default class MessagePadDialogueListContainer extends React.PureComponent render() { const orderedDialogues = _.orderBy( _.values(this.props.dialogues), - dialogue => (dialogue.isPlaceholder + (dialogue) => (dialogue.isPlaceholder ? Number.POSITIVE_INFINITY : _.maxBy(dialogue.reply, 'at').at), 'desc', ); return (
    - {_.map(orderedDialogues, dialogue => ( + {_.map(orderedDialogues, (dialogue) => ( ({ +const mapStateToProps = (state) => ({ activeId: state.activeId, isPosting: state.activeId !== null ? state.isPosting[state.activeId] @@ -16,7 +16,7 @@ const mapStateToProps = state => ({ : '', }); -const mapDispatchToProps = dispatch => ({ +const mapDispatchToProps = (dispatch) => ({ handleChange(id, value) { if (id === null) { return; @@ -119,8 +119,8 @@ export default class MessagePadInputContainer extends React.PureComponent { ref="input" disabled={this.props.isPosting} value={this.props.inputValue} - onKeyDown={ev => this.handleKeyDown(ev)} - onChange={ev => this.props.handleChange(this.props.activeId, ev.target.value)} + onKeyDown={(ev) => this.handleKeyDown(ev)} + onChange={(ev) => this.props.handleChange(this.props.activeId, ev.target.value)} />