mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 17:36:08 +08:00
patches from zhengzhou site
This commit is contained in:
19 files changed
+80
-40
No files matched your search
@@ -20,8 +20,8 @@
|
||||
"devDependencies": {
|
||||
"@types/diff": "^8.0.0",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/lodash": "^4.17.20",
|
||||
"sass": "^1.94.1"
|
||||
"@types/lodash": "^4.17.21",
|
||||
"sass": "^1.94.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
"dependencies": {
|
||||
"@hydrooj/common": "workspace:^",
|
||||
"@hydrooj/utils": "workspace:^",
|
||||
"@zip.js/zip.js": "^2.8.10",
|
||||
"@zip.js/zip.js": "^2.8.11",
|
||||
"cac": "^6.7.14",
|
||||
"p-queue": "^9.0.0",
|
||||
"p-queue": "^9.0.1",
|
||||
"schemastery": "^3.17.2",
|
||||
"semver": "^7.7.3",
|
||||
"shell-quote": "^1.8.3",
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"@mongodb-js/saslprep": "^1.3.2",
|
||||
"@simplewebauthn/server": "13.2.2",
|
||||
"@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",
|
||||
"cac": "^6.7.14",
|
||||
"cordis": "4.0.0-beta.5",
|
||||
@@ -37,14 +37,14 @@
|
||||
"koa-static-cache": "^5.1.4",
|
||||
"lodash": "^4.17.21",
|
||||
"lru-cache": "^11.2.2",
|
||||
"mime-types": "^3.0.1",
|
||||
"mime-types": "^3.0.2",
|
||||
"moment-timezone": "^0.6.0",
|
||||
"mongodb": "^7.0.0",
|
||||
"mongodb-uri": "^0.9.7",
|
||||
"nanoid": "^5.1.6",
|
||||
"nodemailer": "^7.0.10",
|
||||
"notp": "^2.0.3",
|
||||
"p-queue": "^9.0.0",
|
||||
"p-queue": "^9.0.1",
|
||||
"sanitize-filename": "^1.6.3",
|
||||
"schemastery": "^3.17.2",
|
||||
"semver": "^7.7.3",
|
||||
@@ -60,7 +60,7 @@
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/koa-static-cache": "^5.1.4",
|
||||
"@types/lodash": "^4.17.20",
|
||||
"@types/lodash": "^4.17.21",
|
||||
"@types/mime-types": "^3.0.1",
|
||||
"@types/mongodb-uri": "^0.9.4",
|
||||
"@types/nodemailer": "^7.0.4",
|
||||
|
||||
@@ -333,7 +333,7 @@ export class ContestProblemListHandler extends ContestDetailBaseHandler {
|
||||
await this.limitRate('add_discussion', 3600, 60);
|
||||
await contest.addClarification(domainId, tid, this.user._id, content, this.request.ip, subject);
|
||||
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.',
|
||||
params: [this.tdoc.title, subject > 0 ? `#${this.tdoc.pids.indexOf(subject) + 1}` : 'the contest'],
|
||||
url: this.url('contest_clarification', { tid }),
|
||||
|
||||
@@ -333,8 +333,8 @@ class RecordMainConnectionHandler extends ConnectionHandler {
|
||||
if (!this.all) {
|
||||
if (rdoc.contest && ![this.tid, '000000000000000000000000'].includes(rdoc.contest.toString())) return;
|
||||
if (this.tid && rdoc.contest?.toString() !== '0'.repeat(24)) {
|
||||
if (contest.isLocked(this.tdoc) && !this.pretest) return;
|
||||
if (!contest.canShowSelfRecord.call(this, this.tdoc, true)) return;
|
||||
if (rdoc.uid !== this.user._id && !contest.canShowRecord.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),
|
||||
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 (!problem.canViewBy(pdoc, this.user)) 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];
|
||||
} & {
|
||||
_id: ObjectId;
|
||||
notify?: boolean;
|
||||
};
|
||||
|
||||
export interface RecordHistoryDoc extends RecordJudgeInfo {
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
Filter, FindOptions, MatchKeysAndValues,
|
||||
ObjectId, OnlyFieldsOfType, PushOperator, UpdateFilter,
|
||||
} from 'mongodb';
|
||||
import { ProblemConfigFile } from '@hydrooj/common';
|
||||
import { ProblemConfigFile, STATUS_TEXTS } from '@hydrooj/common';
|
||||
import { Context } from '../context';
|
||||
import { ProblemNotFoundError } from '../error';
|
||||
import { JudgeMeta, RecordDoc } from '../interface';
|
||||
@@ -13,6 +13,7 @@ import { MaybeArray, NumberKeys } from '../typeutils';
|
||||
import { ArgMethod, buildProjection, Time } from '../utils';
|
||||
import { STATUS } from './builtin';
|
||||
import DomainModel from './domain';
|
||||
import MessageModel from './message';
|
||||
import problem from './problem';
|
||||
import SystemModel from './system';
|
||||
import task from './task';
|
||||
@@ -137,6 +138,7 @@ export default class RecordModel {
|
||||
files?: Record<string, string>;
|
||||
hackTarget?: ObjectId;
|
||||
type: 'judge' | 'rejudge' | 'pretest' | 'hack' | 'generate';
|
||||
notify?: boolean;
|
||||
} = { type: 'judge' },
|
||||
) {
|
||||
const data: RecordDoc = {
|
||||
@@ -161,6 +163,7 @@ export default class RecordModel {
|
||||
if (args.contest) data.contest = args.contest;
|
||||
if (args.files) data.files = args.files;
|
||||
if (args.hackTarget) data.hackTarget = args.hackTarget;
|
||||
if (args.notify) data.notify = true;
|
||||
if (args.type === 'rejudge') {
|
||||
args.type = 'judge';
|
||||
data.rejudged = true;
|
||||
@@ -290,6 +293,14 @@ export async function apply(ctx: Context) {
|
||||
]));
|
||||
ctx.on('domain/delete', (domainId) => RecordModel.coll.deleteMany({ domainId }));
|
||||
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 (SystemModel.get('record.statMode') === 'unique') {
|
||||
await RecordModel.collStat.deleteMany({
|
||||
|
||||
@@ -163,7 +163,7 @@ export class User {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ export function apply(ctx: Context, config: ReturnType<typeof Config>) {
|
||||
type, id: data.id, data, token: `t${token++}`,
|
||||
});
|
||||
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(),
|
||||
]);
|
||||
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 tdocs = await Promise.all(tids.map((i) => ContestModel.get('system', i)));
|
||||
const convertUname = Types.Username[0];
|
||||
let cnt = 0;
|
||||
for (const line of data) {
|
||||
cnt++;
|
||||
const id = line.id || cnt;
|
||||
const uname = convertUname(line.name);
|
||||
const email = `${line.id}@onsite.local`;
|
||||
const email = `${id}@onsite.local`;
|
||||
let team = await UserModel.getByEmail('system', email);
|
||||
if (!team) {
|
||||
const uid = await UserModel.create(email, uname, line.password || randomstring());
|
||||
team = await UserModel.getById('system', uid);
|
||||
} 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.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);
|
||||
for (const tdoc of tdocs) {
|
||||
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 });
|
||||
}
|
||||
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(
|
||||
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([
|
||||
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'),
|
||||
cacheSize: Schema.number().default(100).description('Cache size'),
|
||||
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>) {
|
||||
@@ -200,6 +201,7 @@ export async function apply(ctx: Context, config: ReturnType<typeof Config>) {
|
||||
options: {
|
||||
submission_timestamp_unit: 'millisecond',
|
||||
},
|
||||
...(typeof config.override === 'object' ? config.override || {} : {}),
|
||||
},
|
||||
...state,
|
||||
};
|
||||
@@ -216,6 +218,7 @@ export async function apply(ctx: Context, config: ReturnType<typeof Config>) {
|
||||
js: indexJs,
|
||||
css: indexCss,
|
||||
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'];
|
||||
md.renderer.rules.video = function tokenizeReturn(tokens, idx) {
|
||||
let src = md.utils.escapeHtml(tokens[idx].attrGet('src'));
|
||||
@@ -109,7 +109,7 @@ export function Media(md: MarkdownIt) {
|
||||
return `\
|
||||
<object classid="clsid:${uuid().toUpperCase()}">
|
||||
<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>
|
||||
</embed>
|
||||
</object>`;
|
||||
|
||||
@@ -20,11 +20,11 @@
|
||||
"@fontsource/source-code-pro": "^5.2.7",
|
||||
"@fontsource/ubuntu-mono": "^5.2.8",
|
||||
"@hydrooj/utils": "workspace:^",
|
||||
"@mantine/core": "^8.3.8",
|
||||
"@mantine/hooks": "^8.3.8",
|
||||
"@mantine/notifications": "^8.3.8",
|
||||
"@sentry/browser": "^10.25.0",
|
||||
"@sentry/webpack-plugin": "^4.6.0",
|
||||
"@mantine/core": "^8.3.9",
|
||||
"@mantine/hooks": "^8.3.9",
|
||||
"@mantine/notifications": "^8.3.9",
|
||||
"@sentry/browser": "^10.26.0",
|
||||
"@sentry/webpack-plugin": "^4.6.1",
|
||||
"@simplewebauthn/browser": "13.2.2",
|
||||
"@svgr/webpack": "^8.1.0",
|
||||
"@types/html-to-text": "^9.0.4",
|
||||
@@ -41,7 +41,7 @@
|
||||
"@types/redux-logger": "^3.0.13",
|
||||
"@types/uuid": "^11.0.0",
|
||||
"@types/webpack-env": "^1.18.8",
|
||||
"@vscode/codicons": "^0.0.42",
|
||||
"@vscode/codicons": "^0.0.43",
|
||||
"ajv": "^8.17.1",
|
||||
"allotment": "^1.20.4",
|
||||
"autoprefixer": "^10.4.22",
|
||||
@@ -52,8 +52,8 @@
|
||||
"clipboard": "^2.0.11",
|
||||
"copy-webpack-plugin": "^13.0.1",
|
||||
"cordis": "4.0.0-beta.5",
|
||||
"core-js": "^3.46.0",
|
||||
"core-js-compat": "^3.46.0",
|
||||
"core-js": "^3.47.0",
|
||||
"core-js-compat": "^3.47.0",
|
||||
"css-loader": "^7.1.2",
|
||||
"diff": "^8.0.2",
|
||||
"diff-dom": "^5.2.1",
|
||||
@@ -67,9 +67,9 @@
|
||||
"jquery-scroll-lock": "^3.1.3",
|
||||
"jquery.easing": "^1.4.1",
|
||||
"jquery.transit": "^0.9.12",
|
||||
"mantine-contextmenu": "^8.3.8",
|
||||
"mantine-contextmenu": "^8.3.9",
|
||||
"matchmedia-polyfill": "^0.3.2",
|
||||
"md-editor-rt": "^6.1.1",
|
||||
"md-editor-rt": "^6.2.0",
|
||||
"mini-css-extract-plugin": "^2.9.4",
|
||||
"moment": "^2.30.1",
|
||||
"monaco-editor": "0.52.2",
|
||||
@@ -80,7 +80,7 @@
|
||||
"nanoid": "^5.1.6",
|
||||
"normalize.css": "^8.0.1",
|
||||
"nprogress": "^0.2.0",
|
||||
"p-queue": "^9.0.0",
|
||||
"p-queue": "^9.0.1",
|
||||
"pickadate": "^3.6.4",
|
||||
"postcss-loader": "7.3.4",
|
||||
"postcss-preset-mantine": "^1.18.0",
|
||||
@@ -101,7 +101,7 @@
|
||||
"redux-promise-middleware": "^6.2.0",
|
||||
"redux-thunk": "^3.1.0",
|
||||
"rupture": "^0.7.1",
|
||||
"sass": "^1.94.1",
|
||||
"sass": "^1.94.2",
|
||||
"sass-loader": "^16.0.6",
|
||||
"schemastery": "^3.17.2",
|
||||
"shorty.js": "^1.0.1",
|
||||
@@ -117,8 +117,8 @@
|
||||
"timeago.js": "^4.0.2",
|
||||
"ts-loader": "^9.5.4",
|
||||
"web-streams-polyfill": "^4.2.0",
|
||||
"webpack": "^5.102.1",
|
||||
"webpack-bundle-analyzer": "^4.10.2",
|
||||
"webpack": "^5.103.0",
|
||||
"webpack-bundle-analyzer": "^5.0.1",
|
||||
"webpack-dev-server": "^5.2.2",
|
||||
"webpack-manifest-plugin": "^6.0.1",
|
||||
"webpackbar": "^7.0.0"
|
||||
|
||||
@@ -103,9 +103,16 @@
|
||||
<span class="icon icon-comment--text"></span> {{ _('Discussion') }}
|
||||
</a></li>
|
||||
{% 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}) }}">
|
||||
<span class="icon icon-flag"></span> {{ _('All Submissions') }}
|
||||
</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') }}">
|
||||
<span class="icon icon-help"></span> {{ _('Help') }}
|
||||
</a></li>
|
||||
|
||||
@@ -16,6 +16,6 @@
|
||||
"@types/react": "^19.2.6",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@vitejs/plugin-react-swc": "^4.2.2",
|
||||
"vite": "^7.2.2"
|
||||
"vite": "^7.2.4"
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user