diff --git a/hydro/entry/master.ts b/hydro/entry/master.ts index 1ed4254e1..18718925c 100644 --- a/hydro/entry/master.ts +++ b/hydro/entry/master.ts @@ -32,9 +32,9 @@ export async function load(call: Entry) { require('../lib/i18n'); require('../utils'); require('../error'); - try { - require('../options').default(); - } catch (e) { + const config = require('../options').default(); + if (!config) { + console.log('Starting setup'); await call({ entry: 'setup', newProcess: true }).catch((err) => { console.error('Cannot start setup process.', err); process.exit(1); diff --git a/hydro/handler/contest.ts b/hydro/handler/contest.ts index 7ff894b61..af4525480 100644 --- a/hydro/handler/contest.ts +++ b/hydro/handler/contest.ts @@ -8,14 +8,13 @@ import { } from '../error'; import { isContent, isTitle } from '../lib/validator'; import paginate from '../lib/paginate'; -import { PERM, PRIV } from '../model/builtin'; +import { PERM, PRIV, CONSTANT } from '../model/builtin'; import * as contest from '../model/contest'; import * as document from '../model/document'; import * as problem from '../model/problem'; import * as record from '../model/record'; import * as user from '../model/user'; import * as message from '../model/message'; -import * as system from '../model/system'; import { Route, Handler, Types, param, } from '../service/server'; @@ -40,7 +39,7 @@ class ContestListHandler extends ContestHandler { qs = `rule=${rule}`; } // eslint-disable-next-line prefer-const - [tdocs, tpcount] = await paginate(tdocs, page, await system.get('CONTEST_PER_PAGE')); + [tdocs, tpcount] = await paginate(tdocs, page, CONSTANT.CONTEST_PER_PAGE); const tids = []; for (const tdoc of tdocs) tids.push(tdoc.docId); const tsdict = await contest.getListStatus(domainId, this.user._id, tids); diff --git a/hydro/handler/discussion.ts b/hydro/handler/discussion.ts index 2fd7ac46d..055d5d4b9 100644 --- a/hydro/handler/discussion.ts +++ b/hydro/handler/discussion.ts @@ -1,14 +1,15 @@ import { ObjectID } from 'mongodb'; import { isSafeInteger } from 'lodash'; import { DiscussionNotFoundError, DocumentNotFoundError } from '../error'; -import { Drdoc, Mdoc } from '../interface'; +import { + Mdoc, Drdoc, Ddoc, Drrdoc, +} from '../interface'; import paginate from '../lib/paginate'; -import * as system from '../model/system'; import * as user from '../model/user'; import * as message from '../model/message'; import * as discussion from '../model/discussion'; import * as document from '../model/document'; -import { PERM, PRIV } from '../model/builtin'; +import { PERM, PRIV, CONSTANT } from '../model/builtin'; import { Route, Handler, Types, param, } from '../service/server'; @@ -23,46 +24,45 @@ export const typeMapper = { }; class DiscussionHandler extends Handler { - ddoc?: any; + ddoc?: Ddoc; - drdoc?: any; + drdoc?: Drdoc; - drrdoc?: any; + drrdoc?: Drrdoc; vnode?: any; - async _prepare({ - domainId, type, name, did, drid, drrid, - }) { + @param('type', Types.String, true) + @param('name', Types.String, 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, + ) { this.checkPerm(PERM.PERM_VIEW_DISCUSSION); if (did) { - did = new ObjectID(did); this.ddoc = await discussion.get(domainId, did); if (!this.ddoc) throw new DiscussionNotFoundError(did); type = discussion.typeDisplay[this.ddoc.parentType]; - name = this.ddoc.parentId; - if (drid) { - drid = new ObjectID(drid); + name = this.ddoc.parentId.toString(); + if (drrid) { + [this.drdoc, this.drrdoc] = await discussion.getTailReply(domainId, drid, drrid); + if (!this.drrdoc) throw new DiscussionNotFoundError(drrid); + } else if (drid) { this.drdoc = await discussion.getReply(domainId, drid); if (!this.drdoc) throw new DiscussionNotFoundError(drid); if (!this.drdoc.parentId.equals(this.ddoc._id)) { throw new DocumentNotFoundError(drid); } - if (drrid) { - drrid = new ObjectID(drrid); - [, this.drrdoc] = await discussion.getTailReply(domainId, drid, drrid); - if (!this.drrdoc) throw new DiscussionNotFoundError(drrid); - if (!this.drrdoc.parentId.equals(this.drdoc._id)) { - throw new DocumentNotFoundError(drid); - } - } } } // TODO(twd2): do more visibility check eg. contest // TODO(twd2): exclude problem/contest discussions? // TODO(iceboy): continuation based pagination. - if (ObjectID.isValid(name.toString())) name = new ObjectID(name); - this.vnode = await discussion.getVnode(domainId, typeMapper[type], name, this); + this.vnode = await discussion.getVnode(domainId, typeMapper[type], name); + if (this.vnode.hidden) this.checkPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN); if (this.ddoc) { this.ddoc.parentType = this.ddoc.parentType || this.vnode.type; this.ddoc.parentId = this.ddoc.parentId || this.vnode.id; @@ -76,7 +76,7 @@ class DiscussionMainHandler extends Handler { const [ddocs, dpcount] = await paginate( discussion.getMulti(domainId), page, - await system.get('DISCUSSION_PER_PAGE'), + CONSTANT.DISCUSSION_PER_PAGE, ); const udict = await user.getList(domainId, ddocs.map((ddoc) => ddoc.owner)); const path = [ @@ -84,7 +84,9 @@ class DiscussionMainHandler extends Handler { ['discussion_main', null], ]; const [vndict, vnodes] = await Promise.all([ - discussion.getListVnodes(domainId, ddocs, this), + discussion.getListVnodes( + domainId, ddocs, this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN), + ), discussion.getNodes(domainId), ]); this.response.template = 'discussion_main_or_node.html'; @@ -106,7 +108,7 @@ class DiscussionNodeHandler extends DiscussionHandler { const [ddocs, dpcount] = await paginate( discussion.getMulti(domainId, { parentType: typeMapper[type], parentId: name }), page, - await system.get('DISCUSSION_PER_PAGE'), + CONSTANT.DISCUSSION_PER_PAGE, ); const uids = ddocs.map((ddoc) => ddoc.owner); uids.push(this.vnode.owner); @@ -152,9 +154,10 @@ class DiscussionCreateHandler extends DiscussionHandler { @param('title', Types.String, isTitle) @param('content', Types.String, isContent) @param('highlight', Types.Boolean) + @param('pin', Types.Boolean) async post( domainId: string, type: string, _name: string, - title: string, content: string, highlight = false, + title: string, content: string, highlight = false, pin = false, ) { this.limitRate('add_discussion', 3600, 30); let name: ObjectID | string | number; @@ -162,9 +165,10 @@ class DiscussionCreateHandler extends DiscussionHandler { else if (isSafeInteger(parseInt(_name, 10))) name = parseInt(_name, 10); else name = _name; if (highlight) this.checkPerm(PERM.PERM_HIGHLIGHT_DISCUSSION); + if (pin) this.checkPerm(PERM.PERM_PIN_DISCUSSION); const did = await discussion.add( domainId, typeMapper[type], name, this.user._id, - title, content, this.request.ip, highlight, + title, content, this.request.ip, highlight, pin, ); this.response.body = { did }; this.response.redirect = this.url('discussion_detail', { did }); @@ -181,7 +185,7 @@ class DiscussionDetailHandler extends DiscussionHandler { const [drdocs, pcount, drcount] = await paginate( discussion.getMultiReply(domainId, did), page, - await system.get('REPLY_PER_PAGE'), + CONSTANT.REPLY_PER_PAGE, ); const uids = drdocs.map((drdoc) => drdoc.owner); uids.push(this.ddoc.owner); @@ -327,13 +331,16 @@ class DiscussionEditHandler extends DiscussionHandler { @param('title', Types.String, isTitle) @param('content', Types.String, isContent) @param('highlight', Types.Boolean) + @param('pin', Types.Boolean) async postUpdate( - domainId: string, did: ObjectID, title: string, content: string, highlight = false, + domainId: string, did: ObjectID, title: string, content: string, + highlight = false, pin = false, ) { if (this.ddoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_DISCUSSION); else this.checkPerm(PERM.PERM_EDIT_DISCUSSION_SELF); - if (highlight && !this.ddoc.highlight) this.checkPerm(PERM.PERM_HIGHLIGHT_DISCUSSION); - await discussion.edit(domainId, did, title, content, highlight); + if (highlight !== this.ddoc.highlight) this.checkPerm(PERM.PERM_HIGHLIGHT_DISCUSSION); + if (pin !== this.ddoc.pin) this.checkPerm(PERM.PERM_PIN_DISCUSSION); + await discussion.edit(domainId, did, title, content, highlight, pin); this.response.body = { did }; this.response.redirect = this.url('discussion_detail', { did }); } diff --git a/hydro/handler/home.ts b/hydro/handler/home.ts index be1ffe98d..7712c1105 100644 --- a/hydro/handler/home.ts +++ b/hydro/handler/home.ts @@ -21,7 +21,7 @@ import * as domain from '../model/domain'; import * as discussion from '../model/discussion'; import * as token from '../model/token'; import * as training from '../model/training'; -import { PERM, PRIV } from '../model/builtin'; +import { PERM, PRIV, CONSTANT } from '../model/builtin'; import { isContent, isPassword, isEmail, isTitle, } from '../lib/validator'; @@ -33,7 +33,7 @@ class HomeHandler extends Handler { if (this.user.hasPerm(PERM.PERM_VIEW_HOMEWORK)) { const tdocs = await contest.getMulti(domainId, {}, document.TYPE_HOMEWORK) .sort('beginAt', -1) - .limit(await system.get('HOMEWORK_ON_MAIN')) + .limit(CONSTANT.HOMEWORK_ON_MAIN) .toArray(); const tsdict = await contest.getListStatus( domainId, this.user._id, @@ -48,7 +48,7 @@ class HomeHandler extends Handler { if (this.user.hasPerm(PERM.PERM_VIEW_CONTEST)) { const tdocs = await contest.getMulti(domainId) .sort('beginAt', -1) - .limit(await system.get('CONTEST_ON_MAIN')) + .limit(CONSTANT.CONTEST_ON_MAIN) .toArray(); const tsdict = await contest.getListStatus( domainId, this.user._id, tdocs.map((tdoc) => tdoc.docId), @@ -62,7 +62,7 @@ class HomeHandler extends Handler { if (this.user.hasPerm(PERM.PERM_VIEW_TRAINING)) { const tdocs = await training.getMulti(domainId) .sort('_id', 1) - .limit(await system.get('TRAINING_ON_MAIN')) + .limit(CONSTANT.TRAINING_ON_MAIN) .toArray(); const tsdict = await training.getListStatus( domainId, this.user._id, tdocs.map((tdoc) => tdoc.docId), @@ -75,9 +75,11 @@ class HomeHandler extends Handler { async discussion(domainId: string): Promise<[any[], any]> { if (this.user.hasPerm(PERM.PERM_VIEW_DISCUSSION)) { const ddocs = await discussion.getMulti(domainId) - .limit(await system.get('DISCUSSION_ON_MAIN')) + .limit(CONSTANT.DISCUSSION_ON_MAIN) .toArray(); - const vndict = await discussion.getListVnodes(domainId, ddocs, this); + const vndict = await discussion.getListVnodes( + domainId, ddocs, this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN), + ); return [ddocs, vndict]; } return [[], {}]; diff --git a/hydro/handler/homework.ts b/hydro/handler/homework.ts index 206628e5d..a4bb9a6cf 100644 --- a/hydro/handler/homework.ts +++ b/hydro/handler/homework.ts @@ -12,8 +12,7 @@ import { Route, Handler, Types, param, } from '../service/server'; import * as bus from '../service/bus'; -import { PERM } from '../model/builtin'; -import * as system from '../model/system'; +import { PERM, CONSTANT } from '../model/builtin'; import * as user from '../model/user'; import * as contest from '../model/contest'; import * as discussion from '../model/discussion'; @@ -84,7 +83,7 @@ class HomeworkDetailHandler extends HomeworkHandler { const [ddocs, dpcount, dcount] = await paginate( discussion.getMulti(domainId, { parentType: tdoc.docType, parentId: tdoc.docId }), page, - await system.get('DISCUSSION_PER_PAGE'), + CONSTANT.DISCUSSION_PER_PAGE, ); const uids = ddocs.map((ddoc) => ddoc.owner); uids.push(tdoc.owner); diff --git a/hydro/handler/misc.ts b/hydro/handler/misc.ts index 217ab9756..29d29a431 100644 --- a/hydro/handler/misc.ts +++ b/hydro/handler/misc.ts @@ -102,34 +102,6 @@ class SwitchLanguageHandler extends Handler { } } -class UiSettingsHandler extends Handler { - async get() { - const [ - header_logo, header_logo_2x, - nav_logo_dark, nav_logo_light, - nav_logo_dark_2x, nav_logo_light_2x, - header_background, header_background_2x, - ] = await system.getMany([ - 'ui.header_logo', 'ui.header_logo_2x', - 'ui.nav_logo_dark', 'ui.nav_logo_light', - 'ui.nav_logo_dark_2x', 'ui.nav_logo_light_2x', - 'ui.header_background', 'ui.header_background2x', - ]); - this.response.body = await this.renderHTML('extra.css', { - header_logo, - header_logo_2x, - nav_logo_dark, - nav_logo_light, - nav_logo_dark_2x, - nav_logo_light_2x, - header_background, - header_background_2x, - }); - this.response.type = 'text/css'; - this.ctx.set('nolog', '1'); - } -} - export async function apply() { Route('file_download', '/fs/:fileId/:secret', FileDownloadHandler); Route('file_download_with_name', '/fs/:fileId/:name/:secret', FileDownloadHandler); @@ -137,7 +109,6 @@ export async function apply() { Route('status', '/status', StatusHandler); Route('status_update', '/status/update', StatusUpdateHandler); Route('switch_language', '/language/:lang', SwitchLanguageHandler); - Route('ui', '/extra.css', UiSettingsHandler); } global.Hydro.handler.misc = apply; diff --git a/hydro/handler/problem.ts b/hydro/handler/problem.ts index bf09d42d5..5294b2247 100644 --- a/hydro/handler/problem.ts +++ b/hydro/handler/problem.ts @@ -19,8 +19,7 @@ import * as record from '../model/record'; import * as domain from '../model/domain'; import * as user from '../model/user'; import * as solution from '../model/solution'; -import * as system from '../model/system'; -import { PERM, PRIV } from '../model/builtin'; +import { PERM, PRIV, CONSTANT } from '../model/builtin'; import * as bus from '../service/bus'; import { Route, Connection, Handler, ConnectionHandler, Types, param, multipart, @@ -78,7 +77,7 @@ class ProblemMainHandler extends ProblemHandler { const [pdocs, ppcount, pcount] = await paginate( problem.getMulti(domainId, query).sort({ pid: 1 }), page, - await system.get('PROBLEM_PER_PAGE'), + CONSTANT.PROBLEM_PER_PAGE, ); if (this.user.hasPriv(PRIV.PRIV_USER_PROFILE)) { psdict = await problem.getListStatus( @@ -122,7 +121,7 @@ class ProblemCategoryHandler extends ProblemHandler { const [pdocs, ppcount, pcount] = await paginate( problem.getMulti(domainId, q).sort({ pid: 1 }), page, - await system.get('PROBLEM_PER_PAGE'), + CONSTANT.PROBLEM_PER_PAGE, ); if (this.user.hasPriv(PRIV.PRIV_USER_PROFILE)) { psdict = await problem.getListStatus( @@ -466,7 +465,7 @@ class ProblemSolutionHandler extends ProblemDetailHandler { const [psdocs, pcount, pscount] = await paginate( solution.getMulti(domainId, this.pdoc.docId), page, - await system.get('SOLUTION_PER_PAGE'), + CONSTANT.SOLUTION_PER_PAGE, ); const uids = [this.pdoc.owner]; const docids = []; diff --git a/hydro/handler/record.ts b/hydro/handler/record.ts index c1563d7bf..1e91d65f1 100644 --- a/hydro/handler/record.ts +++ b/hydro/handler/record.ts @@ -1,10 +1,9 @@ import { ObjectID } from 'mongodb'; import { PermissionError, RecordNotFoundError } from '../error'; -import { PERM } from '../model/builtin'; +import { PERM, CONSTANT } from '../model/builtin'; import * as problem from '../model/problem'; import * as record from '../model/record'; import * as contest from '../model/contest'; -import * as system from '../model/system'; import * as user from '../model/user'; import paginate from '../lib/paginate'; import * as bus from '../service/bus'; @@ -34,7 +33,7 @@ class RecordListHandler extends RecordHandler { const [rdocs] = await paginate( record.getMulti(domainId, q).sort('_id', -1), page, - await system.get('RECORD_PER_PAGE'), + CONSTANT.RECORD_PER_PAGE, ); const canViewProblemHidden = this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN); const [udict, pdict] = await Promise.all([ diff --git a/hydro/handler/training.ts b/hydro/handler/training.ts index d8a821f1e..f0426e1ba 100644 --- a/hydro/handler/training.ts +++ b/hydro/handler/training.ts @@ -3,12 +3,11 @@ import { ObjectID } from 'mongodb'; import { ValidationError, ProblemNotFoundError } from '../error'; import { isTitle, isContent, isDescription } from '../lib/validator'; import paginate from '../lib/paginate'; -import { PERM, PRIV } from '../model/builtin'; +import { PERM, PRIV, CONSTANT } from '../model/builtin'; import * as problem from '../model/problem'; import * as builtin from '../model/builtin'; import * as training from '../model/training'; import * as user from '../model/user'; -import * as system from '../model/system'; import { Route, Handler, Types, param, } from '../service/server'; @@ -58,9 +57,9 @@ class TrainingMainHandler extends Handler { @param('page', Types.UnsignedInt, true) async get(domainId: string, page = 1) { const [tdocs, tpcount] = await paginate( - training.getMulti(domainId).sort('_id', 1), + training.getMulti(domainId), page, - await system.get('TRAINING_PER_PAGE'), + CONSTANT.TRAINING_PER_PAGE, ); const tids: Set = new Set(); for (const tdoc of tdocs) tids.add(tdoc.docId); diff --git a/hydro/interface.ts b/hydro/interface.ts index 16fa9ac44..c7a13bd96 100644 --- a/hydro/interface.ts +++ b/hydro/interface.ts @@ -231,6 +231,7 @@ export interface DomainDoc extends Dictionary { roles: Dictionary, gravatar: string, bulletin: string, + pidCounter: number, } // Message @@ -262,10 +263,14 @@ export interface Ddoc { _id: ObjectID, docType: number, docId: ObjectID, + parentType: number, + parentId: ObjectID | number, owner: number, title: string, content: string, ip: string, + pin: boolean, + highlight: boolean, } // Discussion reply @@ -277,6 +282,7 @@ export interface Drdoc { parentId: ObjectID, owner: number, ip: string, + content: string, reply: Drrdoc[], } diff --git a/hydro/model/builtin.ts b/hydro/model/builtin.ts index 0b031855e..0598fd0be 100644 --- a/hydro/model/builtin.ts +++ b/hydro/model/builtin.ts @@ -46,6 +46,7 @@ export const PERM = { PERM_VIEW_DISCUSSION: 1n << 27n, PERM_CREATE_DISCUSSION: 1n << 28n, PERM_HIGHLIGHT_DISCUSSION: 1n << 29n, + PERM_PIN_DISCUSSION: 1n << 59n, PERM_EDIT_DISCUSSION: 1n << 30n, PERM_EDIT_DISCUSSION_SELF: 1n << 31n, PERM_DELETE_DISCUSSION: 1n << 32n, @@ -603,6 +604,20 @@ export const USER_GENDER_ICONS = { [USER_GENDER_OTHER]: '?', }; +export const CONSTANT = { + PROBLEM_PER_PAGE: 100, + CONTEST_PER_PAGE: 20, + DISCUSSION_PER_PAGE: 50, + RECORD_PER_PAGE: 100, + SOLUTION_PER_PAGE: 20, + TRAINING_PER_PAGE: 10, + REPLY_PER_PAGE: 50, + HOMEWORK_ON_MAIN: 5, + DISCUSSION_ON_MAIN: 20, + CONTEST_ON_MAIN: 10, + TRAINING_ON_MAIN: 10, +}; + global.Hydro.model.builtin = { Permission, PERM, diff --git a/hydro/model/discussion.ts b/hydro/model/discussion.ts index 1c569f9a7..cf42cd3f5 100644 --- a/hydro/model/discussion.ts +++ b/hydro/model/discussion.ts @@ -3,7 +3,6 @@ import * as problem from './problem'; import * as contest from './contest'; import * as training from './training'; import * as document from './document'; -import { PERM } from './builtin'; import { DocumentNotFoundError } from '../error'; import { Ddoc, Drdoc, Drrdoc } from '../interface'; @@ -18,7 +17,7 @@ export const typeDisplay = { export function add( domainId: string, parentType: number, parentId: ObjectID | number | string, owner: number, title: string, content: string, - ip: string | null = null, highlight: boolean = false, + ip: string | null = null, highlight: boolean, pin: boolean, ): Promise { return document.add( domainId, content, owner, document.TYPE_DISCUSSION, @@ -26,8 +25,9 @@ export function add( { title, ip, - highlight, nReply: 0, + highlight, + pin, updateAt: new Date(), views: 0, }, @@ -40,9 +40,11 @@ export function get(domainId: string, did: ObjectID): Promise { export function edit( domainId: string, did: ObjectID, - title: string, content: string, highlight: boolean, + title: string, content: string, highlight: boolean, pin: boolean, ): Promise { - return document.set(domainId, document.TYPE_DISCUSSION, did, { title, content, highlight }); + return document.set(domainId, document.TYPE_DISCUSSION, did, { + title, content, highlight, pin, + }); } export function del(domainId: string, did: ObjectID) { @@ -60,7 +62,8 @@ export function count(domainId: string, query: any) { } export function getMulti(domainId: string, query: any = {}) { - return document.getMulti(domainId, document.TYPE_DISCUSSION, query).sort('updateAt', -1); + return document.getMulti(domainId, document.TYPE_DISCUSSION, query) + .sort({ pin: -1, updateAt: -1 }); } export async function addReply( @@ -158,13 +161,12 @@ export function getNode(domainId: string, _id: string) { return document.get(domainId, document.TYPE_DISCUSSION_NODE, _id); } -export async function getVnode(domainId: string, type: number, id: string, handler: any) { +export async function getVnode(domainId: string, type: number, id: string) { if (type === document.TYPE_PROBLEM) { // @ts-ignore if (Number.isSafeInteger(parseInt(id, 10))) id = parseInt(id, 10); const pdoc = await problem.get(domainId, id); if (!pdoc) return null; - if (pdoc.hidden && handler) handler.checkPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN); return { ...pdoc, type, id }; } if (type === document.TYPE_CONTEST) { @@ -199,12 +201,13 @@ export function getNodes(domainId: string) { return document.getMulti(domainId, document.TYPE_DISCUSSION_NODE).toArray(); } -export async function getListVnodes(domainId: string, ddocs: any, handler: any) { +export async function getListVnodes(domainId: string, ddocs: any, getHidden: boolean) { const tasks = []; const res = {}; - async function task(ddoc) { - const vnode = await getVnode(domainId, ddoc.parentType, ddoc.parentId, handler); + async function task(ddoc: Ddoc) { + const vnode = await getVnode(domainId, ddoc.parentType, ddoc.parentId.toString()); if (!res[ddoc.parentType]) res[ddoc.parentType] = {}; + if (vnode.hidden && !getHidden) return; res[ddoc.parentType][ddoc.parentId] = vnode; } for (const ddoc of ddocs) tasks.push(task(ddoc)); diff --git a/hydro/model/document.ts b/hydro/model/document.ts index 571ff90f5..3dd5502fa 100644 --- a/hydro/model/document.ts +++ b/hydro/model/document.ts @@ -328,66 +328,40 @@ export async function revSetStatus( return await collStatus.updateOne(filter, update); } +/* eslint-disable object-curly-newline */ async function ensureIndexes() { - await coll.createIndex({ domainId: 1, docType: 1, docId: 1 }, { unique: true }); - await coll.createIndex({ - domainId: 1, docType: 1, owner: 1, docId: -1, - }); + const ic = coll.createIndex.bind(coll); + const is = collStatus.createIndex.bind(coll); + const u = { unique: true }; + const s = { sparse: true }; + await ic({ domainId: 1, docType: 1, docId: 1 }, u); + await ic({ domainId: 1, docType: 1, owner: 1, docId: -1 }); // For problem - await coll.createIndex({ - domainId: 1, docType: 1, category: 1, docId: 1, - }, { sparse: true }); - await coll.createIndex({ - domainId: 1, docType: 1, hidden: 1, category: 1, docId: 1, - }, { sparse: true }); - await coll.createIndex({ - domainId: 1, docType: 1, tag: 1, docId: 1, - }, { sparse: true }); - await coll.createIndex({ - domainId: 1, docType: 1, hidden: 1, tag: 1, docId: 1, - }, { sparse: true }); + await ic({ domainId: 1, docType: 1, category: 1, docId: 1 }, s); + await ic({ domainId: 1, docType: 1, hidden: 1, category: 1, docId: 1 }, s); + await ic({ domainId: 1, docType: 1, tag: 1, docId: 1 }, s); + await ic({ domainId: 1, docType: 1, hidden: 1, tag: 1, docId: 1 }, s); // For problem solution - await coll.createIndex({ - domainId: 1, docType: 1, parentType: 1, parentId: 1, vote: -1, docId: -1, - }, { sparse: true }); + await ic({ domainId: 1, docType: 1, parentType: 1, parentId: 1, vote: -1, docId: -1 }, s); // For discussion - await coll.createIndex({ - domainId: 1, docType: 1, updateAt: -1, docId: -1, - }, { sparse: true }); - await coll.createIndex({ - domainId: 1, docType: 1, parentType: 1, parentId: 1, updateAt: -1, docId: -1, - }, { sparse: true }); + await ic({ domainId: 1, docType: 1, pin: -1, updateAt: -1, docId: -1 }, s); + await ic({ domainId: 1, docType: 1, parentType: 1, parentId: 1, updateAt: -1, docId: -1 }, s); // Hidden doc - await coll.createIndex({ - domainId: 1, docType: 1, hidden: 1, docId: -1, - }, { sparse: true }); + await ic({ domainId: 1, docType: 1, hidden: 1, docId: -1 }, s); // For contest - await coll.createIndex({ domainId: 1, docType: 1, pids: 1 }, { sparse: true }); - await coll.createIndex({ - domainId: 1, docType: 1, rule: 1, docId: -1, - }, { sparse: true }); + await ic({ domainId: 1, docType: 1, pids: 1 }, s); + await ic({ domainId: 1, docType: 1, rule: 1, docId: -1 }, s); // For training - await coll.createIndex({ domainId: 1, docType: 1, 'dag.pids': 1 }, { sparse: true }); - - await collStatus.createIndex({ - domainId: 1, docType: 1, uid: 1, docId: 1, - }, { unique: true }); + await ic({ domainId: 1, docType: 1, 'dag.pids': 1 }, s); + await is({ domainId: 1, docType: 1, uid: 1, docId: 1 }, u); // For rp system - await collStatus.createIndex({ - domainId: 1, docType: 1, docId: 1, status: 1, rid: 1, rp: 1, - }, { sparse: true }); + await is({ domainId: 1, docType: 1, docId: 1, status: 1, rid: 1, rp: 1 }, s); // For contest rule OI - await collStatus.createIndex({ - domainId: 1, docType: 1, docId: 1, score: -1, - }, { sparse: true }); + await is({ domainId: 1, docType: 1, docId: 1, score: -1 }, s); // For contest rule ACM - await collStatus.createIndex({ - domainId: 1, docType: 1, docId: 1, accept: -1, time: 1, - }, { sparse: true }); + await is({ domainId: 1, docType: 1, docId: 1, accept: -1, time: 1 }, s); // For training - await collStatus.createIndex({ - domainId: 1, docType: 1, uid: 1, enroll: 1, docId: 1, - }, { sparse: true }); + await is({ domainId: 1, docType: 1, uid: 1, enroll: 1, docId: 1 }, s); } global.Hydro.postInit.push(ensureIndexes); diff --git a/hydro/model/domain.ts b/hydro/model/domain.ts index 23148e0cb..147d01e64 100644 --- a/hydro/model/domain.ts +++ b/hydro/model/domain.ts @@ -7,9 +7,16 @@ const coll = db.collection('domain'); const collUser = db.collection('domain.user'); export async function add(domainId: string, owner: number, name: string, bulletin: string) { - await coll.insertOne({ - _id: domainId, owner, name, bulletin, roles: {}, - }); + const ddoc: DomainDoc = { + _id: domainId, + owner, + name, + bulletin, + roles: {}, + gravatar: '', + pidCounter: 0, + }; + await coll.insertOne(ddoc); return domainId; } @@ -17,11 +24,6 @@ export function get(domainId: string): Promise { return coll.findOne({ _id: domainId }); } -export function getMany(query: any, sort: any, page: number, limit: number): Promise { - return coll.find(query).sort(sort).skip((page - 1) * limit).limit(limit) - .toArray(); -} - export function getMulti(query: any = {}) { return coll.find(query); } @@ -36,7 +38,7 @@ export async function inc(domainId: string, field: string, n: number): Promise> { @@ -155,7 +157,6 @@ global.Hydro.model.domain = { add, inc, get, - getMany, edit, getMulti, getList, diff --git a/hydro/model/setting.ts b/hydro/model/setting.ts index 6345dee4f..c424e887a 100644 --- a/hydro/model/setting.ts +++ b/hydro/model/setting.ts @@ -134,17 +134,6 @@ SystemSetting( Setting('setting_oauth', 'oauth.googleappid', null, null, 'text', 'Google Oauth ClientID', null), Setting('setting_oauth', 'oauth.googlesecret', null, null, 'text', 'Google Oauth Secret', null, FLAG_SECRET), Setting('setting_proxy', 'proxy', null, null, 'text', 'Proxy Server URL'), - Setting('setting_storage', 'PROBLEM_PER_PAGE', null, 100, 'number', 'Problems per Page', null, FLAG_HIDDEN), - Setting('setting_storage', 'CONTEST_PER_PAGE', null, 20, 'number', 'Contests per Page', null, FLAG_HIDDEN), - Setting('setting_storage', 'DISCUSSION_PER_PAGE', null, 50, 'number', 'Discussion per Page', null, FLAG_HIDDEN), - Setting('setting_storage', 'RECORD_PER_PAGE', null, 100, 'number', 'Record per Page', null, FLAG_HIDDEN), - Setting('setting_storage', 'SOLUTION_PER_PAGE', null, 20, 'number', 'Solutions per Page', null, FLAG_HIDDEN), - Setting('setting_storage', 'TRAINING_PER_PAGE', null, 10, 'number', 'Training per Page', null, FLAG_HIDDEN), - Setting('setting_storage', 'REPLY_PER_PAGE', null, 50, 'number', 'Reply per Page', null, FLAG_HIDDEN), - Setting('setting_storage', 'HOMEWORK_ON_MAIN', null, 5, 'number', 'HOMEWORK_ON_MAIN', null, FLAG_HIDDEN), - Setting('setting_storage', 'DISCUSSION_ON_MAIN', null, 20, 'number', 'DISCUSSION_ON_MAIN', null, FLAG_HIDDEN), - Setting('setting_storage', 'CONTEST_ON_MAIN', null, 10, 'number', 'CONTEST_ON_MAIN', null, FLAG_HIDDEN), - Setting('setting_storage', 'TRAINING_ON_MAIN', null, 10, 'number', 'TRAINING_ON_MAIN', null, FLAG_HIDDEN), Setting('setting_storage', 'db.ver', null, 1, 'number', 'Database version', null, FLAG_DISABLED | FLAG_HIDDEN), Setting('setting_storage', 'user', null, 1, 'number', 'User Counter', null, FLAG_DISABLED | FLAG_HIDDEN), ); diff --git a/hydro/model/training.ts b/hydro/model/training.ts index 8080d361a..64f3b8125 100644 --- a/hydro/model/training.ts +++ b/hydro/model/training.ts @@ -108,7 +108,7 @@ export async function getList(domainId: string, tids: ObjectID[]) { } export const getMulti = (domainId: string, query: any = {}) => - document.getMulti(domainId, document.TYPE_TRAINING, query); + document.getMulti(domainId, document.TYPE_TRAINING, query).sort('_id', 1); global.Hydro.model.training = { getPids, diff --git a/hydro/options.ts b/hydro/options.ts index 54e9601d6..79d00673f 100644 --- a/hydro/options.ts +++ b/hydro/options.ts @@ -8,6 +8,7 @@ export default function load() { if (!fs.existsSync(f)) f = path.resolve(os.homedir(), '.config', 'hydro', 'config.json'); if (!fs.existsSync(f)) f = path.resolve(os.homedir(), '.hydro', 'config.json'); if (!fs.existsSync(f)) f = path.resolve('/config/config.json'); + if (!fs.existsSync(f)) return null; let result: any = {}; try { result = JSON.parse(fs.readFileSync(f).toString()); diff --git a/hydro/service/server.ts b/hydro/service/server.ts index dda17ebd8..7f56b736d 100644 --- a/hydro/service/server.ts +++ b/hydro/service/server.ts @@ -745,7 +745,7 @@ export function Connection( // TODO use postInit? export async function start() { const port = await system.get('server.port'); - if (!argv.debug) { + if (argv.debug) { app.use(morgan(':method :url :status :res[content-length] - :response-time ms', { skip: (req, res) => res.hasHeader('nolog'), })); diff --git a/locales/zh_CN.yaml b/locales/zh_CN.yaml index 17336880c..0df07cb59 100644 --- a/locales/zh_CN.yaml +++ b/locales/zh_CN.yaml @@ -524,7 +524,6 @@ Problem Solution: 题解列表 Problem Tags Visibility: 题目标签可见性 problem_create: 创建题目 problem_detail: 题目详情 -problem_detail: 题目详情 problem_edit: 编辑题目 problem_import: 导入题目 problem_main: 题库 diff --git a/package.json b/package.json index be13aca78..2ee7e3dfc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hydrooj", - "version": "2.11.9", + "version": "2.12.0", "main": "dist/loader.js", "bin": "bin/hydrooj.js", "repository": "https://github.com/hydro-dev/Hydro.git",