mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
bug fix
This commit is contained in:
1 parent
63bf550311
commit
d9f13feade
17 files changed
+54
-34
No files matched your search
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hydrooj",
|
"name": "hydrooj",
|
||||||
"version": "2.32.13",
|
"version": "2.32.14",
|
||||||
"bin": "bin/hydrooj.js",
|
"bin": "bin/hydrooj.js",
|
||||||
"main": "src/loader",
|
"main": "src/loader",
|
||||||
"module": "src/loader",
|
"module": "src/loader",
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ export async function add(
|
|||||||
};
|
};
|
||||||
await bus.serial('discussion/before-add', payload);
|
await bus.serial('discussion/before-add', payload);
|
||||||
const res = await document.add(
|
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']),
|
null, payload.parentType, payload.parentId, omit(payload, ['domainId', 'content', 'owner', 'parentType', 'parentId']),
|
||||||
);
|
);
|
||||||
payload.docId = res;
|
payload.docId = res;
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ class DomainModel {
|
|||||||
const query: FilterQuery<DomainDoc> = { lower: domainId.toLowerCase() };
|
const query: FilterQuery<DomainDoc> = { lower: domainId.toLowerCase() };
|
||||||
await bus.serial('domain/before-get', query);
|
await bus.serial('domain/before-get', query);
|
||||||
const result = await coll.findOne(query);
|
const result = await coll.findOne(query);
|
||||||
await bus.serial('domain/get', result);
|
if (result) await bus.serial('domain/get', result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ class DomainModel {
|
|||||||
const query: FilterQuery<DomainDoc> = { host };
|
const query: FilterQuery<DomainDoc> = { host };
|
||||||
await bus.serial('domain/before-get', query);
|
await bus.serial('domain/before-get', query);
|
||||||
const result = await coll.findOne(query);
|
const result = await coll.findOne(query);
|
||||||
await bus.serial('domain/get', result);
|
if (result) await bus.serial('domain/get', result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ class DomainModel {
|
|||||||
static async edit(domainId: string, $set: Partial<DomainDoc>) {
|
static async edit(domainId: string, $set: Partial<DomainDoc>) {
|
||||||
await bus.serial('domain/before-update', domainId, $set);
|
await bus.serial('domain/before-update', domainId, $set);
|
||||||
const result = await coll.findOneAndUpdate({ _id: domainId }, { $set }, { returnDocument: 'after' });
|
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;
|
return result.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,12 +96,12 @@ class DomainModel {
|
|||||||
{ $inc: { [field]: n } },
|
{ $inc: { [field]: n } },
|
||||||
{ returnDocument: 'after' },
|
{ returnDocument: 'after' },
|
||||||
);
|
);
|
||||||
return res.value[field];
|
return res.value?.[field];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ArgMethod
|
@ArgMethod
|
||||||
static async getList(domainIds: string[]): Promise<Dictionary<DomainDoc>> {
|
static async getList(domainIds: string[]) {
|
||||||
const r = {};
|
const r: Record<string, DomainDoc | null> = {};
|
||||||
// eslint-disable-next-line no-await-in-loop
|
// eslint-disable-next-line no-await-in-loop
|
||||||
for (const domainId of domainIds) r[domainId] = await DomainModel.get(domainId);
|
for (const domainId of domainIds) r[domainId] = await DomainModel.get(domainId);
|
||||||
return r;
|
return r;
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ class MessageModel {
|
|||||||
{ $bit: { flag: { xor: flag } } },
|
{ $bit: { flag: { xor: flag } } },
|
||||||
{ returnDocument: 'after' },
|
{ returnDocument: 'after' },
|
||||||
);
|
);
|
||||||
return result.value;
|
return result.value || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async del(_id: ObjectID) {
|
static async del(_id: ObjectID) {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class OauthModel {
|
|||||||
{ $set: { value } },
|
{ $set: { value } },
|
||||||
{ upsert: true, returnDocument: 'after' },
|
{ upsert: true, returnDocument: 'after' },
|
||||||
);
|
);
|
||||||
return res.value.uid;
|
return res.value?.uid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,14 +3,15 @@ import {
|
|||||||
PushOperator, MatchKeysAndValues, OnlyFieldsOfType,
|
PushOperator, MatchKeysAndValues, OnlyFieldsOfType,
|
||||||
FilterQuery,
|
FilterQuery,
|
||||||
} from 'mongodb';
|
} from 'mongodb';
|
||||||
import { sum } from 'lodash';
|
import { omit, sum } from 'lodash';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { STATUS } from './builtin';
|
import { STATUS } from './builtin';
|
||||||
import task from './task';
|
import task from './task';
|
||||||
import problem from './problem';
|
import problem from './problem';
|
||||||
import {
|
import {
|
||||||
RecordDoc, ContestInfo, ProblemConfigFile, ExternalProblemId,
|
RecordDoc, ContestInfo, ProblemConfigFile, ExternalProblemId, FileInfo,
|
||||||
} from '../interface';
|
} from '../interface';
|
||||||
|
import { ProblemNotFoundError } from '../error';
|
||||||
import { ArgMethod, buildProjection, Time } from '../utils';
|
import { ArgMethod, buildProjection, Time } from '../utils';
|
||||||
import { MaybeArray } from '../typeutils';
|
import { MaybeArray } from '../typeutils';
|
||||||
import * as bus from '../service/bus';
|
import * as bus from '../service/bus';
|
||||||
@@ -34,7 +35,7 @@ class RecordModel {
|
|||||||
|
|
||||||
static async get(_id: ObjectID): Promise<RecordDoc | null>
|
static async get(_id: ObjectID): Promise<RecordDoc | null>
|
||||||
static async get(domainId: string, _id: ObjectID): Promise<RecordDoc | null>
|
static async get(domainId: string, _id: ObjectID): Promise<RecordDoc | null>
|
||||||
static async get(arg0, arg1?) {
|
static async get(arg0: string | ObjectID, arg1?: any) {
|
||||||
const _id = arg1 || arg0;
|
const _id = arg1 || arg0;
|
||||||
const domainId = arg1 ? arg0 : null;
|
const domainId = arg1 ? arg0 : null;
|
||||||
const res = await RecordModel.coll.findOne({ _id });
|
const res = await RecordModel.coll.findOne({ _id });
|
||||||
@@ -61,15 +62,16 @@ class RecordModel {
|
|||||||
|
|
||||||
static async judge(domainId: string, rid: ObjectID, priority = 0, config: ProblemConfigFile = {}) {
|
static async judge(domainId: string, rid: ObjectID, priority = 0, config: ProblemConfigFile = {}) {
|
||||||
const rdoc = await RecordModel.get(domainId, rid);
|
const rdoc = await RecordModel.get(domainId, rid);
|
||||||
let data = [];
|
if (!rdoc) return null;
|
||||||
delete rdoc._id;
|
let data: FileInfo[] = [];
|
||||||
if (rdoc.pid) {
|
if (rdoc.pid) {
|
||||||
const pdoc = await problem.get(rdoc.pdomain, rdoc.pid);
|
const pdoc = await problem.get(rdoc.pdomain, rdoc.pid);
|
||||||
|
if (!pdoc) throw new ProblemNotFoundError(rdoc?.pdomain, rdoc.pid);
|
||||||
data = pdoc.data;
|
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') {
|
if (pdoc.config.type === 'remote_judge') {
|
||||||
return await task.add({
|
return await task.add({
|
||||||
...rdoc,
|
...omit(rdoc, ['_id']),
|
||||||
priority,
|
priority,
|
||||||
type: 'remotejudge',
|
type: 'remotejudge',
|
||||||
subType: pdoc.config.subType,
|
subType: pdoc.config.subType,
|
||||||
@@ -82,7 +84,7 @@ class RecordModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return await task.add({
|
return await task.add({
|
||||||
...rdoc,
|
...omit(rdoc, ['_id']),
|
||||||
priority,
|
priority,
|
||||||
type: 'judge',
|
type: 'judge',
|
||||||
rid,
|
rid,
|
||||||
@@ -162,7 +164,7 @@ class RecordModel {
|
|||||||
$update,
|
$update,
|
||||||
{ returnDocument: 'after' },
|
{ returnDocument: 'after' },
|
||||||
);
|
);
|
||||||
return res.value;
|
return res.value || null;
|
||||||
}
|
}
|
||||||
return await RecordModel.get(domainId, _id);
|
return await RecordModel.get(domainId, _id);
|
||||||
}
|
}
|
||||||
@@ -204,14 +206,13 @@ class RecordModel {
|
|||||||
static async getList(
|
static async getList(
|
||||||
domainId: string, rids: ObjectID[], showHidden: boolean, fields?: (keyof RecordDoc)[],
|
domainId: string, rids: ObjectID[], showHidden: boolean, fields?: (keyof RecordDoc)[],
|
||||||
): Promise<Record<string, Partial<RecordDoc>>> {
|
): Promise<Record<string, Partial<RecordDoc>>> {
|
||||||
const r = {};
|
const r: Record<string, RecordDoc> = {};
|
||||||
rids = Array.from(new Set(rids));
|
rids = Array.from(new Set(rids));
|
||||||
let cursor = RecordModel.coll.find({ domainId, _id: { $in: rids } });
|
let cursor = RecordModel.coll.find({ domainId, _id: { $in: rids } });
|
||||||
if (fields) cursor = cursor.project(buildProjection(fields));
|
if (fields) cursor = cursor.project(buildProjection(fields));
|
||||||
const rdocs = await cursor.toArray();
|
const rdocs = await cursor.toArray();
|
||||||
for (const rdoc of rdocs) {
|
for (const rdoc of rdocs) {
|
||||||
if (rdoc.hidden && !showHidden) r[rdoc._id.toHexString()] = null;
|
if (!rdoc.hidden || showHidden) r[rdoc._id.toHexString()] = rdoc;
|
||||||
else r[rdoc._id.toHexString()] = rdoc;
|
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -868,6 +868,7 @@ let started = false;
|
|||||||
export function start() {
|
export function start() {
|
||||||
if (started) return;
|
if (started) return;
|
||||||
const port = system.get('server.port');
|
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());
|
app.use(router.routes()).use(router.allowedMethods());
|
||||||
server.listen(argv.options.port || port);
|
server.listen(argv.options.port || port);
|
||||||
logger.success('Server listening at: %d', argv.options.port || port);
|
logger.success('Server listening at: %d', argv.options.port || port);
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ class StorageService {
|
|||||||
async signDownloadLink(target: string, filename?: string, noExpire = false, useAlternativeEndpointFor?: 'user' | 'judge'): Promise<string> {
|
async signDownloadLink(target: string, filename?: string, noExpire = false, useAlternativeEndpointFor?: 'user' | 'judge'): Promise<string> {
|
||||||
if (target.includes('..') || target.includes('//')) throw new Error('Invalid path');
|
if (target.includes('..') || target.includes('//')) throw new Error('Invalid path');
|
||||||
try {
|
try {
|
||||||
const headers = {};
|
const headers: Record<string, string> = {};
|
||||||
if (filename) headers['response-content-disposition'] = `attachment; filename="${encodeRFC5987ValueChars(filename)}"`;
|
if (filename) headers['response-content-disposition'] = `attachment; filename="${encodeRFC5987ValueChars(filename)}"`;
|
||||||
if (/\.(jpe?g|png|gif)$/i.test(target)) headers['cache-control'] = 'max-age=604800';
|
if (/\.(jpe?g|png|gif)$/i.test(target)) headers['cache-control'] = 'max-age=604800';
|
||||||
const url = await this.client.presignedGetObject(
|
const url = await this.client.presignedGetObject(
|
||||||
@@ -209,6 +209,19 @@ class StorageService {
|
|||||||
throw e;
|
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();
|
const service = new StorageService();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@hydrooj/sonic",
|
"name": "@hydrooj/sonic",
|
||||||
"version": "1.1.0",
|
"version": "1.1.1",
|
||||||
"description": "Sonic search service",
|
"description": "Sonic search service",
|
||||||
"main": "service.js",
|
"main": "service.js",
|
||||||
"typings": "service.d.ts",
|
"typings": "service.d.ts",
|
||||||
|
|||||||
@@ -3,12 +3,17 @@ import sonic from './service';
|
|||||||
|
|
||||||
export const description = 'Sonic problem search re-index';
|
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);
|
if (domainId) await sonic.flushb('problem', domainId);
|
||||||
else await sonic.flushc('problem');
|
else await sonic.flushc('problem');
|
||||||
|
let i = 0;
|
||||||
const cb = async (pdoc) => {
|
const cb = async (pdoc) => {
|
||||||
await sonic.push('problem', `${pdoc.domainId}@title`, pdoc.docId.toString(), pdoc.title);
|
i++;
|
||||||
await sonic.push('problem', `${pdoc.domainId}@content`, pdoc.docId.toString(), pdoc.content.toString());
|
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);
|
if (domainId) await iterateAllProblemInDomain(domainId, ['title', 'content'], cb);
|
||||||
else await iterateAllProblem(['title', 'content'], cb);
|
else await iterateAllProblem(['title', 'content'], cb);
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ module.exports = {
|
|||||||
'consistent-return': 'warn',
|
'consistent-return': 'warn',
|
||||||
'func-names': 'off',
|
'func-names': 'off',
|
||||||
'import/first': 'off',
|
'import/first': 'off',
|
||||||
|
'import/extensions': 'off',
|
||||||
'import/no-extraneous-dependencies': 'off',
|
'import/no-extraneous-dependencies': 'off',
|
||||||
'max-classes-per-file': 'off',
|
'max-classes-per-file': 'off',
|
||||||
'no-empty': ['warn', { allowEmptyCatch: true }],
|
'no-empty': ['warn', { allowEmptyCatch: true }],
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ export default function (env = {}) {
|
|||||||
name: `vditor-${require('vditor/package.json').version}`,
|
name: `vditor-${require('vditor/package.json').version}`,
|
||||||
},
|
},
|
||||||
vendors: {
|
vendors: {
|
||||||
test: /[\\/]node_modules[\\/]/,
|
test: /[\\/]node_modules[\\/].+\.([jt]sx?|json|yaml)$/,
|
||||||
priority: -10,
|
priority: -10,
|
||||||
name: 'vendors',
|
name: 'vendors',
|
||||||
reuseExistingChunk: true,
|
reuseExistingChunk: true,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import TimeAgo from 'timeago-react';
|
import TimeAgo from 'timeago-react';
|
||||||
import moment from 'dayjs';
|
import moment from 'moment';
|
||||||
import 'jquery-scroll-lock';
|
import 'jquery-scroll-lock';
|
||||||
import 'jquery.easing';
|
import 'jquery.easing';
|
||||||
import i18n from 'vj/utils/i18n';
|
import i18n from 'vj/utils/i18n';
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import TimeAgo from 'timeago-react';
|
import TimeAgo from 'timeago-react';
|
||||||
import moment from 'dayjs';
|
import moment from 'moment';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
import substitute from 'vj/utils/substitute';
|
import substitute from 'vj/utils/substitute';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@hydrooj/ui-default",
|
"name": "@hydrooj/ui-default",
|
||||||
"version": "4.16.3",
|
"version": "4.16.4",
|
||||||
"author": "undefined <i@undefined.moe>",
|
"author": "undefined <i@undefined.moe>",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"main": "hydro.js",
|
"main": "hydro.js",
|
||||||
@@ -42,7 +42,6 @@
|
|||||||
"clipboard": "^2.0.8",
|
"clipboard": "^2.0.8",
|
||||||
"copy-webpack-plugin": "^6.4.1",
|
"copy-webpack-plugin": "^6.4.1",
|
||||||
"css-loader": "^4.3.0",
|
"css-loader": "^4.3.0",
|
||||||
"dayjs": "^1.10.6",
|
|
||||||
"diff-dom": "^4.2.2",
|
"diff-dom": "^4.2.2",
|
||||||
"echarts": "^5.1.2",
|
"echarts": "^5.1.2",
|
||||||
"emojify.js": "^1.1.0",
|
"emojify.js": "^1.1.0",
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
<link rel="icon" type="image/png" href="{{ static_url('favicon-16x16.png') }}" sizes="16x16">
|
<link rel="icon" type="image/png" href="{{ static_url('favicon-16x16.png') }}" sizes="16x16">
|
||||||
<meta name="theme-color" content="#56758f">
|
<meta name="theme-color" content="#56758f">
|
||||||
{% block ogp %}
|
{% block ogp %}
|
||||||
<meta property="og:site_name" content="{{ handler.domain.ui.name|default(model.system.get('server.name')) }}" />
|
<meta property="og:site_name" content="{{ (handler.domain|default({ui:[]})).ui.name|default(model.system.get('server.name')) }}" />
|
||||||
<meta property="og:title" content="{{ handler.renderTitle(page_name) }}" />
|
<meta property="og:title" content="{{ handler.renderTitle(page_name) }}" />
|
||||||
<meta property="og:url" content="{{ model.system.get('server.url') }}{{ handler.ctx.request.path }}" />
|
<meta property="og:url" content="{{ model.system.get('server.url') }}{{ handler.ctx.request.path }}" />
|
||||||
<meta property="og:image" content="{{ static_url('favicon-96x96.png') }}" />
|
<meta property="og:image" content="{{ static_url('favicon-96x96.png') }}" />
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<div class="dialog--signin__bg">
|
<div class="dialog--signin__bg">
|
||||||
<div class="dialog--signin__side">
|
<div class="dialog--signin__side">
|
||||||
<h1>{{ _("Don't have an account?") }}</h1>
|
<h1>{{ _("Don't have an account?") }}</h1>
|
||||||
<p>{{ _('By signing up a {0} universal account, you can submit code and join discussions in all online judging services provided by us.').format(handler.domain.ui.name|default(model.system.get('server.name'))) }}</p>
|
<p>{{ _('By signing up a {0} universal account, you can submit code and join discussions in all online judging services provided by us.').format((handler.domain|default({ui:[]})).ui.name|default(model.system.get('server.name'))) }}</p>
|
||||||
<div><a href="{{ url('user_register') }}" class="inverse rounded button">{{ _('Sign Up Now') }}</a></div>
|
<div><a href="{{ url('user_register') }}" class="inverse rounded button">{{ _('Sign Up Now') }}</a></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in new issue
Block a user