From 3c5fdb90325dc460c90bd1cd3a1615aec6f40197 Mon Sep 17 00:00:00 2001 From: masnn Date: Fri, 15 May 2020 13:10:41 +0800 Subject: [PATCH] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E5=8F=AF=E4=BB=A5=E4=B8=8E?= =?UTF-8?q?=E5=90=8C=E4=B8=80=E7=94=A8=E6=88=B7=E5=88=9B=E5=BB=BA=E5=A4=9A?= =?UTF-8?q?=E4=B8=AA=E4=BC=9A=E8=AF=9D=E7=9A=84=E7=89=B9=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hydro/handler/home.js | 34 +++++------ hydro/handler/import.js | 4 +- hydro/handler/problem.js | 6 +- hydro/handler/record.js | 20 ++----- hydro/model/message.js | 56 +++++++++---------- hydro/model/token.js | 2 +- hydro/model/user.js | 7 ++- hydro/service/server.js | 10 ++-- module/migrate-vijos/handler.js | 2 +- .../{handler.js => lib.js} | 53 ++++++++++++------ module/problem-import-syzoj/locale/zh_CN.yaml | 9 ++- .../MessagePadDialogueContentContainer.js | 2 +- .../MessagePadDialogueListContainer.js | 4 +- .../messagepad/MessagePadInputContainer.js | 24 ++++---- .../messagepad/reducers/dialogues.js | 2 +- ui/components/scratchpad/reducers/records.js | 3 +- 16 files changed, 120 insertions(+), 118 deletions(-) rename module/problem-import-syzoj/{handler.js => lib.js} (54%) diff --git a/hydro/handler/home.js b/hydro/handler/home.js index 3577da95d..3c9841510 100644 --- a/hydro/handler/home.js +++ b/hydro/handler/home.js @@ -1,6 +1,6 @@ const { VerifyPasswordError, UserAlreadyExistError, InvalidTokenError, - NotFoundError, MessageNotFoundError, + NotFoundError, } = require('../error'); const bus = require('../service/bus'); const { @@ -200,38 +200,34 @@ class HomeMessagesHandler extends Handler { this.udoc(udict, mdoc.from); this.udoc(udict, mdoc.to); } - this.response.body = { messages: mdocs, udict }; + const path = [ + ['Hydro', '/'], + ['home_messages', null], + ]; + this.response.body = { messages: mdocs, udict, path }; this.response.template = 'home_messages.html'; } - async postSendMessage({ uid, content }) { + async postSend({ uid, content, type = 'full' }) { const udoc = await user.getById(uid); - const mdoc = await message.add(this.user._id, udoc._id, content); + let mdoc = await message.send(this.user._id, uid, content); + if (type === 'single') { + mdoc = mdoc.reply[mdoc.reply.length - 1]; + } // TODO(twd2): improve here: // projection mdoc.from_udoc = this.user; - this.udoc(mdoc, 'from'); mdoc.to_udoc = udoc; + this.udoc(mdoc, 'from'); this.udoc(mdoc, 'to'); if (this.user._id !== uid) { await bus.publish(`user_message-${uid}`, { type: 'new', data: mdoc }); } - this.back({ mdoc }); + this.back(type === 'full' ? { mdoc } : { reply: mdoc }); } - async postReplyMessage({ message_id, content }) { - const [mdoc, reply] = await message.addReply(message_id, this.user._id, content); - if (!mdoc) throw new MessageNotFoundError(message_id); - if (mdoc.from !== mdoc.to) { - const other = mdoc.from === this.user._id ? mdoc.from : mdoc.to; - await bus.publish(`user_message-${other}`, { type: 'reply', data: mdoc }); - } - mdoc.reply = [reply]; - this.back({ reply }); - } - - async postDeleteMessage({ message_id }) { - await message.delete(message_id, this.user._id); + async postDeleteMessage({ messageId }) { + await message.delete(messageId, this.user._id); this.back(); } } diff --git a/hydro/handler/import.js b/hydro/handler/import.js index 9f45a0393..8717fc709 100644 --- a/hydro/handler/import.js +++ b/hydro/handler/import.js @@ -22,10 +22,10 @@ class ProblemImportHandler extends Handler { async post({ url, pid, hidden, remoteType, }) { - if (typeof this[`from_${remoteType}`] !== 'function') { + if (typeof global.Hydro.lib[`import.${remoteType}`] !== 'function') { throw new ValidationError('remoteType'); } - const [pdoc, testdata] = await this[`from_${remoteType}`](url); + const [pdoc, testdata] = await global.Hydro.lib[`import.${remoteType}`](url, this); if (pid) pdoc.pid = pid; if (hidden) pdoc.hidden = true; const _id = await problem.add(pdoc); diff --git a/hydro/handler/problem.js b/hydro/handler/problem.js index 323c9af8d..d77520094 100644 --- a/hydro/handler/problem.js +++ b/hydro/handler/problem.js @@ -118,8 +118,10 @@ class ProblemSubmitHandler extends ProblemDetailHandler { const rid = await record.add({ uid: this.uid, lang, code, pid: this.pdoc._id, }); - await record.judge(rid); - this.user.nSubmit++; + await Promise.all([ + record.judge(rid), + user.inc(this.user._id, 'nSubmit'), + ]); this.response.body = { rid }; this.response.redirect = `/r/${rid}`; } diff --git a/hydro/handler/record.js b/hydro/handler/record.js index a422609b2..12a977745 100644 --- a/hydro/handler/record.js +++ b/hydro/handler/record.js @@ -17,22 +17,10 @@ class RecordListHandler extends Handler { this.response.template = 'record_main.html'; const q = {}; const rdocs = await record.getMany(q, { _id: -1 }, page, await system.get('RECORD_PER_PAGE')); - const pdict = {}; - const udict = {}; - const ulist = []; - const plist = []; - for (const rdoc of rdocs) { - ulist[rdoc.uid] = user.getById(rdoc.uid); - plist[rdoc.pid] = problem.get(rdoc.pid, this.user._id); - } - await Promise.all(ulist); - await Promise.all(plist); - for (const udoc of ulist) { - udict[udoc._id] = udoc; - } - for (const pdoc of plist) { - pdict[pdoc._id] = pdoc; - } + const [udict, pdict] = await Promise.all([ + user.getList(rdocs.map((rdoc) => rdoc.uid)), + problem.getList(rdocs.map((rdoc) => rdoc.pid)), + ]); const path = [ ['Hydro', '/'], ['record_main', null], diff --git a/hydro/model/message.js b/hydro/model/message.js index e6820fdcc..340ddbaf5 100644 --- a/hydro/model/message.js +++ b/hydro/model/message.js @@ -1,26 +1,39 @@ +const { ObjectID } = require('bson'); const { MessageNotFoundError } = require('../error'); const db = require('../service/db.js'); const coll = db.collection('message'); -async function add(from, to, content) { - const res = await coll.insertOne({ - from, - to, - fromUnread: false, - toUnread: true, - reply: [ - { - from, unread: true, content, at: new Date(), +async function send(from, to, content) { + await coll.updateOne({ + from: Math.min(from, to), + to: Math.max(from, to), + }, { + $push: { + reply: { + _id: new ObjectID(), + from, + content, + unread: true, + at: new Date(), }, - ], + }, + $setOnInsert: { + from: Math.min(from, to), + to: Math.max(from, to), + fromUnread: false, + toUnread: true, + }, + }, { upsert: true }); + return await coll.findOne({ // eslint-disable-line no-return-await + from: Math.min(from, to), + to: Math.max(from, to), }); - return await coll.findOne({ _id: res.insertedId }); // eslint-disable-line no-return-await } async function get(_id) { const doc = await coll.findOne({ _id }); - if (!doc) throw new MessageNotFoundError(); + if (!doc) throw new MessageNotFoundError(_id); return doc; } @@ -42,23 +55,6 @@ function getMulti(uid) { return coll.find({ $or: [{ from: uid }, { to: uid }] }); } -async function addReply(_id, from, content) { - const reply = { - from, - content, - unread: true, - at: new Date(), - }; - await coll.updateOne({ _id }, { $push: { reply } }); - return await coll.findOne({ _id }); // eslint-disable-line no-return-await -} - -async function send(from, to, content) { - const sdoc = await coll.findOne({ $or: [{ from, to }, { from: to, to: from }] }); - await addReply(sdoc._id, from, content); - return sdoc._id; -} - function index() { return Promise.all([ coll.createIndex({ to: 1, _id: -1 }), @@ -68,12 +64,10 @@ function index() { global.Hydro.model.message = module.exports = { count, - add, get, del, getMany, getMulti, - addReply, send, index, }; diff --git a/hydro/model/token.js b/hydro/model/token.js index f8276fbcf..a0c364c6e 100644 --- a/hydro/model/token.js +++ b/hydro/model/token.js @@ -63,9 +63,9 @@ module.exports = { const now = new Date(); const res = await coll.findOneAndUpdate({ _id: tokenId, tokenType }, { $set: { + ...data, updateAt: now, expireAt: new Date(now.getTime() + expireSeconds * 1000), - ...data, tokenType, }, }); diff --git a/hydro/model/user.js b/hydro/model/user.js index 5394174e3..0460a5dba 100644 --- a/hydro/model/user.js +++ b/hydro/model/user.js @@ -96,9 +96,10 @@ async function changePassword(uid, currentPassword, newPassword) { }); } -async function inc(_id, field, n) { - await coll.findOneAndUpdate({ _id }, { $inc: { [field]: n } }); - const udoc = await getById(_id); +async function inc(_id, field, n = 1) { + const udoc = await coll.findOne({ _id }); + udoc[field] = udoc[field] + n || n; + await coll.updateOne({ _id }, { $set: { [field]: udoc[field] } }); return udoc; } diff --git a/hydro/service/server.js b/hydro/service/server.js index 1e4adb31f..bc50c7267 100644 --- a/hydro/service/server.js +++ b/hydro/service/server.js @@ -151,7 +151,10 @@ class Handler { if (bdoc) throw new BlacklistedError(this.request.ip); this.user = await user.getById(this.session.uid); if (!this.user) throw new UserNotFoundError(this.session.uid); - [this.csrfToken] = await token.add(token.TYPE_CSRF_TOKEN, 600, { path: this.request.path }); + [this.csrfToken] = await token.add(token.TYPE_CSRF_TOKEN, 600, { + path: this.request.path, + uid: this.session.uid, + }); this.preferJson = (this.request.headers.accept || '').includes('application/json'); } @@ -321,7 +324,6 @@ class ConnectionHandler { _: (str) => (str ? str.toString().translate(this.user.language) : ''), user: this.user, })); - console.timeEnd(name); return res; } @@ -335,9 +337,7 @@ class ConnectionHandler { async ___prepare() { try { - this.session = { - uid: await token.get(this.request.params.token, token.TYPE_CSRF_TOKEN), - }; + this.session = await token.get(this.request.params.token, token.TYPE_CSRF_TOKEN); await token.delete(this.request.params.token, token.TYPE_CSRF_TOKEN); } catch (e) { this.session = { uid: 1 }; diff --git a/module/migrate-vijos/handler.js b/module/migrate-vijos/handler.js index 29f52087d..d8b01c053 100644 --- a/module/migrate-vijos/handler.js +++ b/module/migrate-vijos/handler.js @@ -22,7 +22,7 @@ class MigrateVijosHandler extends Handler { }) { migrate({ host, port, name, username, password, - }, (data) => message.send(0, 0, data)); + }, (data) => message.send(1, 1, data)); this.response.redirect = '/manage/log'; } } diff --git a/module/problem-import-syzoj/handler.js b/module/problem-import-syzoj/lib.js similarity index 54% rename from module/problem-import-syzoj/handler.js rename to module/problem-import-syzoj/lib.js index 4e67112f8..17cf6b949 100644 --- a/module/problem-import-syzoj/handler.js +++ b/module/problem-import-syzoj/lib.js @@ -4,7 +4,7 @@ const { download } = global.Hydro.lib; const { axios } = global.Hydro.lib; const { ValidationError, RemoteOnlineJudgeError } = global.Hydro.error; -async function syzoj(url) { +async function syzoj(url, handler) { const RE_SYZOJ = /https?:\/\/([a-zA-Z0-9.]+)\/problem\/([0-9]+)\/?/i; assert(url.match(RE_SYZOJ), new ValidationError('url')); if (!url.endsWith('/')) url += '/'; @@ -13,40 +13,59 @@ async function syzoj(url) { 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, - ]; + const content = []; + if (p.description) { + content.push( + handler.translate('problem.import.problem_description'), + p.description, + ); + } + if (p.input_format) { + content.push( + handler.translate('problem.import.input_format'), + p.input_format, + ); + } + if (p.output_format) { + content.push( + handler.translate('problem.import.output_format'), + p.output_format, + ); + } + if (p.hint) { + content.push( + handler.translate('problem.import.hint'), + p.hint, + ); + } + if (p.limit_and_hint) { + content.push( + handler.translate('problem.import.limit_and_hint'), + p.limit_and_hint, + ); + } if (p.have_additional_file) { content.push( - this.translate('problem.import.additional_file'), + handler.translate('problem.import.additional_file'), `${url}download/additional_file`, ); } const pdoc = { title: p.title, content: content.join(' \n'), - owner: this.user._id, + owner: handler.user._id, from: url, pid: `${host}_${pid}`, + tags: p.tags, 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]; } -global.Hydro.handler.import.ProblemImportHandler.from_syzoj = syzoj; +global.Hydro.lib['import.syzoj'] = syzoj; diff --git a/module/problem-import-syzoj/locale/zh_CN.yaml b/module/problem-import-syzoj/locale/zh_CN.yaml index af79dab04..a013924c9 100644 --- a/module/problem-import-syzoj/locale/zh_CN.yaml +++ b/module/problem-import-syzoj/locale/zh_CN.yaml @@ -1 +1,8 @@ -Import problem from syzoj.: 从运行 syzoj 系统的 OJ 导入题目。 \ No newline at end of file +Import problem from syzoj.: 从运行 syzoj 系统的 OJ 导入题目。 +problem.import.problem_description: '## 题目描述' +problem.import.input_format: '## 输入格式' +problem.import.output_format: '## 输出格式' +problem.import.hint: '## 提示' +problem.import.limit_and_hint: '## 限制' +problem.import.additional_file: '## 附加文件' +import: 导入 \ No newline at end of file diff --git a/ui/components/messagepad/MessagePadDialogueContentContainer.js b/ui/components/messagepad/MessagePadDialogueContentContainer.js index 346cb4f02..e1e97bb77 100644 --- a/ui/components/messagepad/MessagePadDialogueContentContainer.js +++ b/ui/components/messagepad/MessagePadDialogueContentContainer.js @@ -56,7 +56,7 @@ export default class MessagePadDialogueContentContainer extends React.PureCompon return _.map(this.props.item.reply, (reply, idx) => ( ({ const mapDispatchToProps = (dispatch) => ({ handleChange(id, value) { - if (id === null) { - return; - } + if (id === null) return; dispatch({ type: 'DIALOGUES_INPUT_CHANGED', payload: value, @@ -30,11 +28,9 @@ const mapDispatchToProps = (dispatch) => ({ }); }, postSend(placeholderId, uid, value) { - if (placeholderId === null) { - return; - } + if (placeholderId === null) return; const req = request.post('', { - operation: 'send_message', + operation: 'send', uid, content: value, }); @@ -46,20 +42,19 @@ const mapDispatchToProps = (dispatch) => ({ }, }); }, - postReply(id, value) { - if (id === null) { - return; - } + postReply(_id, uid, value) { + if (_id === null) return; const req = request.post('', { - operation: 'reply_message', - message_id: id, + operation: 'send', + type: 'single', + uid, content: value, }); dispatch({ type: 'DIALOGUES_POST_REPLY', payload: req, meta: { - dialogueId: id, + dialogueId: _id, }, }); }, @@ -103,6 +98,7 @@ export default class MessagePadInputContainer extends React.PureComponent { } else { this.props.postReply( this.props.activeId, + state.dialogues[this.props.activeId].to, this.props.inputValue, ); } diff --git a/ui/components/messagepad/reducers/dialogues.js b/ui/components/messagepad/reducers/dialogues.js index f4534709e..639d17bd7 100644 --- a/ui/components/messagepad/reducers/dialogues.js +++ b/ui/components/messagepad/reducers/dialogues.js @@ -22,7 +22,7 @@ export default function reducer(state = {}, action) { ...state, [id]: { _id: id, - from: UserContext.uid, + from: UserContext._id, to: user._id, to_udoc: { ...user }, reply: [], diff --git a/ui/components/scratchpad/reducers/records.js b/ui/components/scratchpad/reducers/records.js index e5ced2247..1380268f8 100644 --- a/ui/components/scratchpad/reducers/records.js +++ b/ui/components/scratchpad/reducers/records.js @@ -15,8 +15,7 @@ export default function reducer(state = { } case 'SCRATCHPAD_RECORDS_PUSH': { const { rdoc } = action.payload; - if (rdoc.uid !== UserContext.uid - || rdoc.domain_id !== UserContext.domain + if (rdoc.uid !== UserContext._id || rdoc.pid !== Context.problemId ) { return state;