mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 09:36:08 +08:00
patches from zhengzhou site
This commit is contained in:
19 files changed
+80
-40
No files matched your search
@@ -111,6 +111,22 @@ Hydro 用户群:1085853538
|
|||||||
<details>
|
<details>
|
||||||
<summary><h2>更新日志(点击展开)</h2></summary>
|
<summary><h2>更新日志(点击展开)</h2></summary>
|
||||||
|
|
||||||
|
## Hydro 5.0.0-beta.13 / UI 4.58.0-beta.14
|
||||||
|
|
||||||
|
- ui: 比赛页面添加打印快捷入口
|
||||||
|
- ui: 比赛题目列表显示时空限制和提交按钮
|
||||||
|
- core: 检查提解投票权限
|
||||||
|
- ui: 修复题目配置页文件操作
|
||||||
|
- ui: 移除旧的 window.Hydro.util 和 window.Hydro.component 对象
|
||||||
|
- onsite-toolkit: 添加导入工具
|
||||||
|
- onsite-toolkit: 添加 submit 和 printfile 支持
|
||||||
|
- onsite-toolkit: 支持限制通信功能
|
||||||
|
- core: backup: 速度优化
|
||||||
|
- ui: 优化超长公式显示
|
||||||
|
- core&ui: 添加允许所有人邀请的选项
|
||||||
|
- ui: 优化题目配置页暗色模式
|
||||||
|
- scoreboard-xcpcio: 支持注册为默认榜单
|
||||||
|
|
||||||
## Hydro 5.0.0-beta.12 / UI 4.58.0-beta.12
|
## Hydro 5.0.0-beta.12 / UI 4.58.0-beta.12
|
||||||
|
|
||||||
- framework: 修复 SSE 模式参数识别
|
- framework: 修复 SSE 模式参数识别
|
||||||
@@ -121,7 +137,6 @@ Hydro 用户群:1085853538
|
|||||||
- core: 添加 hydrooj diag 快速诊断工具
|
- core: 添加 hydrooj diag 快速诊断工具
|
||||||
- ui: 禁用链接自动折叠,复制链接自动转义括号
|
- ui: 禁用链接自动折叠,复制链接自动转义括号
|
||||||
- ui: 优化资源缓存
|
- ui: 优化资源缓存
|
||||||
- ui: 优化超长公式显示
|
|
||||||
- core&ui: 支持题目排序
|
- core&ui: 支持题目排序
|
||||||
|
|
||||||
*Breaking API Changes*
|
*Breaking API Changes*
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"repository": "https://github.com/hydro-dev/Hydro",
|
"repository": "https://github.com/hydro-dev/Hydro",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@antfu/eslint-config": "^6.2.0",
|
"@antfu/eslint-config": "^6.2.0",
|
||||||
"@eslint-react/eslint-plugin": "^2.3.5",
|
"@eslint-react/eslint-plugin": "^2.3.7",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.47.0",
|
"@typescript-eslint/eslint-plugin": "^8.47.0",
|
||||||
"@typescript-eslint/parser": "^8.47.0",
|
"@typescript-eslint/parser": "^8.47.0",
|
||||||
"eslint-plugin-de-morgan": "^2.0.0",
|
"eslint-plugin-de-morgan": "^2.0.0",
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ export namespace Time {
|
|||||||
throw new Error('No bson module found');
|
throw new Error('No bson module found');
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
isMoment ||= require('moment');
|
isMoment ||= require('moment').isMoment;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error('No moment module found');
|
throw new Error('No moment module found');
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -35,7 +35,7 @@
|
|||||||
"@types/autocannon": "^7.12.7",
|
"@types/autocannon": "^7.12.7",
|
||||||
"@types/chai": "^5.2.3",
|
"@types/chai": "^5.2.3",
|
||||||
"@types/cross-spawn": "^6.0.6",
|
"@types/cross-spawn": "^6.0.6",
|
||||||
"@types/node": "24.10.0",
|
"@types/node": "24.10.1",
|
||||||
"@types/semver": "^7.7.1",
|
"@types/semver": "^7.7.1",
|
||||||
"autocannon": "^8.0.0",
|
"autocannon": "^8.0.0",
|
||||||
"cac": "^6.7.14",
|
"cac": "^6.7.14",
|
||||||
|
|||||||
@@ -20,8 +20,8 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/diff": "^8.0.0",
|
"@types/diff": "^8.0.0",
|
||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
"@types/lodash": "^4.17.20",
|
"@types/lodash": "^4.17.21",
|
||||||
"sass": "^1.94.1"
|
"sass": "^1.94.2"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "*",
|
"react": "*",
|
||||||
|
|||||||
@@ -8,9 +8,9 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hydrooj/common": "workspace:^",
|
"@hydrooj/common": "workspace:^",
|
||||||
"@hydrooj/utils": "workspace:^",
|
"@hydrooj/utils": "workspace:^",
|
||||||
"@zip.js/zip.js": "^2.8.10",
|
"@zip.js/zip.js": "^2.8.11",
|
||||||
"cac": "^6.7.14",
|
"cac": "^6.7.14",
|
||||||
"p-queue": "^9.0.0",
|
"p-queue": "^9.0.1",
|
||||||
"schemastery": "^3.17.2",
|
"schemastery": "^3.17.2",
|
||||||
"semver": "^7.7.3",
|
"semver": "^7.7.3",
|
||||||
"shell-quote": "^1.8.3",
|
"shell-quote": "^1.8.3",
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
"@mongodb-js/saslprep": "^1.3.2",
|
"@mongodb-js/saslprep": "^1.3.2",
|
||||||
"@simplewebauthn/server": "13.2.2",
|
"@simplewebauthn/server": "13.2.2",
|
||||||
"@undefined-moe/require-resolve-hook": "^1.1.0",
|
"@undefined-moe/require-resolve-hook": "^1.1.0",
|
||||||
"@zip.js/zip.js": "^2.8.10",
|
"@zip.js/zip.js": "^2.8.11",
|
||||||
"adm-zip": "0.5.16",
|
"adm-zip": "0.5.16",
|
||||||
"cac": "^6.7.14",
|
"cac": "^6.7.14",
|
||||||
"cordis": "4.0.0-beta.5",
|
"cordis": "4.0.0-beta.5",
|
||||||
@@ -37,14 +37,14 @@
|
|||||||
"koa-static-cache": "^5.1.4",
|
"koa-static-cache": "^5.1.4",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lru-cache": "^11.2.2",
|
"lru-cache": "^11.2.2",
|
||||||
"mime-types": "^3.0.1",
|
"mime-types": "^3.0.2",
|
||||||
"moment-timezone": "^0.6.0",
|
"moment-timezone": "^0.6.0",
|
||||||
"mongodb": "^7.0.0",
|
"mongodb": "^7.0.0",
|
||||||
"mongodb-uri": "^0.9.7",
|
"mongodb-uri": "^0.9.7",
|
||||||
"nanoid": "^5.1.6",
|
"nanoid": "^5.1.6",
|
||||||
"nodemailer": "^7.0.10",
|
"nodemailer": "^7.0.10",
|
||||||
"notp": "^2.0.3",
|
"notp": "^2.0.3",
|
||||||
"p-queue": "^9.0.0",
|
"p-queue": "^9.0.1",
|
||||||
"sanitize-filename": "^1.6.3",
|
"sanitize-filename": "^1.6.3",
|
||||||
"schemastery": "^3.17.2",
|
"schemastery": "^3.17.2",
|
||||||
"semver": "^7.7.3",
|
"semver": "^7.7.3",
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
"@types/fs-extra": "^11.0.4",
|
"@types/fs-extra": "^11.0.4",
|
||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
"@types/koa-static-cache": "^5.1.4",
|
"@types/koa-static-cache": "^5.1.4",
|
||||||
"@types/lodash": "^4.17.20",
|
"@types/lodash": "^4.17.21",
|
||||||
"@types/mime-types": "^3.0.1",
|
"@types/mime-types": "^3.0.1",
|
||||||
"@types/mongodb-uri": "^0.9.4",
|
"@types/mongodb-uri": "^0.9.4",
|
||||||
"@types/nodemailer": "^7.0.4",
|
"@types/nodemailer": "^7.0.4",
|
||||||
|
|||||||
@@ -333,7 +333,7 @@ export class ContestProblemListHandler extends ContestDetailBaseHandler {
|
|||||||
await this.limitRate('add_discussion', 3600, 60);
|
await this.limitRate('add_discussion', 3600, 60);
|
||||||
await contest.addClarification(domainId, tid, this.user._id, content, this.request.ip, subject);
|
await contest.addClarification(domainId, tid, this.user._id, content, this.request.ip, subject);
|
||||||
if (!this.user.own(this.tdoc)) {
|
if (!this.user.own(this.tdoc)) {
|
||||||
await message.send(1, this.tdoc.maintainer.concat(this.tdoc.owner), JSON.stringify({
|
await message.send(1, (this.tdoc.maintainer || []).concat(this.tdoc.owner), JSON.stringify({
|
||||||
message: 'Contest {0} has a new clarification about {1}, please go to contest clarifications page to reply.',
|
message: 'Contest {0} has a new clarification about {1}, please go to contest clarifications page to reply.',
|
||||||
params: [this.tdoc.title, subject > 0 ? `#${this.tdoc.pids.indexOf(subject) + 1}` : 'the contest'],
|
params: [this.tdoc.title, subject > 0 ? `#${this.tdoc.pids.indexOf(subject) + 1}` : 'the contest'],
|
||||||
url: this.url('contest_clarification', { tid }),
|
url: this.url('contest_clarification', { tid }),
|
||||||
|
|||||||
@@ -333,8 +333,8 @@ class RecordMainConnectionHandler extends ConnectionHandler {
|
|||||||
if (!this.all) {
|
if (!this.all) {
|
||||||
if (rdoc.contest && ![this.tid, '000000000000000000000000'].includes(rdoc.contest.toString())) return;
|
if (rdoc.contest && ![this.tid, '000000000000000000000000'].includes(rdoc.contest.toString())) return;
|
||||||
if (this.tid && rdoc.contest?.toString() !== '0'.repeat(24)) {
|
if (this.tid && rdoc.contest?.toString() !== '0'.repeat(24)) {
|
||||||
if (contest.isLocked(this.tdoc) && !this.pretest) return;
|
if (rdoc.uid !== this.user._id && !contest.canShowRecord.call(this, this.tdoc, true)) return;
|
||||||
if (!contest.canShowSelfRecord.call(this, this.tdoc, true)) return;
|
if (rdoc.uid === this.user._id && !contest.canShowSelfRecord.call(this, this.tdoc, true)) return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -345,7 +345,7 @@ class RecordMainConnectionHandler extends ConnectionHandler {
|
|||||||
user.getById(this.args.domainId, rdoc.uid),
|
user.getById(this.args.domainId, rdoc.uid),
|
||||||
problem.get(rdoc.domainId, rdoc.pid),
|
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 : null;
|
||||||
if (pdoc && !rdoc.contest) {
|
if (pdoc && !rdoc.contest) {
|
||||||
if (!problem.canViewBy(pdoc, this.user)) pdoc = null;
|
if (!problem.canViewBy(pdoc, this.user)) pdoc = null;
|
||||||
if (!this.user.hasPerm(PERM.PERM_VIEW_PROBLEM)) pdoc = null;
|
if (!this.user.hasPerm(PERM.PERM_VIEW_PROBLEM)) pdoc = null;
|
||||||
|
|||||||
@@ -213,6 +213,7 @@ export type RecordDoc = {
|
|||||||
[K in keyof RecordPayload]: K extends 'hackTarget' | 'contest' ? ObjectId : RecordPayload[K];
|
[K in keyof RecordPayload]: K extends 'hackTarget' | 'contest' ? ObjectId : RecordPayload[K];
|
||||||
} & {
|
} & {
|
||||||
_id: ObjectId;
|
_id: ObjectId;
|
||||||
|
notify?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface RecordHistoryDoc extends RecordJudgeInfo {
|
export interface RecordHistoryDoc extends RecordJudgeInfo {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
Filter, FindOptions, MatchKeysAndValues,
|
Filter, FindOptions, MatchKeysAndValues,
|
||||||
ObjectId, OnlyFieldsOfType, PushOperator, UpdateFilter,
|
ObjectId, OnlyFieldsOfType, PushOperator, UpdateFilter,
|
||||||
} from 'mongodb';
|
} from 'mongodb';
|
||||||
import { ProblemConfigFile } from '@hydrooj/common';
|
import { ProblemConfigFile, STATUS_TEXTS } from '@hydrooj/common';
|
||||||
import { Context } from '../context';
|
import { Context } from '../context';
|
||||||
import { ProblemNotFoundError } from '../error';
|
import { ProblemNotFoundError } from '../error';
|
||||||
import { JudgeMeta, RecordDoc } from '../interface';
|
import { JudgeMeta, RecordDoc } from '../interface';
|
||||||
@@ -13,6 +13,7 @@ import { MaybeArray, NumberKeys } from '../typeutils';
|
|||||||
import { ArgMethod, buildProjection, Time } from '../utils';
|
import { ArgMethod, buildProjection, Time } from '../utils';
|
||||||
import { STATUS } from './builtin';
|
import { STATUS } from './builtin';
|
||||||
import DomainModel from './domain';
|
import DomainModel from './domain';
|
||||||
|
import MessageModel from './message';
|
||||||
import problem from './problem';
|
import problem from './problem';
|
||||||
import SystemModel from './system';
|
import SystemModel from './system';
|
||||||
import task from './task';
|
import task from './task';
|
||||||
@@ -137,6 +138,7 @@ export default class RecordModel {
|
|||||||
files?: Record<string, string>;
|
files?: Record<string, string>;
|
||||||
hackTarget?: ObjectId;
|
hackTarget?: ObjectId;
|
||||||
type: 'judge' | 'rejudge' | 'pretest' | 'hack' | 'generate';
|
type: 'judge' | 'rejudge' | 'pretest' | 'hack' | 'generate';
|
||||||
|
notify?: boolean;
|
||||||
} = { type: 'judge' },
|
} = { type: 'judge' },
|
||||||
) {
|
) {
|
||||||
const data: RecordDoc = {
|
const data: RecordDoc = {
|
||||||
@@ -161,6 +163,7 @@ export default class RecordModel {
|
|||||||
if (args.contest) data.contest = args.contest;
|
if (args.contest) data.contest = args.contest;
|
||||||
if (args.files) data.files = args.files;
|
if (args.files) data.files = args.files;
|
||||||
if (args.hackTarget) data.hackTarget = args.hackTarget;
|
if (args.hackTarget) data.hackTarget = args.hackTarget;
|
||||||
|
if (args.notify) data.notify = true;
|
||||||
if (args.type === 'rejudge') {
|
if (args.type === 'rejudge') {
|
||||||
args.type = 'judge';
|
args.type = 'judge';
|
||||||
data.rejudged = true;
|
data.rejudged = true;
|
||||||
@@ -290,6 +293,14 @@ export async function apply(ctx: Context) {
|
|||||||
]));
|
]));
|
||||||
ctx.on('domain/delete', (domainId) => RecordModel.coll.deleteMany({ domainId }));
|
ctx.on('domain/delete', (domainId) => RecordModel.coll.deleteMany({ domainId }));
|
||||||
ctx.on('record/judge', async (rdoc, updated) => {
|
ctx.on('record/judge', async (rdoc, updated) => {
|
||||||
|
if (rdoc.contest?.toHexString().startsWith('0'.repeat(23))) return;
|
||||||
|
if (rdoc.notify) {
|
||||||
|
const pdoc = await Hydro.model.problem.get(rdoc.domainId, rdoc.pid);
|
||||||
|
await MessageModel.send(1, rdoc.uid, JSON.stringify({
|
||||||
|
message: 'Judge Result\n{0}: {1}',
|
||||||
|
params: [pdoc.title, STATUS_TEXTS[rdoc.status]],
|
||||||
|
}), MessageModel.FLAG_I18N);
|
||||||
|
}
|
||||||
if (rdoc.status === STATUS.STATUS_ACCEPTED && updated) {
|
if (rdoc.status === STATUS.STATUS_ACCEPTED && updated) {
|
||||||
if (SystemModel.get('record.statMode') === 'unique') {
|
if (SystemModel.get('record.statMode') === 'unique') {
|
||||||
await RecordModel.collStat.deleteMany({
|
await RecordModel.collStat.deleteMany({
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ export class User {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getFields(type: 'public' | 'private' = 'public') {
|
getFields(type: 'public' | 'private' = 'public') {
|
||||||
const fields = ['_id', 'uname', 'mail', 'perm', 'role', 'priv', 'regat', 'loginat', 'tfa', 'authn'].concat(this._publicFields);
|
const fields = ['_id', 'uname', 'mail', 'perm', 'role', 'priv', 'regat', 'loginat', 'tfa', 'authn', 'avatar'].concat(this._publicFields);
|
||||||
return type === 'public' ? fields : fields.concat(this._privateFields);
|
return type === 'public' ? fields : fields.concat(this._privateFields);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ export function apply(ctx: Context, config: ReturnType<typeof Config>) {
|
|||||||
type, id: data.id, data, token: `t${token++}`,
|
type, id: data.id, data, token: `t${token++}`,
|
||||||
});
|
});
|
||||||
const [pdict, tsdocs] = await Promise.all([
|
const [pdict, tsdocs] = await Promise.all([
|
||||||
ProblemModel.getList(tdoc.domainId, tdoc.pids, true, false, ProblemModel.PROJECTION_LIST, true),
|
ProblemModel.getList(tdoc.domainId, tdoc.pids, true, false, ProblemModel.PROJECTION_LIST.concat('config'), true),
|
||||||
ContestModel.getMultiStatus(tdoc.domainId, { docId: tdoc._id }).toArray(),
|
ContestModel.getMultiStatus(tdoc.domainId, { docId: tdoc._id }).toArray(),
|
||||||
]);
|
]);
|
||||||
const udict = await UserModel.getList(tdoc.domainId, tsdocs.map((i) => i.uid));
|
const udict = await UserModel.getList(tdoc.domainId, tsdocs.map((i) => i.uid));
|
||||||
@@ -326,15 +326,18 @@ export function apply(ctx: Context, config: ReturnType<typeof Config>) {
|
|||||||
const tids = tidsInput.split(',').map((i) => i.trim()).filter((i) => i).map((i) => new ObjectId(i));
|
const tids = tidsInput.split(',').map((i) => i.trim()).filter((i) => i).map((i) => new ObjectId(i));
|
||||||
const tdocs = await Promise.all(tids.map((i) => ContestModel.get('system', i)));
|
const tdocs = await Promise.all(tids.map((i) => ContestModel.get('system', i)));
|
||||||
const convertUname = Types.Username[0];
|
const convertUname = Types.Username[0];
|
||||||
|
let cnt = 0;
|
||||||
for (const line of data) {
|
for (const line of data) {
|
||||||
|
cnt++;
|
||||||
|
const id = line.id || cnt;
|
||||||
const uname = convertUname(line.name);
|
const uname = convertUname(line.name);
|
||||||
const email = `${line.id}@onsite.local`;
|
const email = `${id}@onsite.local`;
|
||||||
let team = await UserModel.getByEmail('system', email);
|
let team = await UserModel.getByEmail('system', email);
|
||||||
if (!team) {
|
if (!team) {
|
||||||
const uid = await UserModel.create(email, uname, line.password || randomstring());
|
const uid = await UserModel.create(email, uname, line.password || randomstring());
|
||||||
team = await UserModel.getById('system', uid);
|
team = await UserModel.getById('system', uid);
|
||||||
} else {
|
} else {
|
||||||
await UserModel.setById(team._id, { uname });
|
await UserModel.setUname(team._id, uname);
|
||||||
if (line.password) await UserModel.setPassword(team._id, line.password);
|
if (line.password) await UserModel.setPassword(team._id, line.password);
|
||||||
}
|
}
|
||||||
if (line.member1) line.members.push(line.member1);
|
if (line.member1) line.members.push(line.member1);
|
||||||
@@ -346,7 +349,7 @@ export function apply(ctx: Context, config: ReturnType<typeof Config>) {
|
|||||||
if (Object.keys(set).length) await UserModel.setById(team._id, set);
|
if (Object.keys(set).length) await UserModel.setById(team._id, set);
|
||||||
for (const tdoc of tdocs) {
|
for (const tdoc of tdocs) {
|
||||||
const tsdoc = await ContestModel.getStatus('system', tdoc.docId, team._id);
|
const tsdoc = await ContestModel.getStatus('system', tdoc.docId, team._id);
|
||||||
if (!tsdoc?.attend) await ContestModel.attend('system', tdoc.docId, team._id, 'rank' in line ? { unrank: !line.rank } : {});
|
if (!tsdoc?.attend) await ContestModel.attend('system', tdoc.docId, team._id, 'rank' in line ? { unrank: !line.rank, subscribe: 1 } : { subscribe: 1 });
|
||||||
else if ('rank' in line && tsdoc.unrank === line.rank) await ContestModel.setStatus('system', tdoc.docId, team._id, { unrank: !line.rank });
|
else if ('rank' in line && tsdoc.unrank === line.rank) await ContestModel.setStatus('system', tdoc.docId, team._id, { unrank: !line.rank });
|
||||||
}
|
}
|
||||||
if (line.ip) await coll.updateOne({ _id: normalizeIp(line.ip) }, { $set: { uid: team._id } }, { upsert: true });
|
if (line.ip) await coll.updateOne({ _id: normalizeIp(line.ip) }, { $set: { uid: team._id } }, { upsert: true });
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ Language ${SettingModel.langs[lang].display} (${lang})
|
|||||||
|
|
||||||
const rid = await RecordModel.add(
|
const rid = await RecordModel.add(
|
||||||
domainId, this.pdoc.docId, this.user._id, lang, code, true,
|
domainId, this.pdoc.docId, this.user._id, lang, code, true,
|
||||||
{ contest: this.tdoc.docId, files, type: 'judge' },
|
{ contest: this.tdoc.docId, files, type: 'judge', notify: true },
|
||||||
);
|
);
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
ProblemModel.inc(domainId, this.pdoc.docId, 'nSubmit', 1),
|
ProblemModel.inc(domainId, this.pdoc.docId, 'nSubmit', 1),
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ export const Config = Schema.object({
|
|||||||
cacheTTL: Schema.number().default(0).description('Cache TTL in milliseconds'),
|
cacheTTL: Schema.number().default(0).description('Cache TTL in milliseconds'),
|
||||||
cacheSize: Schema.number().default(100).description('Cache size'),
|
cacheSize: Schema.number().default(100).description('Cache size'),
|
||||||
asDefault: Schema.boolean().default(false).description('As default scoreboard'),
|
asDefault: Schema.boolean().default(false).description('As default scoreboard'),
|
||||||
|
override: Schema.any().default({}).description('Scoreboard contest override'),
|
||||||
});
|
});
|
||||||
|
|
||||||
export async function apply(ctx: Context, config: ReturnType<typeof Config>) {
|
export async function apply(ctx: Context, config: ReturnType<typeof Config>) {
|
||||||
@@ -200,6 +201,7 @@ export async function apply(ctx: Context, config: ReturnType<typeof Config>) {
|
|||||||
options: {
|
options: {
|
||||||
submission_timestamp_unit: 'millisecond',
|
submission_timestamp_unit: 'millisecond',
|
||||||
},
|
},
|
||||||
|
...(typeof config.override === 'object' ? config.override || {} : {}),
|
||||||
},
|
},
|
||||||
...state,
|
...state,
|
||||||
};
|
};
|
||||||
@@ -216,6 +218,7 @@ export async function apply(ctx: Context, config: ReturnType<typeof Config>) {
|
|||||||
js: indexJs,
|
js: indexJs,
|
||||||
css: indexCss,
|
css: indexCss,
|
||||||
realtime,
|
realtime,
|
||||||
|
tdoc: this.tdoc,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ declare module 'hydrooj' {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Media(md: MarkdownIt) {
|
export function Media(md: MarkdownIt, { pdfToolbar = false }: { pdfToolbar?: boolean } = {}) {
|
||||||
const supported = ['youtube', 'vimeo', 'vine', 'prezi', 'bilibili', 'youku', 'msoffice'];
|
const supported = ['youtube', 'vimeo', 'vine', 'prezi', 'bilibili', 'youku', 'msoffice'];
|
||||||
md.renderer.rules.video = function tokenizeReturn(tokens, idx) {
|
md.renderer.rules.video = function tokenizeReturn(tokens, idx) {
|
||||||
let src = md.utils.escapeHtml(tokens[idx].attrGet('src'));
|
let src = md.utils.escapeHtml(tokens[idx].attrGet('src'));
|
||||||
@@ -109,7 +109,7 @@ export function Media(md: MarkdownIt) {
|
|||||||
return `\
|
return `\
|
||||||
<object classid="clsid:${uuid().toUpperCase()}">
|
<object classid="clsid:${uuid().toUpperCase()}">
|
||||||
<param name="SRC" value="${src}">
|
<param name="SRC" value="${src}">
|
||||||
<embed type="application/pdf" width="100%" style="min-height:100vh;border:none;" fullscreen="yes" src="${src}#toolbar=0&navpanes=0">
|
<embed type="application/pdf" width="100%" style="min-height:100vh;border:none;" fullscreen="yes" src="${src}#toolbar=${pdfToolbar ? '0' : '1'}&navpanes=0&view=FitH">
|
||||||
<noembed></noembed>
|
<noembed></noembed>
|
||||||
</embed>
|
</embed>
|
||||||
</object>`;
|
</object>`;
|
||||||
|
|||||||
@@ -20,11 +20,11 @@
|
|||||||
"@fontsource/source-code-pro": "^5.2.7",
|
"@fontsource/source-code-pro": "^5.2.7",
|
||||||
"@fontsource/ubuntu-mono": "^5.2.8",
|
"@fontsource/ubuntu-mono": "^5.2.8",
|
||||||
"@hydrooj/utils": "workspace:^",
|
"@hydrooj/utils": "workspace:^",
|
||||||
"@mantine/core": "^8.3.8",
|
"@mantine/core": "^8.3.9",
|
||||||
"@mantine/hooks": "^8.3.8",
|
"@mantine/hooks": "^8.3.9",
|
||||||
"@mantine/notifications": "^8.3.8",
|
"@mantine/notifications": "^8.3.9",
|
||||||
"@sentry/browser": "^10.25.0",
|
"@sentry/browser": "^10.26.0",
|
||||||
"@sentry/webpack-plugin": "^4.6.0",
|
"@sentry/webpack-plugin": "^4.6.1",
|
||||||
"@simplewebauthn/browser": "13.2.2",
|
"@simplewebauthn/browser": "13.2.2",
|
||||||
"@svgr/webpack": "^8.1.0",
|
"@svgr/webpack": "^8.1.0",
|
||||||
"@types/html-to-text": "^9.0.4",
|
"@types/html-to-text": "^9.0.4",
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
"@types/redux-logger": "^3.0.13",
|
"@types/redux-logger": "^3.0.13",
|
||||||
"@types/uuid": "^11.0.0",
|
"@types/uuid": "^11.0.0",
|
||||||
"@types/webpack-env": "^1.18.8",
|
"@types/webpack-env": "^1.18.8",
|
||||||
"@vscode/codicons": "^0.0.42",
|
"@vscode/codicons": "^0.0.43",
|
||||||
"ajv": "^8.17.1",
|
"ajv": "^8.17.1",
|
||||||
"allotment": "^1.20.4",
|
"allotment": "^1.20.4",
|
||||||
"autoprefixer": "^10.4.22",
|
"autoprefixer": "^10.4.22",
|
||||||
@@ -52,8 +52,8 @@
|
|||||||
"clipboard": "^2.0.11",
|
"clipboard": "^2.0.11",
|
||||||
"copy-webpack-plugin": "^13.0.1",
|
"copy-webpack-plugin": "^13.0.1",
|
||||||
"cordis": "4.0.0-beta.5",
|
"cordis": "4.0.0-beta.5",
|
||||||
"core-js": "^3.46.0",
|
"core-js": "^3.47.0",
|
||||||
"core-js-compat": "^3.46.0",
|
"core-js-compat": "^3.47.0",
|
||||||
"css-loader": "^7.1.2",
|
"css-loader": "^7.1.2",
|
||||||
"diff": "^8.0.2",
|
"diff": "^8.0.2",
|
||||||
"diff-dom": "^5.2.1",
|
"diff-dom": "^5.2.1",
|
||||||
@@ -67,9 +67,9 @@
|
|||||||
"jquery-scroll-lock": "^3.1.3",
|
"jquery-scroll-lock": "^3.1.3",
|
||||||
"jquery.easing": "^1.4.1",
|
"jquery.easing": "^1.4.1",
|
||||||
"jquery.transit": "^0.9.12",
|
"jquery.transit": "^0.9.12",
|
||||||
"mantine-contextmenu": "^8.3.8",
|
"mantine-contextmenu": "^8.3.9",
|
||||||
"matchmedia-polyfill": "^0.3.2",
|
"matchmedia-polyfill": "^0.3.2",
|
||||||
"md-editor-rt": "^6.1.1",
|
"md-editor-rt": "^6.2.0",
|
||||||
"mini-css-extract-plugin": "^2.9.4",
|
"mini-css-extract-plugin": "^2.9.4",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"monaco-editor": "0.52.2",
|
"monaco-editor": "0.52.2",
|
||||||
@@ -80,7 +80,7 @@
|
|||||||
"nanoid": "^5.1.6",
|
"nanoid": "^5.1.6",
|
||||||
"normalize.css": "^8.0.1",
|
"normalize.css": "^8.0.1",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"p-queue": "^9.0.0",
|
"p-queue": "^9.0.1",
|
||||||
"pickadate": "^3.6.4",
|
"pickadate": "^3.6.4",
|
||||||
"postcss-loader": "7.3.4",
|
"postcss-loader": "7.3.4",
|
||||||
"postcss-preset-mantine": "^1.18.0",
|
"postcss-preset-mantine": "^1.18.0",
|
||||||
@@ -101,7 +101,7 @@
|
|||||||
"redux-promise-middleware": "^6.2.0",
|
"redux-promise-middleware": "^6.2.0",
|
||||||
"redux-thunk": "^3.1.0",
|
"redux-thunk": "^3.1.0",
|
||||||
"rupture": "^0.7.1",
|
"rupture": "^0.7.1",
|
||||||
"sass": "^1.94.1",
|
"sass": "^1.94.2",
|
||||||
"sass-loader": "^16.0.6",
|
"sass-loader": "^16.0.6",
|
||||||
"schemastery": "^3.17.2",
|
"schemastery": "^3.17.2",
|
||||||
"shorty.js": "^1.0.1",
|
"shorty.js": "^1.0.1",
|
||||||
@@ -117,8 +117,8 @@
|
|||||||
"timeago.js": "^4.0.2",
|
"timeago.js": "^4.0.2",
|
||||||
"ts-loader": "^9.5.4",
|
"ts-loader": "^9.5.4",
|
||||||
"web-streams-polyfill": "^4.2.0",
|
"web-streams-polyfill": "^4.2.0",
|
||||||
"webpack": "^5.102.1",
|
"webpack": "^5.103.0",
|
||||||
"webpack-bundle-analyzer": "^4.10.2",
|
"webpack-bundle-analyzer": "^5.0.1",
|
||||||
"webpack-dev-server": "^5.2.2",
|
"webpack-dev-server": "^5.2.2",
|
||||||
"webpack-manifest-plugin": "^6.0.1",
|
"webpack-manifest-plugin": "^6.0.1",
|
||||||
"webpackbar": "^7.0.0"
|
"webpackbar": "^7.0.0"
|
||||||
|
|||||||
@@ -103,9 +103,16 @@
|
|||||||
<span class="icon icon-comment--text"></span> {{ _('Discussion') }}
|
<span class="icon icon-comment--text"></span> {{ _('Discussion') }}
|
||||||
</a></li>
|
</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if handler.user.own(tdoc) or handler.user.hasPriv(PRIV.PRIV_READ_RECORD_CODE) or handler.user.hasPerm(perm.PERM_READ_RECORD_CODE) %}
|
||||||
<li class="menu__item"><a class="menu__link" href="{{ url('record_main', query={tid:tdoc.docId}) }}">
|
<li class="menu__item"><a class="menu__link" href="{{ url('record_main', query={tid:tdoc.docId}) }}">
|
||||||
<span class="icon icon-flag"></span> {{ _('All Submissions') }}
|
<span class="icon icon-flag"></span> {{ _('All Submissions') }}
|
||||||
</a></li>
|
</a></li>
|
||||||
|
{% endif %}
|
||||||
|
{% if tsdoc.attend and model.contest.canShowSelfRecord.call(handler, tdoc) %}
|
||||||
|
<li class="menu__item"><a class="menu__link" href="{{ url('record_main', query={tid:tdoc.docId, uidOrName:handler.user._id}) }}">
|
||||||
|
<span class="icon icon-flag"></span> {{ _('My Submissions') }}
|
||||||
|
</a></li>
|
||||||
|
{% endif %}
|
||||||
<li class="menu__item"><a class="menu__link" href="{{ url('wiki_help', anchor='contest') }}">
|
<li class="menu__item"><a class="menu__link" href="{{ url('wiki_help', anchor='contest') }}">
|
||||||
<span class="icon icon-help"></span> {{ _('Help') }}
|
<span class="icon icon-help"></span> {{ _('Help') }}
|
||||||
</a></li>
|
</a></li>
|
||||||
|
|||||||
@@ -16,6 +16,6 @@
|
|||||||
"@types/react": "^19.2.6",
|
"@types/react": "^19.2.6",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
"@vitejs/plugin-react-swc": "^4.2.2",
|
"@vitejs/plugin-react-swc": "^4.2.2",
|
||||||
"vite": "^7.2.2"
|
"vite": "^7.2.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user