core: use alpha-indexed id in contest

This commit is contained in:
undefined committed 2021-05-29 12:29:03 +08:00
1 parent 8852aa149b
commit 800b332c8b
8 files changed
+39 -33

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "hydrooj", "name": "hydrooj",
"version": "2.28.4", "version": "2.28.5",
"bin": "bin/hydrooj.js", "bin": "bin/hydrooj.js",
"main": "dist/loader.js", "main": "dist/loader.js",
"typings": "dist/loader.d.ts", "typings": "dist/loader.d.ts",
+13 -10
View File
@@ -6,9 +6,7 @@ import {
ContestNotLiveError, ValidationError, ProblemNotFoundError, ContestNotLiveError, ValidationError, ProblemNotFoundError,
ContestNotAttendedError, PermissionError, BadRequestError, ContestNotAttendedError, PermissionError, BadRequestError,
} from '../error'; } from '../error';
import { import { ProblemDoc, Tdoc, User } from '../interface';
ProblemId, ProblemDoc, Tdoc, User,
} from '../interface';
import paginate from '../lib/paginate'; import paginate from '../lib/paginate';
import { PERM, PRIV } from '../model/builtin'; import { PERM, PRIV } from '../model/builtin';
import * as contest from '../model/contest'; import * as contest from '../model/contest';
@@ -79,6 +77,7 @@ export class ContestDetailHandler extends Handler {
for (const key in pdict) { for (const key in pdict) {
// @ts-ignore // @ts-ignore
if (pdict[key].domainId !== domainId) pdict[key].docId = `${pdict[key].domainId}:${pdict[key].docId}`; if (pdict[key].domainId !== domainId) pdict[key].docId = `${pdict[key].domainId}:${pdict[key].docId}`;
pdict[key].pid = (tdoc.pids.indexOf(pdict[key].docId) + 10).toString(36).toUpperCase();
} }
this.response.body = { this.response.body = {
path, tdoc, tsdoc, attended, udict, pdict, psdict, rdict, page, path, tdoc, tsdoc, attended, udict, pdict, psdict, rdict, page,
@@ -102,7 +101,7 @@ export class ContestDetailHandler extends Handler {
} }
} }
class ContestBoardcastHandler extends Handler { export class ContestBoardcastHandler extends Handler {
@param('tid', Types.ObjectID) @param('tid', Types.ObjectID)
async get(domainId: string, tid: ObjectID) { async get(domainId: string, tid: ObjectID) {
const tdoc = await contest.get(domainId, tid); const tdoc = await contest.get(domainId, tid);
@@ -131,7 +130,7 @@ class ContestBoardcastHandler extends Handler {
} }
} }
class ContestScoreboardHandler extends Handler { export class ContestScoreboardHandler extends Handler {
@param('tid', Types.ObjectID) @param('tid', Types.ObjectID)
@param('page', Types.PositiveInt, true) @param('page', Types.PositiveInt, true)
async get(domainId: string, tid: ObjectID, page = 1) { async get(domainId: string, tid: ObjectID, page = 1) {
@@ -149,7 +148,7 @@ class ContestScoreboardHandler extends Handler {
} }
} }
class ContestScoreboardDownloadHandler extends Handler { export class ContestScoreboardDownloadHandler extends Handler {
@param('tid', Types.ObjectID) @param('tid', Types.ObjectID)
@param('ext', Types.Range(['csv', 'html'])) @param('ext', Types.Range(['csv', 'html']))
async get(domainId: string, tid: ObjectID, ext: string) { async get(domainId: string, tid: ObjectID, ext: string) {
@@ -253,15 +252,17 @@ export class ContestProblemHandler extends Handler {
@param('tid', Types.ObjectID) @param('tid', Types.ObjectID)
@param('pid', Types.Name) @param('pid', Types.Name)
async _prepare(domainId: string, tid: ObjectID, pid: ProblemId) { async _prepare(domainId: string, tid: ObjectID, _pid: string) {
this.tdoc = await contest.get(domainId, tid); this.tdoc = await contest.get(domainId, tid);
if (!this.tdoc.pids.includes(pid)) throw new ProblemNotFoundError(domainId, pid); const pid = this.tdoc.pids[parseInt(_pid, 36) - 10];
if (!pid) throw new ProblemNotFoundError(domainId, tid, _pid);
[this.pdoc, this.tsdoc, this.udoc] = await Promise.all([ [this.pdoc, this.tsdoc, this.udoc] = await Promise.all([
problem.get(domainId, pid), problem.get(domainId, pid),
contest.getStatus(domainId, tid, this.user._id), contest.getStatus(domainId, tid, this.user._id),
user.getById(domainId, this.tdoc.owner), user.getById(domainId, this.tdoc.owner),
]); ]);
if (!this.pdoc) throw new ProblemNotFoundError(domainId, pid); if (!this.pdoc) throw new ProblemNotFoundError(domainId, pid);
this.pdoc.pid = _pid;
// @ts-ignore // @ts-ignore
if (this.pdoc.domainId !== domainId) this.pdoc.docId = `${this.pdoc.domainId}:${this.pdoc.docId}`; if (this.pdoc.domainId !== domainId) this.pdoc.docId = `${this.pdoc.domainId}:${this.pdoc.docId}`;
this.attended = this.tsdoc && this.tsdoc.attend === 1; this.attended = this.tsdoc && this.tsdoc.attend === 1;
@@ -316,7 +317,8 @@ export class ContestDetailProblemSubmitHandler extends ContestProblemHandler {
@param('tid', Types.ObjectID) @param('tid', Types.ObjectID)
@param('pid', Types.Name) @param('pid', Types.Name)
async get(domainId: string, tid: ObjectID, pid: ProblemId) { async get(domainId: string, tid: ObjectID, _pid: string) {
const pid = this.tdoc.pids[parseInt(_pid, 36) - 10];
this.response.body.rdocs = []; this.response.body.rdocs = [];
if (contest.canShowRecord.call(this, this.tdoc)) { if (contest.canShowRecord.call(this, this.tdoc)) {
this.response.body.rdocs = await record.getUserInProblemMulti( this.response.body.rdocs = await record.getUserInProblemMulti(
@@ -339,7 +341,8 @@ export class ContestDetailProblemSubmitHandler extends ContestProblemHandler {
@param('lang', Types.Name) @param('lang', Types.Name)
@param('code', Types.Content) @param('code', Types.Content)
@param('pid', Types.Name) @param('pid', Types.Name)
async post(domainId: string, tid: ObjectID, lang: string, code: string, pid: ProblemId) { async post(domainId: string, tid: ObjectID, lang: string, code: string, _pid: string) {
const pid = this.tdoc.pids[parseInt(_pid, 36) - 10];
if (this.response.body.pdoc.config?.langs && !this.response.body.pdoc.config.langs.includes(lang)) { if (this.response.body.pdoc.config?.langs && !this.response.body.pdoc.config.langs.includes(lang)) {
throw new BadRequestError('Language not allowed.'); throw new BadRequestError('Language not allowed.');
} }
+17 -14
View File
@@ -8,7 +8,7 @@ import {
HomeworkNotAttendedError, BadRequestError, HomeworkNotAttendedError, BadRequestError,
} from '../error'; } from '../error';
import { import {
PenaltyRules, Tdoc, ProblemDoc, User, ProblemId, PenaltyRules, Tdoc, ProblemDoc, User,
} from '../interface'; } from '../interface';
import { import {
Route, Handler, Types, param, query, Route, Handler, Types, param, query,
@@ -25,7 +25,6 @@ import record from '../model/record';
import storage from '../model/storage'; import storage from '../model/storage';
import * as document from '../model/document'; import * as document from '../model/document';
import paginate from '../lib/paginate'; import paginate from '../lib/paginate';
import { isExternalPid } from '../lib/validator';
const validatePenaltyRules = (input: string) => yaml.load(input); const validatePenaltyRules = (input: string) => yaml.load(input);
const convertPenaltyRules = validatePenaltyRules; const convertPenaltyRules = validatePenaltyRules;
@@ -94,6 +93,7 @@ class HomeworkDetailHandler extends Handler {
for (const key in pdict) { for (const key in pdict) {
// @ts-ignore // @ts-ignore
if (pdict[key].domainId !== domainId) pdict[key].docId = `${pdict[key].domainId}:${pdict[key].docId}`; if (pdict[key].domainId !== domainId) pdict[key].docId = `${pdict[key].domainId}:${pdict[key].docId}`;
pdict[key].pid = (tdoc.pids.indexOf(pdict[key].docId) + 10).toString(36).toUpperCase();
} }
this.response.template = 'homework_detail.html'; this.response.template = 'homework_detail.html';
this.response.body = { this.response.body = {
@@ -127,17 +127,19 @@ class HomeworkDetailProblemHandler extends Handler {
attended: boolean; attended: boolean;
@param('tid', Types.ObjectID) @param('tid', Types.ObjectID)
@param('pid', Types.String, isExternalPid) @param('pid', Types.String)
async _prepare(domainId: string, tid: ObjectID, pid: ProblemId) { async _prepare(domainId: string, tid: ObjectID, _pid: string) {
if (!this.tdoc.pids.includes(pid)) throw new ProblemNotFoundError(domainId, pid, tid); this.tdoc = await contest.get(domainId, tid, document.TYPE_HOMEWORK);
const pid = this.tdoc.pids[parseInt(_pid, 36) - 10];
if (!pid) throw new ProblemNotFoundError(domainId, tid, _pid);
const pdomainId = typeof pid === 'string' ? pid.split(':')[0] : domainId; const pdomainId = typeof pid === 'string' ? pid.split(':')[0] : domainId;
const ppid = typeof pid === 'number' ? pid : +pid.split(':')[1]; const ppid = typeof pid === 'number' ? pid : +pid.split(':')[1];
[this.tdoc, this.pdoc, this.tsdoc] = await Promise.all([ [this.udoc, this.pdoc, this.tsdoc] = await Promise.all([
contest.get(domainId, tid, document.TYPE_HOMEWORK), user.getById(domainId, this.tdoc.owner),
problem.get(pdomainId, ppid), problem.get(pdomainId, ppid),
contest.getStatus(domainId, tid, this.user._id, document.TYPE_HOMEWORK), contest.getStatus(domainId, tid, this.user._id, document.TYPE_HOMEWORK),
]); ]);
this.udoc = await user.getById(domainId, this.tdoc.owner); this.pdoc.pid = _pid;
this.attended = this.tsdoc && this.tsdoc.attend === 1; this.attended = this.tsdoc && this.tsdoc.attend === 1;
this.response.body = { this.response.body = {
tdoc: this.tdoc, tdoc: this.tdoc,
@@ -150,8 +152,7 @@ class HomeworkDetailProblemHandler extends Handler {
@param('tid', Types.ObjectID) @param('tid', Types.ObjectID)
@param('pid', Types.Name) @param('pid', Types.Name)
// eslint-disable-next-line @typescript-eslint/no-unused-vars async get(domainId: string, tid: ObjectID, pid: string) {
async get(domainId: string, tid: ObjectID, pid: ProblemId) {
if (!contest.isDone(this.tdoc)) { if (!contest.isDone(this.tdoc)) {
if (!this.attended) throw new HomeworkNotAttendedError(tid); if (!this.attended) throw new HomeworkNotAttendedError(tid);
if (contest.isNotStarted(this.tdoc)) throw new HomeworkNotLiveError(tid); if (contest.isNotStarted(this.tdoc)) throw new HomeworkNotLiveError(tid);
@@ -195,8 +196,9 @@ class HomeworkDetailProblemSubmitHandler extends HomeworkDetailProblemHandler {
} }
@param('tid', Types.ObjectID) @param('tid', Types.ObjectID)
@param('pid', Types.String, isExternalPid) @param('pid', Types.String)
async get(domainId: string, tid: ObjectID, pid: ProblemId) { async get(domainId: string, tid: ObjectID, _pid: string) {
const pid = this.tdoc.pids[parseInt(_pid, 36) - 10];
this.response.body.rdocs = contest.canShowRecord.call(this, this.tdoc) this.response.body.rdocs = contest.canShowRecord.call(this, this.tdoc)
? await record.getUserInProblemMulti(domainId, this.user._id, pid, { type: document.TYPE_HOMEWORK, tid }) ? await record.getUserInProblemMulti(domainId, this.user._id, pid, { type: document.TYPE_HOMEWORK, tid })
.sort('_id', -1).limit(10).toArray() .sort('_id', -1).limit(10).toArray()
@@ -212,10 +214,11 @@ class HomeworkDetailProblemSubmitHandler extends HomeworkDetailProblemHandler {
} }
@param('tid', Types.ObjectID) @param('tid', Types.ObjectID)
@param('pid', Types.String, isExternalPid) @param('pid', Types.String)
@param('code', Types.Content) @param('code', Types.Content)
@param('lang', Types.Name) @param('lang', Types.Name)
async post(domainId: string, tid: ObjectID, pid: ProblemId, code: string, lang: string) { async post(domainId: string, tid: ObjectID, _pid: string, code: string, lang: string) {
const pid = this.tdoc.pids[parseInt(_pid, 36) - 10];
if (this.response.body.pdoc.config?.langs && !this.response.body.pdoc.config.langs.includes(lang)) { if (this.response.body.pdoc.config?.langs && !this.response.body.pdoc.config.langs.includes(lang)) {
throw new BadRequestError('Language not allowed.'); throw new BadRequestError('Language not allowed.');
} }
+2 -1
View File
@@ -120,6 +120,7 @@ async function reload(count = 1) {
const shell = new Logger('shell'); const shell = new Logger('shell');
async function executeCommand(input: string) { async function executeCommand(input: string) {
input = input.trim();
// Clear the stack // Clear the stack
setImmediate(async () => { setImmediate(async () => {
if (input === 'restart') return reload(); if (input === 'restart') return reload();
@@ -128,7 +129,7 @@ async function executeCommand(input: string) {
} }
try { try {
// eslint-disable-next-line no-eval // eslint-disable-next-line no-eval
shell.info(await eval(input.trim())); shell.info(await eval(input));
} catch (e) { } catch (e) {
shell.warn(e); shell.warn(e);
} }
+2 -2
View File
@@ -247,8 +247,8 @@ export class ProblemModel {
const l = {}; const l = {};
const ddocs = await Promise.all(Object.keys(parsed).map((i) => domain.get(i))); const ddocs = await Promise.all(Object.keys(parsed).map((i) => domain.get(i)));
const f = ddocs.filter((i) => !( const f = ddocs.filter((i) => !(
i.share === '*' i?.share === '*'
|| (`,${(i.share || '').replace(/,/g, ',').split(',').map((q) => q.trim()).join(',')},`).includes(`,${domainId},`) || (`,${(i?.share || '').replace(/,/g, ',').split(',').map((q) => q.trim()).join(',')},`).includes(`,${domainId},`)
)); ));
if (f.length) { if (f.length) {
if (doThrow) throw new ProblemNotFoundError(f[0]._id, parsed[f[0]._id][0].pid); if (doThrow) throw new ProblemNotFoundError(f[0]._id, parsed[f[0]._id][0].pid);
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@hydrooj/ui-default", "name": "@hydrooj/ui-default",
"version": "4.11.0", "version": "4.11.1",
"author": "undefined <i@undefined.moe>", "author": "undefined <i@undefined.moe>",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"main": "hydro.js", "main": "hydro.js",
@@ -2,15 +2,15 @@
{%- if not invalid %} {%- if not invalid %}
<a <a
{%- if tdoc and tdoc.docType == model.document.TYPE_CONTEST %} {%- if tdoc and tdoc.docType == model.document.TYPE_CONTEST %}
href="{{ url('contest_detail_problem', tid=tdoc.docId, pid=pdoc.docId) }}" href="{{ url('contest_detail_problem', tid=tdoc.docId, pid=pdoc.pid) }}"
{%- elif tdoc and tdoc.docType == model.document.TYPE_HOMEWORK %} {%- elif tdoc and tdoc.docType == model.document.TYPE_HOMEWORK %}
href="{{ url('homework_detail_problem', tid=tdoc.docId, pid=pdoc.docId) }}" href="{{ url('homework_detail_problem', tid=tdoc.docId, pid=pdoc.pid) }}"
{%- else %} {%- else %}
href="{{ url('problem_detail', pid=pdoc.pid|default(pdoc.docId)) }}" href="{{ url('problem_detail', pid=pdoc.pid|default(pdoc.docId)) }}"
{%- endif %} {%- endif %}
> >
{%- endif %} {%- endif %}
{{ pdoc.pid if pdoc.pid else 'P'+pdoc.docId }} {{ pdoc.title }} {{ pdoc.pid if pdoc.pid else 'P'+pdoc.docId }}. {{ pdoc.title }}
{%- if not invalid %} {%- if not invalid %}
</a> </a>
{%- endif %} {%- endif %}
@@ -57,7 +57,6 @@
{% endif %} {% endif %}
{% endif %} {% endif %}
<td class="col--problem col--problem-name"> <td class="col--problem col--problem-name">
#{{ loop.index }}:
{{ problem.render_problem_title(pdict[pid], tdoc=tdoc, show_invisible_flag=false, show_tags=false) }} {{ problem.render_problem_title(pdict[pid], tdoc=tdoc, show_invisible_flag=false, show_tags=false) }}
</td> </td>
</tr> </tr>