diff --git a/packages/hydrooj/package.json b/packages/hydrooj/package.json index 47e6bc89f..2c76c61d1 100644 --- a/packages/hydrooj/package.json +++ b/packages/hydrooj/package.json @@ -1,6 +1,6 @@ { "name": "hydrooj", - "version": "2.32.13", + "version": "2.32.14", "bin": "bin/hydrooj.js", "main": "src/loader", "module": "src/loader", diff --git a/packages/hydrooj/src/model/discussion.ts b/packages/hydrooj/src/model/discussion.ts index ba8332e93..bd30bd2ba 100644 --- a/packages/hydrooj/src/model/discussion.ts +++ b/packages/hydrooj/src/model/discussion.ts @@ -89,7 +89,7 @@ export async function add( }; await bus.serial('discussion/before-add', payload); const res = await document.add( - payload.domainId, payload.content, payload.owner, document.TYPE_DISCUSSION, + payload.domainId!, payload.content!, payload.owner!, document.TYPE_DISCUSSION, null, payload.parentType, payload.parentId, omit(payload, ['domainId', 'content', 'owner', 'parentType', 'parentId']), ); payload.docId = res; diff --git a/packages/hydrooj/src/model/domain.ts b/packages/hydrooj/src/model/domain.ts index 648be46e0..9e25766a0 100644 --- a/packages/hydrooj/src/model/domain.ts +++ b/packages/hydrooj/src/model/domain.ts @@ -63,7 +63,7 @@ class DomainModel { const query: FilterQuery = { lower: domainId.toLowerCase() }; await bus.serial('domain/before-get', query); const result = await coll.findOne(query); - await bus.serial('domain/get', result); + if (result) await bus.serial('domain/get', result); return result; } @@ -72,7 +72,7 @@ class DomainModel { const query: FilterQuery = { host }; await bus.serial('domain/before-get', query); const result = await coll.findOne(query); - await bus.serial('domain/get', result); + if (result) await bus.serial('domain/get', result); return result; } @@ -83,7 +83,7 @@ class DomainModel { static async edit(domainId: string, $set: Partial) { await bus.serial('domain/before-update', domainId, $set); const result = await coll.findOneAndUpdate({ _id: domainId }, { $set }, { returnDocument: 'after' }); - await bus.serial('domain/update', domainId, $set, result.value); + if (result.value) await bus.serial('domain/update', domainId, $set, result.value); return result.value; } @@ -96,12 +96,12 @@ class DomainModel { { $inc: { [field]: n } }, { returnDocument: 'after' }, ); - return res.value[field]; + return res.value?.[field]; } @ArgMethod - static async getList(domainIds: string[]): Promise> { - const r = {}; + static async getList(domainIds: string[]) { + const r: Record = {}; // eslint-disable-next-line no-await-in-loop for (const domainId of domainIds) r[domainId] = await DomainModel.get(domainId); return r; diff --git a/packages/hydrooj/src/model/message.ts b/packages/hydrooj/src/model/message.ts index bd57e42c2..09b50d3d9 100644 --- a/packages/hydrooj/src/model/message.ts +++ b/packages/hydrooj/src/model/message.ts @@ -48,7 +48,7 @@ class MessageModel { { $bit: { flag: { xor: flag } } }, { returnDocument: 'after' }, ); - return result.value; + return result.value || null; } static async del(_id: ObjectID) { diff --git a/packages/hydrooj/src/model/oauth.ts b/packages/hydrooj/src/model/oauth.ts index 05bb225a2..525bb8a3f 100644 --- a/packages/hydrooj/src/model/oauth.ts +++ b/packages/hydrooj/src/model/oauth.ts @@ -21,7 +21,7 @@ class OauthModel { { $set: { value } }, { upsert: true, returnDocument: 'after' }, ); - return res.value.uid; + return res.value?.uid; } } diff --git a/packages/hydrooj/src/model/record.ts b/packages/hydrooj/src/model/record.ts index d05327324..092f6b777 100644 --- a/packages/hydrooj/src/model/record.ts +++ b/packages/hydrooj/src/model/record.ts @@ -3,14 +3,15 @@ import { PushOperator, MatchKeysAndValues, OnlyFieldsOfType, FilterQuery, } from 'mongodb'; -import { sum } from 'lodash'; +import { omit, sum } from 'lodash'; import moment from 'moment'; import { STATUS } from './builtin'; import task from './task'; import problem from './problem'; import { - RecordDoc, ContestInfo, ProblemConfigFile, ExternalProblemId, + RecordDoc, ContestInfo, ProblemConfigFile, ExternalProblemId, FileInfo, } from '../interface'; +import { ProblemNotFoundError } from '../error'; import { ArgMethod, buildProjection, Time } from '../utils'; import { MaybeArray } from '../typeutils'; import * as bus from '../service/bus'; @@ -34,7 +35,7 @@ class RecordModel { static async get(_id: ObjectID): Promise static async get(domainId: string, _id: ObjectID): Promise - static async get(arg0, arg1?) { + static async get(arg0: string | ObjectID, arg1?: any) { const _id = arg1 || arg0; const domainId = arg1 ? arg0 : null; const res = await RecordModel.coll.findOne({ _id }); @@ -61,15 +62,16 @@ class RecordModel { static async judge(domainId: string, rid: ObjectID, priority = 0, config: ProblemConfigFile = {}) { const rdoc = await RecordModel.get(domainId, rid); - let data = []; - delete rdoc._id; + if (!rdoc) return null; + let data: FileInfo[] = []; if (rdoc.pid) { const pdoc = await problem.get(rdoc.pdomain, rdoc.pid); + if (!pdoc) throw new ProblemNotFoundError(rdoc?.pdomain, rdoc.pid); data = pdoc.data; - if (typeof pdoc.config === 'string') throw new Error(); // Just for typings. This won't happen. + if (typeof pdoc.config === 'string') throw new Error(pdoc.config); if (pdoc.config.type === 'remote_judge') { return await task.add({ - ...rdoc, + ...omit(rdoc, ['_id']), priority, type: 'remotejudge', subType: pdoc.config.subType, @@ -82,7 +84,7 @@ class RecordModel { } } return await task.add({ - ...rdoc, + ...omit(rdoc, ['_id']), priority, type: 'judge', rid, @@ -162,7 +164,7 @@ class RecordModel { $update, { returnDocument: 'after' }, ); - return res.value; + return res.value || null; } return await RecordModel.get(domainId, _id); } @@ -204,14 +206,13 @@ class RecordModel { static async getList( domainId: string, rids: ObjectID[], showHidden: boolean, fields?: (keyof RecordDoc)[], ): Promise>> { - const r = {}; + const r: Record = {}; rids = Array.from(new Set(rids)); let cursor = RecordModel.coll.find({ domainId, _id: { $in: rids } }); if (fields) cursor = cursor.project(buildProjection(fields)); const rdocs = await cursor.toArray(); for (const rdoc of rdocs) { - if (rdoc.hidden && !showHidden) r[rdoc._id.toHexString()] = null; - else r[rdoc._id.toHexString()] = rdoc; + if (!rdoc.hidden || showHidden) r[rdoc._id.toHexString()] = rdoc; } return r; } diff --git a/packages/hydrooj/src/service/server.ts b/packages/hydrooj/src/service/server.ts index d13dab15b..4d33d0026 100644 --- a/packages/hydrooj/src/service/server.ts +++ b/packages/hydrooj/src/service/server.ts @@ -868,6 +868,7 @@ let started = false; export function start() { if (started) return; const port = system.get('server.port'); + router.all('domain_main_redirect', '/d/:domain', (ctx) => ctx.redirect(`/d/${ctx.params.domain}/`)); app.use(router.routes()).use(router.allowedMethods()); server.listen(argv.options.port || port); logger.success('Server listening at: %d', argv.options.port || port); diff --git a/packages/hydrooj/src/service/storage.ts b/packages/hydrooj/src/service/storage.ts index 4d34fd861..f80c0b02e 100644 --- a/packages/hydrooj/src/service/storage.ts +++ b/packages/hydrooj/src/service/storage.ts @@ -193,7 +193,7 @@ class StorageService { async signDownloadLink(target: string, filename?: string, noExpire = false, useAlternativeEndpointFor?: 'user' | 'judge'): Promise { if (target.includes('..') || target.includes('//')) throw new Error('Invalid path'); try { - const headers = {}; + const headers: Record = {}; if (filename) headers['response-content-disposition'] = `attachment; filename="${encodeRFC5987ValueChars(filename)}"`; if (/\.(jpe?g|png|gif)$/i.test(target)) headers['cache-control'] = 'max-age=604800'; const url = await this.client.presignedGetObject( @@ -209,6 +209,19 @@ class StorageService { throw e; } } + + async signUpload(target: string, size: number) { + const policy = this.client.newPostPolicy(); + policy.setBucket(this.opts.bucket); + policy.setKey(target); + policy.setExpires(new Date(Date.now() + 30 * 60 * 1000)); + if (size) policy.setContentLengthRange(size - 50, size + 50); + const policyResult = await this.client.presignedPostPolicy(policy); + return { + url: this.replaceWithAlternativeUrlFor.user(policyResult.postURL), + extraFormData: policyResult.formData, + }; + } } const service = new StorageService(); diff --git a/packages/sonic/package.json b/packages/sonic/package.json index c9c490645..a39cd340a 100644 --- a/packages/sonic/package.json +++ b/packages/sonic/package.json @@ -1,6 +1,6 @@ { "name": "@hydrooj/sonic", - "version": "1.1.0", + "version": "1.1.1", "description": "Sonic search service", "main": "service.js", "typings": "service.d.ts", diff --git a/packages/sonic/script.ts b/packages/sonic/script.ts index f6a1af6fe..60cc666a1 100644 --- a/packages/sonic/script.ts +++ b/packages/sonic/script.ts @@ -3,12 +3,17 @@ import sonic from './service'; export const description = 'Sonic problem search re-index'; -export async function run({ domainId }) { +export async function run({ domainId }, report) { if (domainId) await sonic.flushb('problem', domainId); else await sonic.flushc('problem'); + let i = 0; const cb = async (pdoc) => { - await sonic.push('problem', `${pdoc.domainId}@title`, pdoc.docId.toString(), pdoc.title); - await sonic.push('problem', `${pdoc.domainId}@content`, pdoc.docId.toString(), pdoc.content.toString()); + i++; + if (!(i % 1000)) report({ message: `${i} problems indexed` }); + await Promise.all([ + sonic.push('problem', `${pdoc.domainId}@title`, pdoc.docId.toString(), pdoc.title), + sonic.push('problem', `${pdoc.domainId}@content`, pdoc.docId.toString(), pdoc.content.toString()), + ]).catch((e) => console.log(`${pdoc.domainId}/${pdoc.docId}`, e)); }; if (domainId) await iterateAllProblemInDomain(domainId, ['title', 'content'], cb); else await iterateAllProblem(['title', 'content'], cb); diff --git a/packages/ui-default/.eslintrc.js b/packages/ui-default/.eslintrc.js index 160ab38b6..66d10a9e7 100644 --- a/packages/ui-default/.eslintrc.js +++ b/packages/ui-default/.eslintrc.js @@ -69,6 +69,7 @@ module.exports = { 'consistent-return': 'warn', 'func-names': 'off', 'import/first': 'off', + 'import/extensions': 'off', 'import/no-extraneous-dependencies': 'off', 'max-classes-per-file': 'off', 'no-empty': ['warn', { allowEmptyCatch: true }], diff --git a/packages/ui-default/build/config/webpack.js b/packages/ui-default/build/config/webpack.js index 33ebab2f9..1eac5ad5b 100644 --- a/packages/ui-default/build/config/webpack.js +++ b/packages/ui-default/build/config/webpack.js @@ -147,7 +147,7 @@ export default function (env = {}) { name: `vditor-${require('vditor/package.json').version}`, }, vendors: { - test: /[\\/]node_modules[\\/]/, + test: /[\\/]node_modules[\\/].+\.([jt]sx?|json|yaml)$/, priority: -10, name: 'vendors', reuseExistingChunk: true, diff --git a/packages/ui-default/components/messagepad/MessagePadDialogueContentContainer.jsx b/packages/ui-default/components/messagepad/MessagePadDialogueContentContainer.jsx index b795ab2f9..d25675c4e 100644 --- a/packages/ui-default/components/messagepad/MessagePadDialogueContentContainer.jsx +++ b/packages/ui-default/components/messagepad/MessagePadDialogueContentContainer.jsx @@ -1,7 +1,7 @@ import React from 'react'; import { connect } from 'react-redux'; import TimeAgo from 'timeago-react'; -import moment from 'dayjs'; +import moment from 'moment'; import 'jquery-scroll-lock'; import 'jquery.easing'; import i18n from 'vj/utils/i18n'; diff --git a/packages/ui-default/components/scratchpad/ScratchpadRecordsRowContainer.jsx b/packages/ui-default/components/scratchpad/ScratchpadRecordsRowContainer.jsx index 4523927dc..5a78e1142 100644 --- a/packages/ui-default/components/scratchpad/ScratchpadRecordsRowContainer.jsx +++ b/packages/ui-default/components/scratchpad/ScratchpadRecordsRowContainer.jsx @@ -2,7 +2,7 @@ import React from 'react'; import classNames from 'classnames'; import { connect } from 'react-redux'; import TimeAgo from 'timeago-react'; -import moment from 'dayjs'; +import moment from 'moment'; import _ from 'lodash'; import substitute from 'vj/utils/substitute'; diff --git a/packages/ui-default/package.json b/packages/ui-default/package.json index 60b04e4e4..82f9696f3 100644 --- a/packages/ui-default/package.json +++ b/packages/ui-default/package.json @@ -1,6 +1,6 @@ { "name": "@hydrooj/ui-default", - "version": "4.16.3", + "version": "4.16.4", "author": "undefined ", "license": "AGPL-3.0", "main": "hydro.js", @@ -42,7 +42,6 @@ "clipboard": "^2.0.8", "copy-webpack-plugin": "^6.4.1", "css-loader": "^4.3.0", - "dayjs": "^1.10.6", "diff-dom": "^4.2.2", "echarts": "^5.1.2", "emojify.js": "^1.1.0", diff --git a/packages/ui-default/templates/layout/html5.html b/packages/ui-default/templates/layout/html5.html index 84a483026..b79182235 100644 --- a/packages/ui-default/templates/layout/html5.html +++ b/packages/ui-default/templates/layout/html5.html @@ -19,7 +19,7 @@ {% block ogp %} - + diff --git a/packages/ui-default/templates/partials/login_dialog.html b/packages/ui-default/templates/partials/login_dialog.html index b1a5cef7c..ba89734d8 100644 --- a/packages/ui-default/templates/partials/login_dialog.html +++ b/packages/ui-default/templates/partials/login_dialog.html @@ -3,7 +3,7 @@