core: update to mongodb5 (#514)

This commit is contained in:
undefined authored and GitHub committed 2023-02-27 18:31:24 +08:00
1 parent 5a02010120
commit 0685b88066
59 files changed
+610 -601

No files matched your search

+13 -13
View File
@@ -1,4 +1,4 @@
import { ObjectID } from 'mongodb';
import { ObjectId } from 'mongodb';
import { DiscussionNotFoundError } from '../error';
import { BlogDoc } from '../interface';
import paginate from '../lib/paginate';
@@ -12,8 +12,8 @@ import { Handler, param, Types } from '../service/server';
class BlogHandler extends Handler {
ddoc?: BlogDoc;
@param('did', Types.ObjectID, true)
async _prepare(domainId: string, did: ObjectID) {
@param('did', Types.ObjectId, true)
async _prepare(domainId: string, did: ObjectId) {
if (did) {
this.ddoc = await blog.get(did);
if (!this.ddoc) throw new DiscussionNotFoundError(domainId, did);
@@ -42,8 +42,8 @@ class BlogUserHandler extends BlogHandler {
}
class BlogDetailHandler extends BlogHandler {
@param('did', Types.ObjectID)
async get(domainId: string, did: ObjectID) {
@param('did', Types.ObjectId)
async get(domainId: string, did: ObjectId) {
const dsdoc = this.user.hasPriv(PRIV.PRIV_USER_PROFILE)
? await blog.getStatus(did, this.user._id)
: null;
@@ -64,14 +64,14 @@ class BlogDetailHandler extends BlogHandler {
this.checkPriv(PRIV.PRIV_USER_PROFILE);
}
@param('did', Types.ObjectID)
async postStar(domainId: string, did: ObjectID) {
@param('did', Types.ObjectId)
async postStar(domainId: string, did: ObjectId) {
await blog.setStar(did, this.user._id, true);
this.back({ star: true });
}
@param('did', Types.ObjectID)
async postUnstar(domainId: string, did: ObjectID) {
@param('did', Types.ObjectId)
async postUnstar(domainId: string, did: ObjectId) {
await blog.setStar(did, this.user._id, false);
this.back({ star: false });
}
@@ -92,10 +92,10 @@ class BlogEditHandler extends BlogHandler {
this.response.redirect = this.url('blog_detail', { uid: this.user._id, did });
}
@param('did', Types.ObjectID)
@param('did', Types.ObjectId)
@param('title', Types.Title)
@param('content', Types.Content)
async postUpdate(domainId: string, did: ObjectID, title: string, content: string) {
async postUpdate(domainId: string, did: ObjectId, title: string, content: string) {
if (!this.user.own(this.ddoc)) this.checkPriv(PRIV.PRIV_EDIT_SYSTEM);
await Promise.all([
blog.edit(did, title, content),
@@ -105,8 +105,8 @@ class BlogEditHandler extends BlogHandler {
this.response.redirect = this.url('blog_detail', { uid: this.user._id, did });
}
@param('did', Types.ObjectID)
async postDelete(domainId: string, did: ObjectID) {
@param('did', Types.ObjectId)
async postDelete(domainId: string, did: ObjectId) {
if (!this.user.own(this.ddoc)) this.checkPriv(PRIV.PRIV_EDIT_SYSTEM);
await Promise.all([
blog.del(did),
+49 -49
View File
@@ -2,7 +2,7 @@ import AdmZip from 'adm-zip';
import { statSync } from 'fs-extra';
import { pick } from 'lodash';
import moment from 'moment-timezone';
import { ObjectID } from 'mongodb';
import { ObjectId } from 'mongodb';
import {
Counter, sortFiles, streamToBuffer, Time,
} from '@hydrooj/utils/lib/utils';
@@ -47,8 +47,8 @@ registerResolver(
'Query', 'contest(id: ObjectID!)', 'Contest',
async (arg, ctx) => {
ctx.checkPerm(PERM.PERM_VIEW);
arg.id = new ObjectID(arg.id);
ctx.tdoc = await contest.get(ctx.args.domainId, new ObjectID(arg.id));
arg.id = new ObjectId(arg.id);
ctx.tdoc = await contest.get(ctx.args.domainId, new ObjectId(arg.id));
if (!ctx.tdoc) throw new ContestNotFoundError(ctx.args.domainId, arg.id);
return ctx.tdoc;
},
@@ -92,8 +92,8 @@ export class ContestDetailBaseHandler extends Handler {
tdoc?: Tdoc<30>;
tsdoc?: any;
@param('tid', Types.ObjectID, true)
async __prepare(domainId: string, tid: ObjectID) {
@param('tid', Types.ObjectId, true)
async __prepare(domainId: string, tid: ObjectId) {
if (!tid) return; // ProblemDetailHandler also extends from ContestDetailBaseHandler
[this.tdoc, this.tsdoc] = await Promise.all([
contest.get(domainId, tid),
@@ -110,8 +110,8 @@ export class ContestDetailBaseHandler extends Handler {
}
}
@param('tid', Types.ObjectID, true)
async after(domainId: string, tid: ObjectID) {
@param('tid', Types.ObjectId, true)
async after(domainId: string, tid: ObjectId) {
if (!tid || this.tdoc.rule === 'homework') return;
if (this.request.json || !this.response.template) return;
const pdoc = 'pdoc' in this ? (this as any).pdoc : {};
@@ -151,8 +151,8 @@ export class ContestDetailHandler extends Handler {
tdoc?: Tdoc<30>;
tsdoc?: any;
@param('tid', Types.ObjectID)
async prepare(domainId: string, tid: ObjectID) {
@param('tid', Types.ObjectId)
async prepare(domainId: string, tid: ObjectId) {
[this.tdoc, this.tsdoc] = await Promise.all([
contest.get(domainId, tid),
contest.getStatus(domainId, tid, this.user._id),
@@ -169,8 +169,8 @@ export class ContestDetailHandler extends Handler {
}
}
@param('tid', Types.ObjectID)
async get(domainId: string, tid: ObjectID) {
@param('tid', Types.ObjectId)
async get(domainId: string, tid: ObjectId) {
this.response.template = 'contest_detail.html';
const udict = await user.getList(domainId, [this.tdoc.owner]);
this.response.body = {
@@ -216,9 +216,9 @@ export class ContestDetailHandler extends Handler {
];
}
@param('tid', Types.ObjectID)
@param('tid', Types.ObjectId)
@param('code', Types.String, true)
async postAttend(domainId: string, tid: ObjectID, code = '') {
async postAttend(domainId: string, tid: ObjectId, code = '') {
if (contest.isDone(this.tdoc)) throw new ContestNotLiveError(tid);
if (this.tdoc._code && code !== this.tdoc._code) throw new InvalidTokenError('Contest Invitation', code);
await contest.attend(domainId, tid, this.user._id);
@@ -227,8 +227,8 @@ export class ContestDetailHandler extends Handler {
}
export class ContestProblemListHandler extends ContestDetailBaseHandler {
@param('tid', Types.ObjectID)
async get(domainId: string, tid: ObjectID) {
@param('tid', Types.ObjectId)
async get(domainId: string, tid: ObjectId) {
if (contest.isNotStarted(this.tdoc)) throw new ContestNotLiveError(domainId, tid);
if (!this.tsdoc?.attend && !contest.isDone(this.tdoc)) throw new ContestNotAttendedError(domainId, tid);
const [pdict, udict] = await Promise.all([
@@ -260,9 +260,9 @@ export class ContestProblemListHandler extends ContestDetailBaseHandler {
}
export class ContestScoreboardHandler extends ContestDetailBaseHandler {
@param('tid', Types.ObjectID)
@param('tid', Types.ObjectId)
@param('ext', Types.Range(['csv', 'html', 'ghost']), true)
async get(domainId: string, tid: ObjectID, ext = '') {
async get(domainId: string, tid: ObjectId, ext = '') {
if (!contest.canShowScoreboard.call(this, this.tdoc, true)) throw new ContestScoreboardHiddenError(tid);
if (ext) {
await this.exportScoreboard(domainId, tid, ext);
@@ -279,7 +279,7 @@ export class ContestScoreboardHandler extends ContestDetailBaseHandler {
};
}
async exportGhost(domainId: string, tid: ObjectID) {
async exportGhost(domainId: string, tid: ObjectId) {
const tdoc = this.tdoc;
const [pdict, teams] = await Promise.all([
problem.getList(domainId, tdoc.pids, true, false, problem.PROJECTION_LIST, true),
@@ -288,7 +288,7 @@ export class ContestScoreboardHandler extends ContestDetailBaseHandler {
const udict = await user.getList(domainId, teams.map((i) => i.uid));
const teamIds: Record<number, number> = {};
for (let i = 1; i <= teams.length; i++) teamIds[teams[i - 1].uid] = i;
const time = (t: ObjectID) => Math.floor((t.getTimestamp().getTime() - tdoc.beginAt.getTime()) / Time.second);
const time = (t: ObjectId) => Math.floor((t.getTimestamp().getTime() - tdoc.beginAt.getTime()) / Time.second);
const pid = (i: number) => String.fromCharCode(65 + i);
const escape = (i: string) => i.replace(/[",]/g, '');
const unknownSchool = this.translate('Unknown School');
@@ -316,7 +316,7 @@ export class ContestScoreboardHandler extends ContestDetailBaseHandler {
this.binary(res.join('\n'), `${this.tdoc.title}.ghost`);
}
async exportScoreboard(domainId: string, tid: ObjectID, ext: string) {
async exportScoreboard(domainId: string, tid: ObjectId, ext: string) {
await this.limitRate('scoreboard_download', 60, 3);
if (ext === 'ghost') {
await this.exportGhost(domainId, tid);
@@ -330,8 +330,8 @@ export class ContestScoreboardHandler extends ContestDetailBaseHandler {
this.binary(await getContent[ext](rows, this.tdoc), `${this.tdoc.title}.${ext}`);
}
@param('tid', Types.ObjectID)
async postUnlock(domainId: string, tid: ObjectID) {
@param('tid', Types.ObjectId)
async postUnlock(domainId: string, tid: ObjectId) {
if (!this.user.own(this.tdoc)) this.checkPerm(PERM.PERM_EDIT_CONTEST);
if (!contest.isDone(this.tdoc)) throw new ContestNotEndedError(domainId, tid);
await contest.unlockScoreboard(domainId, tid);
@@ -342,8 +342,8 @@ export class ContestScoreboardHandler extends ContestDetailBaseHandler {
export class ContestEditHandler extends Handler {
tdoc: Tdoc;
@param('tid', Types.ObjectID, true)
async prepare(domainId: string, tid: ObjectID) {
@param('tid', Types.ObjectId, true)
async prepare(domainId: string, tid: ObjectId) {
if (tid) {
this.tdoc = await contest.get(domainId, tid);
if (!this.tdoc) throw new ContestNotFoundError(domainId, tid);
@@ -353,8 +353,8 @@ export class ContestEditHandler extends Handler {
} else this.checkPerm(PERM.PERM_CREATE_CONTEST);
}
@param('tid', Types.ObjectID, true)
async get(domainId: string, tid: ObjectID) {
@param('tid', Types.ObjectId, true)
async get(domainId: string, tid: ObjectId) {
this.response.template = 'contest_edit.html';
const rules = {};
for (const i in contest.RULES) {
@@ -375,7 +375,7 @@ export class ContestEditHandler extends Handler {
};
}
@param('tid', Types.ObjectID, true)
@param('tid', Types.ObjectId, true)
@param('beginAtDate', Types.Date)
@param('beginAtTime', Types.Time)
@param('duration', Types.Float)
@@ -392,7 +392,7 @@ export class ContestEditHandler extends Handler {
@param('maintainer', Types.NumericArray, true)
@param('allowViewCode', Types.Boolean)
async postUpdate(
domainId: string, tid: ObjectID, beginAtDate: string, beginAtTime: string, duration: number,
domainId: string, tid: ObjectId, beginAtDate: string, beginAtTime: string, duration: number,
title: string, content: string, rule: string, _pids: string, rated = false,
_code = '', autoHide = false, assign: string[] = null, lock: number = null,
contestDuration: number = null, maintainer: number[] = [], allowViewCode = false,
@@ -442,8 +442,8 @@ export class ContestEditHandler extends Handler {
this.response.redirect = this.url('contest_detail', { tid });
}
@param('tid', Types.ObjectID)
async postDelete(domainId: string, tid: ObjectID) {
@param('tid', Types.ObjectId)
async postDelete(domainId: string, tid: ObjectId) {
if (!this.user.own(this.tdoc)) this.checkPerm(PERM.PERM_EDIT_CONTEST);
await contest.del(domainId, tid);
await record.updateMulti(domainId, { domainId, contest: tid }, undefined, undefined, { contest: '' });
@@ -461,9 +461,9 @@ export class ContestManagementBaseHandler extends ContestDetailBaseHandler {
}
export class ContestCodeHandler extends Handler {
@param('tid', Types.ObjectID)
@param('tid', Types.ObjectId)
@param('all', Types.Boolean)
async get(domainId: string, tid: ObjectID, all: boolean) {
async get(domainId: string, tid: ObjectId, all: boolean) {
await this.limitRate('contest_code', 60, 10);
const [tdoc, tsdocs] = await contest.getAndListStatus(domainId, tid);
if (!this.user.own(tdoc) && !this.user.hasPriv(PRIV.PRIV_READ_RECORD_CODE)) {
@@ -490,7 +490,7 @@ export class ContestCodeHandler extends Handler {
}
const zip = new AdmZip();
const rdocs = await record.getMulti(domainId, {
_id: { $in: Array.from(Object.keys(rnames)).map((id) => new ObjectID(id)) },
_id: { $in: Array.from(Object.keys(rnames)).map((id) => new ObjectId(id)) },
}).toArray();
await Promise.all(rdocs.map(async (rdoc) => {
if (rdoc.files?.code) {
@@ -509,8 +509,8 @@ export class ContestCodeHandler extends Handler {
}
export class ContestManagementHandler extends ContestManagementBaseHandler {
@param('tid', Types.ObjectID)
async get(domainId: string, tid: ObjectID) {
@param('tid', Types.ObjectId)
async get(domainId: string, tid: ObjectId) {
this.response.body = {
tdoc: this.tdoc,
tsdoc: this.tsdoc,
@@ -523,9 +523,9 @@ export class ContestManagementHandler extends ContestManagementBaseHandler {
this.response.template = 'contest_manage.html';
}
@param('tid', Types.ObjectID)
@param('tid', Types.ObjectId)
@param('content', Types.Content)
async postBroadcast(domainId: string, tid: ObjectID, content: string) {
async postBroadcast(domainId: string, tid: ObjectId, content: string) {
const tsdocs = await contest.getMultiStatus(domainId, { docId: tid }).toArray();
const uids = Array.from<number>(new Set(tsdocs.map((tsdoc) => tsdoc.uid)));
const flag = contest.isOngoing(this.tdoc) ? message.FLAG_ALERT : message.FLAG_UNREAD;
@@ -533,9 +533,9 @@ export class ContestManagementHandler extends ContestManagementBaseHandler {
this.back();
}
@param('tid', Types.ObjectID)
@param('tid', Types.ObjectId)
@post('filename', Types.Filename, true)
async postUploadFile(domainId: string, tid: ObjectID, filename: string) {
async postUploadFile(domainId: string, tid: ObjectId, filename: string) {
if ((this.tdoc.files?.length || 0) >= system.get('limit.contest_files')) {
throw new FileLimitExceededError('count');
}
@@ -556,9 +556,9 @@ export class ContestManagementHandler extends ContestManagementBaseHandler {
this.back();
}
@param('tid', Types.ObjectID)
@param('tid', Types.ObjectId)
@post('files', Types.ArrayOf(Types.Filename))
async postDeleteFiles(domainId: string, tid: ObjectID, files: string[]) {
async postDeleteFiles(domainId: string, tid: ObjectId, files: string[]) {
await Promise.all([
storage.del(files.map((t) => `contest/${domainId}/${tid}/${t}`), this.user._id),
contest.edit(domainId, tid, { files: this.tdoc.files.filter((i) => !files.includes(i.name)) }),
@@ -568,10 +568,10 @@ export class ContestManagementHandler extends ContestManagementBaseHandler {
}
export class ContestFileDownloadHandler extends ContestDetailBaseHandler {
@param('tid', Types.ObjectID)
@param('tid', Types.ObjectId)
@param('filename', Types.Filename)
@param('noDisposition', Types.Boolean)
async get(domainId: string, tid: ObjectID, filename: string, noDisposition = false) {
async get(domainId: string, tid: ObjectId, filename: string, noDisposition = false) {
this.response.addHeader('Cache-Control', 'public');
const target = `contest/${domainId}/${tid}/${filename}`;
const file = await storage.getMeta(target);
@@ -586,8 +586,8 @@ export class ContestFileDownloadHandler extends ContestDetailBaseHandler {
}
export class ContestUserHandler extends ContestManagementBaseHandler {
@param('tid', Types.ObjectID)
async get(domainId: string, tid: ObjectID) {
@param('tid', Types.ObjectId)
async get(domainId: string, tid: ObjectId) {
const tsdocs = await contest.getMultiStatus(domainId, { docId: tid }).toArray();
tsdocs.forEach((i) => {
i.endAt = this.tdoc.duration ? moment(this.tsdoc.startAt).add(this.tdoc.duration, 'hours').toDate() : null;
@@ -598,17 +598,17 @@ export class ContestUserHandler extends ContestManagementBaseHandler {
this.response.template = 'contest_user.html';
}
@param('tid', Types.ObjectID)
@param('tid', Types.ObjectId)
@param('uids', Types.NumericArray)
@param('unrank', Types.Boolean)
async postAddUser(domainId: string, tid: ObjectID, uids: number[], unrank = false) {
async postAddUser(domainId: string, tid: ObjectId, uids: number[], unrank = false) {
await Promise.all(uids.map((uid) => contest.attend(domainId, tid, uid, { unrank })));
this.back();
}
@param('tid', Types.ObjectID)
@param('tid', Types.ObjectId)
@param('uid', Types.PositiveInt)
async postRank(domainId: string, tid: ObjectID, uid: number) {
async postRank(domainId: string, tid: ObjectId, uid: number) {
const tsdoc = await contest.getStatus(domainId, tid, uid);
if (!tsdoc) throw new ContestNotAttendedError(uid);
await contest.setStatus(domainId, tid, uid, { unrank: !tsdoc.unrank });
+39 -39
View File
@@ -1,5 +1,5 @@
import { isSafeInteger } from 'lodash';
import { ObjectID } from 'mongodb';
import { ObjectId } from 'mongodb';
import {
DiscussionLockedError, DiscussionNotFoundError, DocumentNotFoundError,
PermissionError,
@@ -31,12 +31,12 @@ class DiscussionHandler extends Handler {
@param('type', Types.Range(Object.keys(typeMapper)), true)
@param('name', Types.Name, true)
@param('did', Types.ObjectID, true)
@param('drid', Types.ObjectID, true)
@param('drrid', Types.ObjectID, true)
@param('did', Types.ObjectId, true)
@param('drid', Types.ObjectId, true)
@param('drrid', Types.ObjectId, true)
async _prepare(
domainId: string, type: string, name: string,
did: ObjectID, drid: ObjectID, drrid: ObjectID,
did: ObjectId, drid: ObjectId, drrid: ObjectId,
) {
this.checkPerm(PERM.PERM_VIEW_DISCUSSION);
if (did) {
@@ -98,8 +98,8 @@ class DiscussionNodeHandler extends DiscussionHandler {
@param('name', Types.Name)
@param('page', Types.PositiveInt, true)
async get(domainId: string, type: string, _name: string, page = 1) {
let name: ObjectID | string | number;
if (ObjectID.isValid(_name)) name = new ObjectID(_name);
let name: ObjectId | string | number;
if (ObjectId.isValid(_name)) name = new ObjectId(_name);
else if (isSafeInteger(parseInt(_name, 10))) name = parseInt(_name, 10);
else name = _name;
const [ddocs, dpcount] = await paginate(
@@ -162,9 +162,9 @@ class DiscussionCreateHandler extends DiscussionHandler {
}
class DiscussionDetailHandler extends DiscussionHandler {
@param('did', Types.ObjectID)
@param('did', Types.ObjectId)
@param('page', Types.PositiveInt, true)
async get(domainId: string, did: ObjectID, page = 1) {
async get(domainId: string, did: ObjectId, page = 1) {
const dsdoc = this.user.hasPriv(PRIV.PRIV_USER_PROFILE)
? await discussion.getStatus(domainId, did, this.user._id)
: null;
@@ -208,19 +208,19 @@ class DiscussionDetailHandler extends DiscussionHandler {
this.checkPriv(PRIV.PRIV_USER_PROFILE);
}
@param('did', Types.ObjectID)
@param('did', Types.ObjectId)
@param('lock', Types.Boolean)
async postSetLock(domainId: string, did: ObjectID, lock: boolean) {
async postSetLock(domainId: string, did: ObjectId, lock: boolean) {
if (!this.user.own(this.ddoc)) this.checkPerm(PERM.PERM_LOCK_DISCUSSION);
await discussion.edit(domainId, did, { lock });
this.back();
}
@param('type', Types.Range(['did', 'drid']))
@param('id', Types.ObjectID)
@param('id', Types.ObjectId)
@param('emoji', Types.Emoji)
@param('reverse', Types.Boolean)
async postReaction(domainId: string, type: string, did: ObjectID, id: string, reverse = false) {
async postReaction(domainId: string, type: string, did: ObjectId, id: string, reverse = false) {
this.checkPerm(PERM.PERM_ADD_REACTION);
const docType = type === 'did' ? document.TYPE_DISCUSSION : document.TYPE_DISCUSSION_REPLY;
const [doc, sdoc] = await discussion.react(domainId, docType, did, id, this.user._id, reverse);
@@ -228,9 +228,9 @@ class DiscussionDetailHandler extends DiscussionHandler {
this.back();
}
@param('did', Types.ObjectID)
@param('did', Types.ObjectId)
@param('content', Types.Content)
async postReply(domainId: string, did: ObjectID, content: string) {
async postReply(domainId: string, did: ObjectId, content: string) {
this.checkPerm(PERM.PERM_REPLY_DISCUSSION);
if (this.ddoc.lock) throw new DiscussionLockedError(domainId, did);
await this.limitRate('add_discussion', 3600, 60);
@@ -247,9 +247,9 @@ class DiscussionDetailHandler extends DiscussionHandler {
this.back({ drid });
}
@param('drid', Types.ObjectID)
@param('drid', Types.ObjectId)
@param('content', Types.Content)
async postTailReply(domainId: string, drid: ObjectID, content: string) {
async postTailReply(domainId: string, drid: ObjectId, content: string) {
this.checkPerm(PERM.PERM_REPLY_DISCUSSION);
if (this.ddoc.lock) throw new DiscussionLockedError(domainId, this.ddoc.docId);
await this.limitRate('add_discussion', 3600, 60);
@@ -266,9 +266,9 @@ class DiscussionDetailHandler extends DiscussionHandler {
this.back();
}
@param('drid', Types.ObjectID)
@param('drid', Types.ObjectId)
@param('content', Types.Content)
async postEditReply(domainId: string, drid: ObjectID, content: string) {
async postEditReply(domainId: string, drid: ObjectId, content: string) {
this.checkPerm(PERM.PERM_EDIT_DISCUSSION_REPLY_SELF);
if (!this.user.own(this.drdoc)) throw new PermissionError(PERM.PERM_EDIT_DISCUSSION_REPLY_SELF);
await Promise.all([
@@ -278,8 +278,8 @@ class DiscussionDetailHandler extends DiscussionHandler {
this.back();
}
@param('drid', Types.ObjectID)
async postDeleteReply(domainId: string, drid: ObjectID) {
@param('drid', Types.ObjectId)
async postDeleteReply(domainId: string, drid: ObjectId) {
const deleteBy = this.user.own(this.drdoc) ? 'self' : this.user.own(this.ddoc) ? 'DiscussionOwner' : 'Admin';
if (!(this.user.own(this.ddoc)
&& this.user.hasPerm(PERM.PERM_DELETE_DISCUSSION_REPLY_SELF_DISCUSSION))) {
@@ -305,10 +305,10 @@ class DiscussionDetailHandler extends DiscussionHandler {
this.back();
}
@param('drid', Types.ObjectID)
@param('drrid', Types.ObjectID)
@param('drid', Types.ObjectId)
@param('drrid', Types.ObjectId)
@param('content', Types.Content)
async postEditTailReply(domainId: string, drid: ObjectID, drrid: ObjectID, content: string) {
async postEditTailReply(domainId: string, drid: ObjectId, drrid: ObjectId, content: string) {
this.checkPerm(PERM.PERM_EDIT_DISCUSSION_REPLY_SELF);
if (!this.user.own(this.drrdoc)) throw new PermissionError(PERM.PERM_EDIT_DISCUSSION_REPLY_SELF);
await Promise.all([
@@ -318,9 +318,9 @@ class DiscussionDetailHandler extends DiscussionHandler {
this.back();
}
@param('drid', Types.ObjectID)
@param('drrid', Types.ObjectID)
async postDeleteTailReply(domainId: string, drid: ObjectID, drrid: ObjectID) {
@param('drid', Types.ObjectId)
@param('drrid', Types.ObjectId)
async postDeleteTailReply(domainId: string, drid: ObjectId, drrid: ObjectId) {
const deleteBy = this.user.own(this.drrdoc) ? 'self' : 'Admin';
if (!this.user.own(this.drrdoc)) this.checkPerm(PERM.PERM_DELETE_DISCUSSION_REPLY);
const msg = JSON.stringify({
@@ -340,26 +340,26 @@ class DiscussionDetailHandler extends DiscussionHandler {
this.back();
}
@param('did', Types.ObjectID)
async postStar(domainId: string, did: ObjectID) {
@param('did', Types.ObjectId)
async postStar(domainId: string, did: ObjectId) {
await discussion.setStar(domainId, did, this.user._id, true);
this.back({ star: true });
}
@param('did', Types.ObjectID)
async postUnstar(domainId: string, did: ObjectID) {
@param('did', Types.ObjectId)
async postUnstar(domainId: string, did: ObjectId) {
await discussion.setStar(domainId, did, this.user._id, false);
this.back({ star: false });
}
}
class DiscussionRawHandler extends DiscussionHandler {
@param('did', Types.ObjectID, true)
@param('drid', Types.ObjectID, true)
@param('drrid', Types.ObjectID, true)
@param('did', Types.ObjectId, true)
@param('drid', Types.ObjectId, true)
@param('drrid', Types.ObjectId, true)
@param('time', Types.UnsignedInt, true)
@param('all', Types.Boolean)
async get(domainId: string, did: ObjectID, drid: ObjectID, drrid: ObjectID, ts: number, all = false) {
async get(domainId: string, did: ObjectId, drid: ObjectId, drrid: ObjectId, ts: number, all = false) {
if (all) {
this.response.body.history = await discussion.getHistory(domainId, drrid || drid || did);
} else {
@@ -384,13 +384,13 @@ class DiscussionEditHandler extends DiscussionHandler {
this.response.body = { ddoc: this.ddoc, path };
}
@param('did', Types.ObjectID)
@param('did', Types.ObjectId)
@param('title', Types.Title)
@param('content', Types.Content)
@param('highlight', Types.Boolean)
@param('pin', Types.Boolean)
async postUpdate(
domainId: string, did: ObjectID, title: string, content: string,
domainId: string, did: ObjectId, title: string, content: string,
highlight = false, pin = false,
) {
if (!this.user.own(this.ddoc)) this.checkPerm(PERM.PERM_EDIT_DISCUSSION);
@@ -407,8 +407,8 @@ class DiscussionEditHandler extends DiscussionHandler {
this.response.redirect = this.url('discussion_detail', { did });
}
@param('did', Types.ObjectID)
async postDelete(domainId: string, did: ObjectID) {
@param('did', Types.ObjectId)
async postDelete(domainId: string, did: ObjectId) {
const deleteBy = this.user.own(this.ddoc) ? 'self' : 'Admin';
if (!this.user.own(this.ddoc)) this.checkPerm(PERM.PERM_DELETE_DISCUSSION);
else this.checkPerm(PERM.PERM_DELETE_DISCUSSION_SELF);
+6 -6
View File
@@ -2,7 +2,7 @@ import path from 'path';
import { generateRegistrationOptions, verifyRegistrationResponse } from '@simplewebauthn/server';
import yaml from 'js-yaml';
import { pick } from 'lodash';
import { Binary, ObjectID } from 'mongodb';
import { Binary, ObjectId } from 'mongodb';
import { Context } from '../context';
import {
AuthOperationError, BlacklistedError, DomainAlreadyExistsError, InvalidTokenError,
@@ -293,7 +293,7 @@ class HomeSecurityHandler extends Handler {
@requireSudo
@param('id', Types.String)
async postDisableAuthn(domainId: string, id: string) {
const authenticators = this.user._authenticators?.filter((c) => c.credentialID.buffer.toString('base64') !== id);
const authenticators = this.user._authenticators?.filter((c) => Buffer.from(c.credentialID.buffer).toString('base64') !== id);
if (this.user._authenticators?.length === authenticators?.length) throw new ValidationError('authenticator');
await user.setById(this.user._id, { authenticators });
this.back();
@@ -498,16 +498,16 @@ class HomeMessagesHandler extends Handler {
this.back({ mdoc, udoc });
}
@param('messageId', Types.ObjectID)
async postDeleteMessage(domainId: string, messageId: ObjectID) {
@param('messageId', Types.ObjectId)
async postDeleteMessage(domainId: string, messageId: ObjectId) {
const msg = await message.get(messageId);
if ([msg.from, msg.to].includes(this.user._id)) await message.del(messageId);
else throw new PermissionError();
this.back();
}
@param('messageId', Types.ObjectID)
async postRead(domainId: string, messageId: ObjectID) {
@param('messageId', Types.ObjectId)
async postRead(domainId: string, messageId: ObjectId) {
const msg = await message.get(messageId);
if ([msg.from, msg.to].includes(this.user._id)) {
await message.setFlag(messageId, message.FLAG_UNREAD);
+13 -13
View File
@@ -1,6 +1,6 @@
import yaml from 'js-yaml';
import moment from 'moment-timezone';
import { ObjectID } from 'mongodb';
import { ObjectId } from 'mongodb';
import { Time } from '@hydrooj/utils/lib/utils';
import {
ContestNotFoundError, HomeworkNotLiveError, NotAssignedError, ValidationError,
@@ -42,8 +42,8 @@ class HomeworkMainHandler extends Handler {
}
class HomeworkDetailHandler extends Handler {
@param('tid', Types.ObjectID)
async prepare(domainId: string, tid: ObjectID) {
@param('tid', Types.ObjectId)
async prepare(domainId: string, tid: ObjectId) {
const tdoc = await contest.get(domainId, tid);
if (tdoc.rule !== 'homework') throw new ContestNotFoundError(domainId, tid);
if (tdoc.assign?.length && !this.user.own(tdoc)) {
@@ -53,9 +53,9 @@ class HomeworkDetailHandler extends Handler {
}
}
@param('tid', Types.ObjectID)
@param('tid', Types.ObjectId)
@param('page', Types.PositiveInt, true)
async get(domainId: string, tid: ObjectID, page = 1) {
async get(domainId: string, tid: ObjectId, page = 1) {
const [tdoc, tsdoc] = await Promise.all([
contest.get(domainId, tid),
contest.getStatus(domainId, tid, this.user._id),
@@ -101,8 +101,8 @@ class HomeworkDetailHandler extends Handler {
Object.assign(this.response.body, { pdict, psdict, rdict });
}
@param('tid', Types.ObjectID)
async postAttend(domainId: string, tid: ObjectID) {
@param('tid', Types.ObjectId)
async postAttend(domainId: string, tid: ObjectId) {
this.checkPerm(PERM.PERM_ATTEND_HOMEWORK);
const tdoc = await contest.get(domainId, tid);
if (contest.isDone(tdoc)) throw new HomeworkNotLiveError(tdoc.docId);
@@ -112,8 +112,8 @@ class HomeworkDetailHandler extends Handler {
}
class HomeworkEditHandler extends Handler {
@param('tid', Types.ObjectID, true)
async get(domainId: string, tid: ObjectID) {
@param('tid', Types.ObjectId, true)
async get(domainId: string, tid: ObjectId) {
const tdoc = tid ? await contest.get(domainId, tid) : null;
if (!tid) this.checkPerm(PERM.PERM_CREATE_HOMEWORK);
else if (!this.user.own(tdoc)) this.checkPerm(PERM.PERM_EDIT_HOMEWORK);
@@ -143,7 +143,7 @@ class HomeworkEditHandler extends Handler {
};
}
@param('tid', Types.ObjectID, true)
@param('tid', Types.ObjectId, true)
@param('beginAtDate', Types.Date)
@param('beginAtTime', Types.Time)
@param('penaltySinceDate', Types.Date)
@@ -157,7 +157,7 @@ class HomeworkEditHandler extends Handler {
@param('maintainer', Types.NumericArray, true)
@param('assign', Types.CommaSeperatedArray, true)
async postUpdate(
domainId: string, tid: ObjectID, beginAtDate: string, beginAtTime: string,
domainId: string, tid: ObjectId, beginAtDate: string, beginAtTime: string,
penaltySinceDate: string, penaltySinceTime: string, extensionDays: number,
penaltyRules: PenaltyRules, title: string, content: string, _pids: string, rated = false,
maintainer: number[] = [], assign: string[] = [],
@@ -203,8 +203,8 @@ class HomeworkEditHandler extends Handler {
this.response.redirect = this.url('homework_detail', { tid });
}
@param('tid', Types.ObjectID)
async postDelete(domainId: string, tid: ObjectID) {
@param('tid', Types.ObjectId)
async postDelete(domainId: string, tid: ObjectId) {
const tdoc = await contest.get(domainId, tid);
if (!this.user.own(tdoc)) this.checkPerm(PERM.PERM_EDIT_HOMEWORK);
await contest.del(domainId, tid);
+4 -4
View File
@@ -1,7 +1,7 @@
import assert from 'assert';
import yaml from 'js-yaml';
import { omit } from 'lodash';
import { ObjectID } from 'mongodb';
import { ObjectId } from 'mongodb';
import {
JudgeResultBody, ProblemConfigFile, RecordDoc, TestCase,
} from '../interface';
@@ -63,7 +63,7 @@ function processPayload(rdoc: RecordDoc, body: Partial<JudgeResultBody>) {
}
export async function next(body: Partial<JudgeResultBody>) {
body.rid = new ObjectID(body.rid);
body.rid = new ObjectId(body.rid);
let rdoc = await record.get(body.rid);
if (!rdoc) return;
const { $set, $push } = processPayload(rdoc, body);
@@ -113,7 +113,7 @@ export async function postJudge(rdoc: RecordDoc) {
const rdocs = await record.getMulti(rdoc.domainId, {
pid: rdoc.pid,
status: STATUS.STATUS_ACCEPTED,
contest: { $ne: new ObjectID('0'.repeat(24)) },
contest: { $ne: new ObjectId('0'.repeat(24)) },
}).project({ _id: 1, contest: 1 }).toArray();
const priority = await record.submissionPriority(rdoc.uid, -5000 - rdocs.length * 5 - 50);
await record.judge(rdoc.domainId, rdocs.map((r) => r._id), priority, {}, { hackRejudge: input });
@@ -128,7 +128,7 @@ export async function postJudge(rdoc: RecordDoc) {
}
export async function end(body: Partial<JudgeResultBody>) {
body.rid &&= new ObjectID(body.rid);
body.rid &&= new ObjectId(body.rid);
let rdoc = await record.get(body.rid);
if (!rdoc) return;
const { $set, $push } = processPayload(rdoc, body);
+39 -39
View File
@@ -3,7 +3,7 @@ import { readFile, statSync } from 'fs-extra';
import {
escapeRegExp, flattenDeep, intersection, pick, uniqBy,
} from 'lodash';
import { FilterQuery, ObjectID } from 'mongodb';
import { Filter, ObjectId } from 'mongodb';
import { nanoid } from 'nanoid';
import { sortFiles, streamToBuffer } from '@hydrooj/utils/lib/utils';
import {
@@ -101,7 +101,7 @@ registerResolver(
);
function buildQuery(udoc: User) {
const q: FilterQuery<ProblemDoc> = {};
const q: Filter<ProblemDoc> = {};
if (!udoc.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN)) {
q.$or = [
{ hidden: false },
@@ -309,8 +309,8 @@ export class ProblemDetailHandler extends ContestDetailBaseHandler {
psdoc: ProblemStatusDoc;
@route('pid', Types.ProblemId, true)
@query('tid', Types.ObjectID, true)
async _prepare(domainId: string, pid: number | string, tid?: ObjectID) {
@query('tid', Types.ObjectId, true)
async _prepare(domainId: string, pid: number | string, tid?: ObjectId) {
this.pdoc = await problem.get(domainId, pid);
if (!this.pdoc) throw new ProblemNotFoundError(domainId, pid);
if (tid) {
@@ -375,7 +375,7 @@ export class ProblemDetailHandler extends ContestDetailBaseHandler {
this.UiContext.extraTitleContent = this.pdoc.title;
}
@query('tid', Types.ObjectID, true)
@query('tid', Types.ObjectId, true)
@query('pjax', Types.Boolean)
async get(...args: any[]) {
// Navigate to current additional file download
@@ -433,7 +433,7 @@ export class ProblemDetailHandler extends ContestDetailBaseHandler {
this.checkPerm(PERM.PERM_REJUDGE_PROBLEM);
const rdocs = await record.getMulti(domainId, {
pid,
contest: { $ne: new ObjectID('0'.repeat(24)) },
contest: { $ne: new ObjectId('0'.repeat(24)) },
status: { $ne: STATUS.STATUS_CANCELED },
'files.hack': { $exists: false },
}).project({ _id: 1, contest: 1 }).toArray();
@@ -469,8 +469,8 @@ export class ProblemDetailHandler extends ContestDetailBaseHandler {
}
export class ProblemSubmitHandler extends ProblemDetailHandler {
@param('tid', Types.ObjectID, true)
async prepare(domainId: string, tid?: ObjectID) {
@param('tid', Types.ObjectId, true)
async prepare(domainId: string, tid?: ObjectId) {
if (tid && !contest.isOngoing(this.tdoc, this.tsdoc)) throw new ContestNotLiveError(this.tdoc.docId);
if (typeof this.pdoc.config === 'string') throw new ProblemConfigError();
}
@@ -497,8 +497,8 @@ export class ProblemSubmitHandler extends ProblemDetailHandler {
@param('code', Types.Content, true)
@param('pretest', Types.Boolean)
@param('input', Types.String, true)
@param('tid', Types.ObjectID, true)
async post(domainId: string, lang: string, code: string, pretest = false, input = '', tid?: ObjectID) {
@param('tid', Types.ObjectId, true)
async post(domainId: string, lang: string, code: string, pretest = false, input = '', tid?: ObjectId) {
const config = this.pdoc.config;
if (typeof config === 'string' || config === null) throw new ProblemConfigError();
if (['submit_answer', 'objective'].includes(config.type)) {
@@ -558,9 +558,9 @@ export class ProblemSubmitHandler extends ProblemDetailHandler {
export class ProblemHackHandler extends ProblemDetailHandler {
rdoc: RecordDoc;
@param('rid', Types.ObjectID)
@param('tid', Types.ObjectID, true)
async prepare(domainId: string, rid: ObjectID, tid?: ObjectID) {
@param('rid', Types.ObjectId)
@param('tid', Types.ObjectId, true)
async prepare(domainId: string, rid: ObjectId, tid?: ObjectId) {
if (typeof this.pdoc.config !== 'object' || !this.pdoc.config.hackable) throw new HackFailedError('This problem is not hackable.');
this.rdoc = await record.get(domainId, rid);
if (!this.rdoc || this.rdoc.pid !== this.pdoc.docId
@@ -586,8 +586,8 @@ export class ProblemHackHandler extends ProblemDetailHandler {
}
@param('input', Types.String, true)
@param('tid', Types.ObjectID, true)
async post(domainId: string, input = '', tid?: ObjectID) {
@param('tid', Types.ObjectId, true)
async post(domainId: string, input = '', tid?: ObjectId) {
await this.limitRate('add_record', 60, system.get('limit.submission_user'), true);
await this.limitRate('add_record', 60, system.get('limit.submission'), false);
const id = `${this.user._id}/${nanoid()}`;
@@ -814,9 +814,9 @@ export class ProblemFileDownloadHandler extends ProblemDetailHandler {
export class ProblemSolutionHandler extends ProblemDetailHandler {
@param('page', Types.PositiveInt, true)
@param('tid', Types.ObjectID, true)
@param('sid', Types.ObjectID, true)
async get(domainId: string, page = 1, tid?: ObjectID, sid?: ObjectID) {
@param('tid', Types.ObjectId, true)
@param('sid', Types.ObjectId, true)
async get(domainId: string, page = 1, tid?: ObjectId, sid?: ObjectId) {
if (tid) throw new PermissionError(PERM.PERM_VIEW_PROBLEM_SOLUTION);
this.response.template = 'problem_solution.html';
const accepted = this.tsdoc?.status === STATUS.STATUS_ACCEPTED;
@@ -857,8 +857,8 @@ export class ProblemSolutionHandler extends ProblemDetailHandler {
}
@param('content', Types.Content)
@param('psid', Types.ObjectID)
async postEditSolution(domainId: string, content: string, psid: ObjectID) {
@param('psid', Types.ObjectId)
async postEditSolution(domainId: string, content: string, psid: ObjectId) {
let psdoc = await solution.get(domainId, psid);
if (!this.user.own(psdoc)) this.checkPerm(PERM.PERM_EDIT_PROBLEM_SOLUTION);
else this.checkPerm(PERM.PERM_EDIT_PROBLEM_SOLUTION_SELF);
@@ -866,8 +866,8 @@ export class ProblemSolutionHandler extends ProblemDetailHandler {
this.back({ psdoc });
}
@param('psid', Types.ObjectID)
async postDeleteSolution(domainId: string, psid: ObjectID) {
@param('psid', Types.ObjectId)
async postDeleteSolution(domainId: string, psid: ObjectId) {
const psdoc = await solution.get(domainId, psid);
if (!this.user.own(psdoc)) this.checkPerm(PERM.PERM_DELETE_PROBLEM_SOLUTION);
else this.checkPerm(PERM.PERM_DELETE_PROBLEM_SOLUTION_SELF);
@@ -875,19 +875,19 @@ export class ProblemSolutionHandler extends ProblemDetailHandler {
this.back();
}
@param('psid', Types.ObjectID)
@param('psid', Types.ObjectId)
@param('content', Types.Content)
async postReply(domainId: string, psid: ObjectID, content: string) {
async postReply(domainId: string, psid: ObjectId, content: string) {
this.checkPerm(PERM.PERM_REPLY_PROBLEM_SOLUTION);
const psdoc = await solution.get(domainId, psid);
await solution.reply(domainId, psdoc.docId, this.user._id, content);
this.back();
}
@param('psid', Types.ObjectID)
@param('psrid', Types.ObjectID)
@param('psid', Types.ObjectId)
@param('psrid', Types.ObjectId)
@param('content', Types.Content)
async postEditReply(domainId: string, psid: ObjectID, psrid: ObjectID, content: string) {
async postEditReply(domainId: string, psid: ObjectId, psrid: ObjectId, content: string) {
const [psdoc, psrdoc] = await solution.getReply(domainId, psid, psrid);
if ((!psdoc) || psdoc.parentId !== this.pdoc.docId) throw new SolutionNotFoundError(domainId, psid);
if (!(this.user.own(psrdoc)
@@ -898,9 +898,9 @@ export class ProblemSolutionHandler extends ProblemDetailHandler {
this.back();
}
@param('psid', Types.ObjectID)
@param('psrid', Types.ObjectID)
async postDeleteReply(domainId: string, psid: ObjectID, psrid: ObjectID) {
@param('psid', Types.ObjectId)
@param('psrid', Types.ObjectId)
async postDeleteReply(domainId: string, psid: ObjectId, psrid: ObjectId) {
const [psdoc, psrdoc] = await solution.getReply(domainId, psid, psrid);
if ((!psdoc) || psdoc.parentId !== this.pdoc.docId) throw new SolutionNotFoundError(psid);
if (!(this.user.own(psrdoc)
@@ -911,24 +911,24 @@ export class ProblemSolutionHandler extends ProblemDetailHandler {
this.back();
}
@param('psid', Types.ObjectID)
async postUpvote(domainId: string, psid: ObjectID) {
@param('psid', Types.ObjectId)
async postUpvote(domainId: string, psid: ObjectId) {
const [psdoc, pssdoc] = await solution.vote(domainId, psid, this.user._id, 1);
this.back({ vote: psdoc.vote, user_vote: pssdoc.vote });
}
@param('psid', Types.ObjectID)
async postDownvote(domainId: string, psid: ObjectID) {
@param('psid', Types.ObjectId)
async postDownvote(domainId: string, psid: ObjectId) {
const [psdoc, pssdoc] = await solution.vote(domainId, psid, this.user._id, -1);
this.back({ vote: psdoc.vote, user_vote: pssdoc.vote });
}
}
export class ProblemSolutionRawHandler extends ProblemDetailHandler {
@param('psid', Types.ObjectID)
@route('psrid', Types.ObjectID, true)
@param('tid', Types.ObjectID, true)
async get(domainId: string, psid: ObjectID, psrid?: ObjectID, tid?: ObjectID) {
@param('psid', Types.ObjectId)
@route('psrid', Types.ObjectId, true)
@param('tid', Types.ObjectId, true)
async get(domainId: string, psid: ObjectId, psrid?: ObjectId, tid?: ObjectId) {
if (tid) throw new PermissionError(PERM.PERM_VIEW_PROBLEM_SOLUTION);
const accepted = this.tsdoc?.status === STATUS.STATUS_ACCEPTED;
if (!accepted || !this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_SOLUTION_ACCEPT)) {
@@ -1001,7 +1001,7 @@ export class ProblemPrefixListHandler extends Handler {
const search = global.Hydro.lib.problemSearch || defaultSearch;
const result = await search(domainId, prefix, { limit: 20 - pdocs.length });
const docs = await problem.getMulti(domainId, { docId: { $in: result.hits.map((i) => +i.split('/')[1]) } })
.project(buildProjection(projection)).toArray();
.project<ProblemDoc>(buildProjection(projection)).toArray();
pdocs.push(...docs);
}
this.response.body = uniqBy(pdocs, 'docId');
+21 -20
View File
@@ -1,7 +1,7 @@
import {
omit, pick, throttle, uniqBy,
} from 'lodash';
import { FilterQuery, ObjectID } from 'mongodb';
import { Filter, ObjectId } from 'mongodb';
import {
ContestNotAttendedError, ContestNotFoundError, HackRejudgeFailedError,
PermissionError, ProblemConfigError,
@@ -29,14 +29,14 @@ class RecordListHandler extends ContestDetailBaseHandler {
@param('page', Types.PositiveInt, true)
@param('pid', Types.ProblemId, true)
@param('tid', Types.ObjectID, true)
@param('tid', Types.ObjectId, true)
@param('uidOrName', Types.UidOrName, true)
@param('lang', Types.String, true)
@param('status', Types.Int, true)
@param('fullStatus', Types.Boolean)
@param('allDomain', Types.Boolean)
async get(
domainId: string, page = 1, pid?: string | number, tid?: ObjectID,
domainId: string, page = 1, pid?: string | number, tid?: ObjectId,
uidOrName?: string, lang?: string, status?: number, full = false,
all = false,
) {
@@ -44,7 +44,7 @@ class RecordListHandler extends ContestDetailBaseHandler {
let tdoc = null;
let invalid = false;
this.response.template = 'record_main.html';
const q: FilterQuery<RecordDoc> = { contest: tid };
const q: Filter<RecordDoc> = { contest: tid };
if (full) uidOrName = this.user._id.toString();
if (tid) {
tdoc = await contest.get(domainId, tid);
@@ -120,8 +120,8 @@ class RecordDetailHandler extends ContestDetailBaseHandler {
rdoc: RecordDoc;
tdoc?: Tdoc<30>;
@param('rid', Types.ObjectID)
async prepare(domainId: string, rid: ObjectID) {
@param('rid', Types.ObjectId)
async prepare(domainId: string, rid: ObjectId) {
this.rdoc = await record.get(domainId, rid);
if (!this.rdoc) throw new RecordNotFoundError(rid);
}
@@ -140,10 +140,10 @@ class RecordDetailHandler extends ContestDetailBaseHandler {
this.response.disposition = `attachment; filename="${langs[lang]?.code_file || `foo.${this.rdoc.lang}`}"`;
}
@param('rid', Types.ObjectID)
@param('rid', Types.ObjectId)
@param('download', Types.Boolean)
// eslint-disable-next-line consistent-return
async get(domainId: string, rid: ObjectID, download = false) {
async get(domainId: string, rid: ObjectId, download = false) {
const rdoc = this.rdoc;
let canViewDetail = true;
if (rdoc.contest?.toString() === '000000000000000000000000') {
@@ -187,14 +187,14 @@ class RecordDetailHandler extends ContestDetailBaseHandler {
};
}
@param('rid', Types.ObjectID)
@param('rid', Types.ObjectId)
async post() {
this.checkPerm(PERM.PERM_REJUDGE);
if (this.rdoc.files?.hack) throw new HackRejudgeFailedError();
}
@param('rid', Types.ObjectID)
async postRejudge(domainId: string, rid: ObjectID) {
@param('rid', Types.ObjectId)
async postRejudge(domainId: string, rid: ObjectId) {
const pdoc = await problem.get(domainId, this.rdoc.pid);
if (!pdoc?.config || typeof pdoc.config === 'string') throw new ProblemConfigError();
const priority = await record.submissionPriority(this.user._id, -20);
@@ -205,8 +205,8 @@ class RecordDetailHandler extends ContestDetailBaseHandler {
this.back();
}
@param('rid', Types.ObjectID)
async postCancel(domainId: string, rid: ObjectID) {
@param('rid', Types.ObjectId)
async postCancel(domainId: string, rid: ObjectId) {
const $set = {
status: STATUS.STATUS_CANCELED,
score: 0,
@@ -237,14 +237,14 @@ class RecordMainConnectionHandler extends ConnectionHandler {
pretest = false;
tdoc: Tdoc<30>;
@param('tid', Types.ObjectID, true)
@param('tid', Types.ObjectId, true)
@param('pid', Types.ProblemId, true)
@param('uidOrName', Types.UidOrName, true)
@param('status', Types.Int, true)
@param('pretest', Types.Boolean)
@param('allDomain', Types.Boolean)
async prepare(
domainId: string, tid?: ObjectID, pid?: string | number, uidOrName?: string,
domainId: string, tid?: ObjectId, pid?: string | number, uidOrName?: string,
status?: number, pretest = false, all = false,
) {
if (tid) {
@@ -279,8 +279,9 @@ class RecordMainConnectionHandler extends ConnectionHandler {
async message(msg: { rids: string[] }) {
if (!(msg.rids instanceof Array)) return;
const rids = msg.rids.map((id) => new ObjectID(id));
const rdocs = await record.getMulti(this.args.domainId, { _id: { $in: rids } }).project(buildProjection(record.PROJECTION_LIST)).toArray();
const rids = msg.rids.map((id) => new ObjectId(id));
const rdocs = await record.getMulti(this.args.domainId, { _id: { $in: rids } })
.project<RecordDoc>(buildProjection(record.PROJECTION_LIST)).toArray();
for (const rdoc of rdocs) this.onRecordChange(rdoc);
}
@@ -303,7 +304,7 @@ class RecordMainConnectionHandler extends ConnectionHandler {
user.getById(this.args.domainId, rdoc.uid),
problem.get(rdoc.domainId, rdoc.pid),
]);
const tdoc = this.tid ? this.tdoc || await contest.get(rdoc.domainId, new ObjectID(this.tid)) : null;
const tdoc = this.tid ? this.tdoc || await contest.get(rdoc.domainId, new ObjectId(this.tid)) : null;
if (pdoc && !rdoc.contest) {
if (!problem.canViewBy(pdoc, this.user)) pdoc = null;
if (!this.user.hasPerm(PERM.PERM_VIEW_PROBLEM)) pdoc = null;
@@ -325,8 +326,8 @@ class RecordDetailConnectionHandler extends ConnectionHandler {
disconnectTimeout: NodeJS.Timeout;
throttleSend: any;
@param('rid', Types.ObjectID)
async prepare(domainId: string, rid: ObjectID) {
@param('rid', Types.ObjectId)
async prepare(domainId: string, rid: ObjectId) {
const rdoc = await record.get(domainId, rid);
if (!rdoc) return;
if (rdoc.contest && rdoc.input === undefined) {
+5 -5
View File
@@ -39,11 +39,11 @@ class AdminStatusHandler extends Handler {
const record = await RecordModel.stat();
const status = await getStatus();
const [domainCount, userCount, problemCount, discussionCount, recordCount] = await Promise.all([
DomainModel.getMulti().count(),
UserModel.getMulti().count(),
DocumentModel.coll.find({ docType: DocumentModel.TYPE_PROBLEM }).count(),
DocumentModel.coll.find({ docType: DocumentModel.TYPE_DISCUSSION }).count(),
RecordModel.coll.find().count(),
DomainModel.coll.countDocuments(),
UserModel.coll.countDocuments(),
DocumentModel.coll.countDocuments({ docType: DocumentModel.TYPE_PROBLEM }),
DocumentModel.coll.countDocuments({ docType: DocumentModel.TYPE_DISCUSSION }),
RecordModel.coll.countDocuments(),
]);
this.response.body = {
record,
+24 -24
View File
@@ -1,7 +1,7 @@
import assert from 'assert';
import { statSync } from 'fs-extra';
import { pick } from 'lodash';
import { FilterQuery, ObjectID } from 'mongodb';
import { Filter, ObjectId } from 'mongodb';
import { sortFiles } from '@hydrooj/utils/lib/utils';
import {
FileLimitExceededError, FileUploadError, ProblemNotFoundError, ValidationError,
@@ -62,19 +62,19 @@ async function _parseDagJson(domainId: string, _dag: string): Promise<Tdoc['dag'
class TrainingMainHandler extends Handler {
@param('page', Types.PositiveInt, true)
async get(domainId: string, page = 1) {
const query: FilterQuery<TrainingDoc> = {};
const query: Filter<TrainingDoc> = {};
await this.ctx.parallel('training/list', query, this);
const [tdocs, tpcount] = await paginate(
training.getMulti(domainId),
page,
system.get('pagination.training'),
);
const tids: Set<ObjectID> = new Set();
const tids: Set<ObjectId> = new Set();
for (const tdoc of tdocs) tids.add(tdoc.docId);
const tsdict = {};
let tdict = {};
if (this.user.hasPriv(PRIV.PRIV_USER_PROFILE)) {
const enrolledTids: Set<ObjectID> = new Set();
const enrolledTids: Set<ObjectId> = new Set();
const tsdocs = await training.getMultiStatus(domainId, {
uid: this.user._id,
$or: [{ docId: { $in: Array.from(tids) } }, { enroll: 1 }],
@@ -97,9 +97,9 @@ class TrainingMainHandler extends Handler {
}
class TrainingDetailHandler extends Handler {
@param('tid', Types.ObjectID)
@param('tid', Types.ObjectId)
@param('uid', Types.PositiveInt, true)
async get(domainId: string, tid: ObjectID, uid = this.user._id) {
async get(domainId: string, tid: ObjectId, uid = this.user._id) {
const tdoc = await training.get(domainId, tid);
await this.ctx.parallel('training/get', tdoc, this);
let enrollUsers: number[] = [];
@@ -160,16 +160,16 @@ class TrainingDetailHandler extends Handler {
this.response.template = 'training_detail.html';
}
@param('tid', Types.ObjectID)
async postEnroll(domainId: string, tid: ObjectID) {
@param('tid', Types.ObjectId)
async postEnroll(domainId: string, tid: ObjectId) {
this.checkPriv(PRIV.PRIV_USER_PROFILE);
const tdoc = await training.get(domainId, tid);
await training.enroll(domainId, tdoc.docId, this.user._id);
this.back();
}
@param('tid', Types.ObjectID)
async postDelete(domainId: string, tid: ObjectID) {
@param('tid', Types.ObjectId)
async postDelete(domainId: string, tid: ObjectId) {
const tdoc = await training.get(domainId, tid);
if (!this.user.own(tdoc)) this.checkPerm(PERM.PERM_EDIT_TRAINING);
await training.del(domainId, tid);
@@ -180,8 +180,8 @@ class TrainingDetailHandler extends Handler {
class TrainingEditHandler extends Handler {
tdoc: TrainingDoc;
@param('tid', Types.ObjectID, true)
async prepare(domainId: string, tid: ObjectID) {
@param('tid', Types.ObjectId, true)
async prepare(domainId: string, tid: ObjectId) {
if (tid) {
this.tdoc = await training.get(domainId, tid);
if (!this.user.own(this.tdoc)) this.checkPerm(PERM.PERM_EDIT_TRAINING);
@@ -198,14 +198,14 @@ class TrainingEditHandler extends Handler {
}
}
@param('tid', Types.ObjectID, true)
@param('tid', Types.ObjectId, true)
@param('title', Types.Title)
@param('content', Types.Content)
@param('dag', Types.Content)
@param('pin', Types.Boolean)
@param('description', Types.Content)
async post(
domainId: string, tid: ObjectID,
domainId: string, tid: ObjectId,
title: string, content: string,
_dag: string, pin = false, description: string,
) {
@@ -229,15 +229,15 @@ class TrainingEditHandler extends Handler {
export class TrainingFilesHandler extends Handler {
tdoc: TrainingDoc;
@param('tid', Types.ObjectID)
async prepare(domainId: string, tid: ObjectID) {
@param('tid', Types.ObjectId)
async prepare(domainId: string, tid: ObjectId) {
this.tdoc = await training.get(domainId, tid);
if (!this.user.own(this.tdoc)) this.checkPerm(PERM.PERM_EDIT_TRAINING);
else this.checkPerm(PERM.PERM_EDIT_TRAINING_SELF);
}
@param('tid', Types.ObjectID)
async get(domainId: string, tid: ObjectID) {
@param('tid', Types.ObjectId)
async get(domainId: string, tid: ObjectId) {
if (!this.user.own(this.tdoc)) this.checkPerm(PERM.PERM_EDIT_TRAINING);
this.response.body = {
tdoc: this.tdoc,
@@ -250,9 +250,9 @@ export class TrainingFilesHandler extends Handler {
this.response.template = 'training_files.html';
}
@param('tid', Types.ObjectID)
@param('tid', Types.ObjectId)
@post('filename', Types.Filename, true)
async postUploadFile(domainId: string, tid: ObjectID, filename: string) {
async postUploadFile(domainId: string, tid: ObjectId, filename: string) {
if ((this.tdoc.files?.length || 0) >= system.get('limit.contest_files')) {
throw new FileLimitExceededError('count');
}
@@ -271,9 +271,9 @@ export class TrainingFilesHandler extends Handler {
this.back();
}
@param('tid', Types.ObjectID)
@param('tid', Types.ObjectId)
@post('files', Types.ArrayOf(Types.Filename))
async postDeleteFiles(domainId: string, tid: ObjectID, files: string[]) {
async postDeleteFiles(domainId: string, tid: ObjectId, files: string[]) {
await Promise.all([
storage.del(files.map((t) => `contest/${domainId}/${tid}/${t}`), this.user._id),
training.edit(domainId, tid, { files: this.tdoc.files.filter((i) => !files.includes(i.name)) }),
@@ -282,10 +282,10 @@ export class TrainingFilesHandler extends Handler {
}
}
export class TrainingFileDownloadHandler extends Handler {
@param('tid', Types.ObjectID)
@param('tid', Types.ObjectId)
@param('filename', Types.Filename)
@param('noDisposition', Types.Boolean)
async get(domainId: string, tid: ObjectID, filename: string, noDisposition = false) {
async get(domainId: string, tid: ObjectId, filename: string, noDisposition = false) {
this.response.addHeader('Cache-Control', 'public');
const target = `training/${domainId}/${tid}/${filename}`;
const file = await storage.getMeta(target);
+3 -1
View File
@@ -1,5 +1,6 @@
import { generateAuthenticationOptions, verifyAuthenticationResponse } from '@simplewebauthn/server';
import moment from 'moment-timezone';
import type { Binary } from 'mongodb';
import {
AuthOperationError, BlacklistedError, BuiltinLoginError, ForbiddenError, InvalidTokenError,
SystemError, UserAlreadyExistError, UserFacingError,
@@ -181,7 +182,8 @@ class UserWebauthnHandler extends Handler {
const tdoc = await token.get(challenge, token.TYPE_WEBAUTHN);
if (!tdoc) throw new InvalidTokenError(token.TYPE_TEXTS[token.TYPE_WEBAUTHN]);
const udoc = await user.getById(domainId, tdoc.uid);
const authenticator = udoc._authenticators?.find((c) => c.credentialID.toString('base64url') === result.id);
const parseId = (id: Binary) => Buffer.from(id.toString('hex'), 'hex').toString('base64url');
const authenticator = udoc._authenticators?.find((c) => parseId(c.credentialID) === result.id);
if (!authenticator) throw new ValidationError('authenticator');
const verification = await verifyAuthenticationResponse({
response: result,