From c6d3ce1e79c05240b717c116696832588312c6f1 Mon Sep 17 00:00:00 2001 From: undefined Date: Sat, 22 May 2021 18:12:51 +0800 Subject: [PATCH] core: use record-conn handler for pretest-conn --- install/install.js | 24 ++- packages/hydrojudge/src/service.ts | 2 +- packages/hydrooj/package.json | 2 +- packages/hydrooj/src/handler/problem.ts | 32 ---- packages/hydrooj/src/handler/record.ts | 46 ++++- .../scratchpad/ScratchpadToolbarContainer.jsx | 22 +-- .../components/scratchpad/index.jsx | 4 +- .../components/scratchpad/reducers/ui.js | 168 +++++++++--------- packages/ui-default/package.json | 2 +- .../ui-default/templates/problem_detail.html | 5 +- 10 files changed, 166 insertions(+), 141 deletions(-) diff --git a/install/install.js b/install/install.js index d38edfdf3..c07b3beba 100644 --- a/install/install.js +++ b/install/install.js @@ -1,3 +1,4 @@ +/* eslint-disable consistent-return */ /* eslint-disable no-undef */ /// @@ -93,7 +94,13 @@ const steps = [ operations: [ () => { const res = exec1('bash -c "source /root/.nvm/nvm.sh && nvm install 14"', { NVM_NODEJS_ORG_MIRROR }); - const ver = res.output.split('Now using node v')[1].split(' ')[0]; + let ver; + try { + ver = res.output.split('Now using node v')[1].split(' ')[0]; + } catch (e) { + log.error('Cannot parse version. Installation fail.'); + return 'retry'; + } setenv('PATH', `/root/.nvm/versions/node/v${ver}/bin:${__env.PATH}`); const rc_path = `/root/.${__env['SHELL'].endsWith('zsh') ? 'zsh' : 'bash'}rc`; if (!fs.exist(rc_path)) fs.writefile(rc_path, source_nvm); @@ -152,12 +159,17 @@ const steps = [ Arch ? 'pacman --needed --quiet --noconfirm -S gcc fpc' : 'apt-get install -y g++ fp-compiler >/dev/null', ], }, + { + init: '正在安装沙箱 / Installing sandbox', + operations: [ + `curl -fSL ${SANDBOX_DOWNLOAD} -o /usr/bin/sandbox`, + 'chmod +x /usr/bin/sandbox', + ], + }, { init: '正在安装 HydroOJ / Installing HydroOJ', operations: [ 'yarn global add hydrooj @hydrooj/ui-default @hydrooj/hydrojudge', - () => http.download(SANDBOX_DOWNLOAD, '/usr/bin/sandbox'), - 'chmod +x /usr/bin/sandbox', () => fs.writefile('/root/.hydro/addon.json', '["@hydrooj/ui-default","@hydrooj/hydrojudge"]'), ], }, @@ -199,7 +211,11 @@ for (let i = 0; i < steps.length; i++) { if (!(step.skip && step.skip())) { for (const op of step.operations) { if (typeof op === 'string') { - exec(op); + let res = exec(op); + while (res === 'retry') { + log.warn('Retry...'); + res = exec(op); + } if (__code !== 0) log.fatal('Error when running %s', op); } else op(); } diff --git a/packages/hydrojudge/src/service.ts b/packages/hydrojudge/src/service.ts index 5ec37a37b..b1a1d6640 100644 --- a/packages/hydrojudge/src/service.ts +++ b/packages/hydrojudge/src/service.ts @@ -201,7 +201,7 @@ async function postInit() { this.clean = []; fs.ensureDirSync(this.tmpdir); tmpfs.mount(this.tmpdir, getConfig('tmpfs_size')); - logger.info(`Submission: ${this.rid}`, { pid: this.pid }); + logger.info(`Submission: ${this.domainId}/${this.rid}`, { pid: this.pid }); if (typeof this.input === 'string') await this.run(); else await this.submission(); } catch (e) { diff --git a/packages/hydrooj/package.json b/packages/hydrooj/package.json index 57dad69ad..74fd52e8f 100644 --- a/packages/hydrooj/package.json +++ b/packages/hydrooj/package.json @@ -1,6 +1,6 @@ { "name": "hydrooj", - "version": "2.26.25", + "version": "2.26.26", "bin": "bin/hydrooj.js", "main": "dist/loader.js", "typings": "dist/loader.d.ts", diff --git a/packages/hydrooj/src/handler/problem.ts b/packages/hydrooj/src/handler/problem.ts index 40e527c7e..71f7e4ff3 100644 --- a/packages/hydrooj/src/handler/problem.ts +++ b/packages/hydrooj/src/handler/problem.ts @@ -308,37 +308,6 @@ export class ProblemPretestHandler extends ProblemDetailHandler { } } -export class ProblemPretestConnectionHandler extends ConnectionHandler { - pid: string; - domainId: string; - dispose: bus.Disposable; - - @param('pid', Types.Name) - async prepare(domainId: string, pid: string) { - const pdoc = await problem.get(domainId, pid); - if (!pdoc) throw new ProblemNotFoundError(domainId, pid); - if (pdoc.hidden) this.checkPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN); - this.pid = pdoc.docId.toString(); - this.domainId = domainId; - this.dispose = bus.on('record/change', this.onRecordChange.bind(this)); - } - - async onRecordChange(rdoc: RecordDoc) { - if ( - rdoc.uid !== this.user._id - || rdoc.pid.toString() !== this.pid - || rdoc.domainId !== this.domainId - ) return; - // TODO handle update - if (rdoc.contest) return; - this.send({ rdoc }); - } - - async cleanup() { - if (this.dispose) this.dispose(); - } -} - export class ProblemManageHandler extends ProblemDetailHandler { async prepare() { if (!this.user.own(this.pdoc, PERM.PERM_EDIT_PROBLEM_SELF)) this.checkPerm(PERM.PERM_EDIT_PROBLEM); @@ -651,7 +620,6 @@ export async function apply() { Route('problem_solution_reply_raw', '/p/:pid/solution/:psid/:psrid/raw', ProblemSolutionRawHandler, PERM.PERM_VIEW_PROBLEM); Route('problem_create', '/problem/create', ProblemCreateHandler, PERM.PERM_CREATE_PROBLEM); Route('problem_prefix_list', '/problem/list', ProblemPrefixListHandler, PERM.PERM_VIEW_PROBLEM); - Connection('problem_pretest_conn', '/conn/pretest', ProblemPretestConnectionHandler, PERM.PERM_SUBMIT_PROBLEM); } global.Hydro.handler.problem = apply; diff --git a/packages/hydrooj/src/handler/record.ts b/packages/hydrooj/src/handler/record.ts index 81e9626cc..e94d8f0ea 100644 --- a/packages/hydrooj/src/handler/record.ts +++ b/packages/hydrooj/src/handler/record.ts @@ -1,5 +1,8 @@ import { FilterQuery, ObjectID } from 'mongodb'; -import { ContestNotFoundError, PermissionError, RecordNotFoundError } from '../error'; +import { + ContestNotFoundError, PermissionError, ProblemNotFoundError, + RecordNotFoundError, UserNotFoundError, +} from '../error'; import { RecordDoc } from '../interface'; import { PERM, STATUS, PRIV } from '../model/builtin'; import * as system from '../model/system'; @@ -158,14 +161,44 @@ class RecordDetailHandler extends Handler { class RecordMainConnectionHandler extends ConnectionHandler { dispose: bus.Disposable; tid: string; + uid: number; + pid: number; + status: number; + pretest: boolean; @param('tid', Types.ObjectID, true) - async prepare(domainId: string, tid?: ObjectID) { + @param('pid', Types.Name, true) + @param('uidOrName', Types.Name, true) + @param('status', Types.Int, true) + @param('pretest', Types.Boolean) + async prepare(domainId: string, tid?: ObjectID, pid?: string, uidOrName?: string, status?: number, pretest = false) { if (tid) { const tdoc = await contest.get(domainId, tid, -1); if (contest.canShowRecord.call(this, tdoc)) this.tid = tid.toHexString(); else throw new PermissionError(PERM.PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD); } + if (tid) { + const tdoc = await contest.get(domainId, tid, -1); + if (!tdoc) throw new ContestNotFoundError(domainId, tid); + if (!contest.canShowScoreboard.call(this, tdoc, true)) throw new PermissionError(PERM.PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD); + this.tid = tid.toHexString(); + } + if (uidOrName) { + let udoc = await user.getById(domainId, +uidOrName); + if (udoc) this.uid = udoc._id; + else { + udoc = await user.getByUname(domainId, uidOrName); + if (udoc) this.uid = udoc._id; + else throw new UserNotFoundError(uidOrName); + } + } + if (pid) { + const pdoc = await problem.get(domainId, pid); + if (pdoc) this.pid = pdoc.docId; + else throw new ProblemNotFoundError(domainId, pid); + } + if (status) this.status = status; + if (pretest) this.pretest = true; this.dispose = bus.on('record/change', this.onRecordChange.bind(this)); } @@ -182,9 +215,11 @@ class RecordMainConnectionHandler extends ConnectionHandler { } async onRecordChange(rdoc: RecordDoc) { - if (rdoc.input) return; - if (rdoc.contest && rdoc.contest.tid.toString() !== this.tid) return; if (rdoc.domainId !== this.domainId) return; + if (!this.pretest && rdoc.input) return; + if (rdoc.contest && rdoc.contest.tid.toString() !== this.tid) return; + if (this.uid && rdoc.uid !== this.uid) return; + if (this.pid && rdoc.pid !== this.pid) return; // eslint-disable-next-line prefer-const let [udoc, pdoc] = await Promise.all([ user.getById(this.domainId, rdoc.uid), @@ -194,7 +229,8 @@ class RecordMainConnectionHandler extends ConnectionHandler { if (pdoc.hidden && !this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN)) pdoc = null; if (!this.user.hasPerm(PERM.PERM_VIEW_PROBLEM)) pdoc = null; } - this.send({ html: await this.renderHTML('record_main_tr.html', { rdoc, udoc, pdoc }) }); + if (this.pretest) this.send({ rdoc }); + else this.send({ html: await this.renderHTML('record_main_tr.html', { rdoc, udoc, pdoc }) }); } } diff --git a/packages/ui-default/components/scratchpad/ScratchpadToolbarContainer.jsx b/packages/ui-default/components/scratchpad/ScratchpadToolbarContainer.jsx index 4c8ee15e1..794ef59bd 100644 --- a/packages/ui-default/components/scratchpad/ScratchpadToolbarContainer.jsx +++ b/packages/ui-default/components/scratchpad/ScratchpadToolbarContainer.jsx @@ -157,16 +157,18 @@ export default connect(mapStateToProps, mapDispatchToProps)(class ScratchpadTool {' '} {i18n('Pretest')} - this.props.togglePanel('records')} - data-global-hotkey="alt+r" - data-tooltip={`${i18n('Toggle Records Panel')} (Alt+R)`} - > - - {' '} - {i18n('Records')} - + {UiContext.canViewRecord && ( + this.props.togglePanel('records')} + data-global-hotkey="alt+r" + data-tooltip={`${i18n('Toggle Records Panel')} (Alt+R)`} + > + + {' '} + {i18n('Records')} + + )} ); } diff --git a/packages/ui-default/components/scratchpad/index.jsx b/packages/ui-default/components/scratchpad/index.jsx index 1d723a0e8..82ebf2654 100644 --- a/packages/ui-default/components/scratchpad/index.jsx +++ b/packages/ui-default/components/scratchpad/index.jsx @@ -14,9 +14,7 @@ function buildNestedPane([a, ...panes]) { a, ...panes.filter((p) => p.props.visible), ]; - if (elements.length === 1) { - return a; - } + if (elements.length === 1) return a; return elements .reduce((prev, curr) => ( ", "license": "AGPL-3.0", "main": "hydro.js", diff --git a/packages/ui-default/templates/problem_detail.html b/packages/ui-default/templates/problem_detail.html index eafc7ae8a..66ccd31ca 100644 --- a/packages/ui-default/templates/problem_detail.html +++ b/packages/ui-default/templates/problem_detail.html @@ -7,19 +7,22 @@ problemNumId: pdoc.docId, postCopyUrl: url('problem_send'), postPretestUrl: url('problem_pretest', pid=pdoc.docId), - pretestConnUrl: "/conn/pretest?pid=" + pdoc.pid|default(pdoc.docId) + "&domainId=" + handler.domainId, codeLang: handler.user.codeLang, codeTemplate: handler.user.codeTemplate, pdoc: pdoc }) }} {% if tdoc %} + {{ set(UiContext, 'canViewRecord', model.contest.canShowRecord.call(handler, tdoc)) }} {{ set(UiContext, 'postSubmitUrl', url('contest_detail_problem_submit' if tdoc.docType == model.document.TYPE_CONTEST else 'homework_detail_problem_submit', pid=pdoc.docId, tid=tdoc.docId)) }} {{ set(UiContext, 'getSubmissionsUrl', url('contest_detail_problem_submit' if tdoc.docType == model.document.TYPE_CONTEST else 'homework_detail_problem_submit', pid=pdoc.docId, tid=tdoc.docId)) }} {{ set(UiContext, 'getRecordDetailUrl', url('record_detail', rid='{rid}', query={tid:tdoc.docId})) }} + {{ set(UiContext, 'pretestConnUrl', "/record-conn?pretest=1&uid=" + handler.user._id + "&pid=" + pdoc.pid|default(pdoc.docId) + "&domainId=" + handler.domainId + "&tid=" + tdoc.docId) }} {% else %} + {{ set(UiContext, 'canViewRecord', true) }} {{ set(UiContext, 'postSubmitUrl', url('problem_submit', pid=pdoc.docId)) }} {{ set(UiContext, 'getSubmissionsUrl', url('problem_submit', pid=pdoc.docId)) }} {{ set(UiContext, 'getRecordDetailUrl', url('record_detail', rid='{rid}')) }} + {{ set(UiContext, 'pretestConnUrl', "/record-conn?pretest=1&uid=" + handler.user._id + "&pid=" + pdoc.pid|default(pdoc.docId) + "&domainId=" + handler.domainId) }} {% endif %}