bug fixes

This commit is contained in:
undefined committed 2025-03-28 09:41:45 +08:00
1 parent 56311e1209
commit ec23e81f8e
18 files changed
+90 -72

No files matched your search

+3 -3
View File
@@ -4,12 +4,12 @@
"license": "MIT", "license": "MIT",
"main": "base.mjs", "main": "base.mjs",
"dependencies": { "dependencies": {
"@eslint-react/eslint-plugin": "^1.37.3", "@eslint-react/eslint-plugin": "^1.38.2",
"@eslint/compat": "^1.2.7", "@eslint/compat": "^1.2.7",
"@eslint/eslintrc": "^3.3.1", "@eslint/eslintrc": "^3.3.1",
"@stylistic/eslint-plugin": "^4.2.0", "@stylistic/eslint-plugin": "^4.2.0",
"@typescript-eslint/eslint-plugin": "^8.27.0", "@typescript-eslint/eslint-plugin": "^8.28.0",
"@typescript-eslint/parser": "^8.27.0", "@typescript-eslint/parser": "^8.28.0",
"eslint-config-airbnb": "^19.0.4", "eslint-config-airbnb": "^19.0.4",
"eslint-plugin-de-morgan": "^1.2.1", "eslint-plugin-de-morgan": "^1.2.1",
"eslint-plugin-github": "^6.0.0", "eslint-plugin-github": "^6.0.0",
+3 -3
View File
@@ -36,7 +36,7 @@
"@types/chai": "^5.2.1", "@types/chai": "^5.2.1",
"@types/cross-spawn": "^6.0.6", "@types/cross-spawn": "^6.0.6",
"@types/node": "22.13.8", "@types/node": "22.13.8",
"@types/semver": "^7.5.8", "@types/semver": "^7.7.0",
"autocannon": "^8.0.0", "autocannon": "^8.0.0",
"cac": "^6.7.14", "cac": "^6.7.14",
"chai": "^5.2.0", "chai": "^5.2.0",
@@ -44,7 +44,7 @@
"cross-spawn": "^7.0.6", "cross-spawn": "^7.0.6",
"esbuild": "0.25.0", "esbuild": "0.25.0",
"eslint": "^9.23.0", "eslint": "^9.23.0",
"eslint-import-resolver-typescript": "^4.2.2", "eslint-import-resolver-typescript": "^4.2.5",
"eslint-import-resolver-webpack": "^0.13.10", "eslint-import-resolver-webpack": "^0.13.10",
"fs-extra": "^11.3.0", "fs-extra": "^11.3.0",
"globals": "^16.0.0", "globals": "^16.0.0",
@@ -53,7 +53,7 @@
"mongodb-memory-server": "10.1.4", "mongodb-memory-server": "10.1.4",
"nyc": "^17.1.0", "nyc": "^17.1.0",
"ora": "^8.2.0", "ora": "^8.2.0",
"oxlint": "^0.16.2", "oxlint": "^0.16.3",
"package-json": "^10.0.1", "package-json": "^10.0.1",
"semver": "^7.7.1", "semver": "^7.7.1",
"simple-git": "^3.27.0", "simple-git": "^3.27.0",
@@ -42,9 +42,16 @@ function MonacoContainer({
editor.layout(); editor.layout();
}, [size]); }, [size]);
React.useEffect(() => { React.useEffect(() => {
if (!editor) return; if (!editor || !model) return;
const decoration = editor.createDecorationsCollection();
const updateDecoration = () => {
const found = model.findMatches("'[hidden]'", true, false, true, '', true).map((i) => i.range);
decoration.set(found.map((range) => ({ range, options: { inlineClassName: 'decoration-hide' } })));
};
updateDecoration();
const disposable = editor.onDidChangeModelContent(() => { const disposable = editor.onDidChangeModelContent(() => {
const val = editor.getValue({ lineEnding: '\n', preserveBOM: false }); const val = editor.getValue({ lineEnding: '\n', preserveBOM: false });
updateDecoration();
try { try {
const loaded = yaml.load(val); const loaded = yaml.load(val);
schema(loaded); schema(loaded);
@@ -55,7 +62,7 @@ function MonacoContainer({
} }
}); });
return () => disposable.dispose(); // eslint-disable-line return () => disposable.dispose(); // eslint-disable-line
}, [editor, setValue, setError]); }, [editor, model, setValue, setError]);
React.useEffect(() => { React.useEffect(() => {
if (!editorRef.current || loading) return; if (!editorRef.current || loading) return;
setLoading(true); setLoading(true);
+14
View File
@@ -0,0 +1,14 @@
import { omit } from 'lodash';
import React from 'react';
export default function DomComponent(props: React.HTMLAttributes<HTMLDivElement> & { childDom: HTMLElement }) {
const [dom, setDom] = React.useState<HTMLElement | null>(null);
React.useEffect(() => {
if (!dom) return;
dom.appendChild(props.childDom);
return () => { // eslint-disable-line consistent-return
dom.removeChild(props.childDom);
};
}, [dom]);
return <div {...omit(props, 'childDom')} ref={setDom}></div>;
}
+4 -3
View File
@@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { ToastContainer } from 'react-toastify'; import { Slide, ToastContainer } from 'react-toastify';
interface C { interface C {
i18n: (key: string, ...args: any[]) => string; i18n: (key: string, ...args: any[]) => string;
@@ -19,14 +19,15 @@ export default function ComponentsProvider(props: { children: React.ReactNode }
<ToastContainer <ToastContainer
position="bottom-left" position="bottom-left"
autoClose={5000} autoClose={5000}
hideProgressBar={false} hideProgressBar
newestOnTop={false} newestOnTop={false}
closeOnClick={false} closeOnClick={false}
rtl={false} rtl={false}
pauseOnFocusLoss pauseOnFocusLoss
draggable draggable
pauseOnHover pauseOnHover
theme={props.theme} theme="colored"
transition={Slide}
/> />
{props.children} {props.children}
</ComponentsContext.Provider>; </ComponentsContext.Provider>;
+1 -1
View File
@@ -20,7 +20,7 @@
"preferUnplugged": true, "preferUnplugged": true,
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"devDependencies": { "devDependencies": {
"@types/adm-zip": "^0.4.34", "@types/adm-zip": "^0.5.7",
"@types/shell-quote": "^1.7.5", "@types/shell-quote": "^1.7.5",
"@types/ws": "^8.18.0" "@types/ws": "^8.18.0"
} }
+2 -2
View File
@@ -59,7 +59,7 @@
}, },
"devDependencies": { "devDependencies": {
"@babel/code-frame": "^7.26.2", "@babel/code-frame": "^7.26.2",
"@types/adm-zip": "^0.4.34", "@types/adm-zip": "^0.5.7",
"@types/babel__code-frame": "^7.0.6", "@types/babel__code-frame": "^7.0.6",
"@types/fs-extra": "^11.0.4", "@types/fs-extra": "^11.0.4",
"@types/js-yaml": "^4.0.9", "@types/js-yaml": "^4.0.9",
@@ -70,7 +70,7 @@
"@types/nodemailer": "^6.4.17", "@types/nodemailer": "^6.4.17",
"@types/notp": "^2.0.5", "@types/notp": "^2.0.5",
"@types/saslprep": "^1.0.3", "@types/saslprep": "^1.0.3",
"@types/semver": "^7.5.8", "@types/semver": "^7.7.0",
"@types/superagent": "^8.1.9", "@types/superagent": "^8.1.9",
"chokidar": "^4.0.3", "chokidar": "^4.0.3",
"moment": "^2.30.1" "moment": "^2.30.1"
+5 -3
View File
@@ -117,15 +117,17 @@ export async function load(ctx: Context) {
resolve(c); resolve(c);
}); });
}); });
await ctx.loader.reloadPlugin(require.resolve('../service/storage'), 'file'); await Promise.all([
ctx.loader.reloadPlugin(require.resolve('../service/storage'), 'file'),
ctx.loader.reloadPlugin(require.resolve('../service/worker'), 'worker'),
ctx.loader.reloadPlugin(require.resolve('../service/server'), 'server'),
]);
require('../lib/index'); require('../lib/index');
await Promise.all([ await Promise.all([
service(pending, fail, ctx), service(pending, fail, ctx),
]); ]);
ctx.plugin(require('../service/worker'));
await builtinModel(ctx); await builtinModel(ctx);
await model(pending, fail, ctx); await model(pending, fail, ctx);
ctx.plugin(require('../service/server'));
ctx = await new Promise((resolve) => { ctx = await new Promise((resolve) => {
ctx.inject(['server', 'setting', 'worker'], (c) => { ctx.inject(['server', 'setting', 'worker'], (c) => {
resolve(c); resolve(c);
+4 -1
View File
@@ -110,9 +110,12 @@ export class Loader extends Service {
if (!plugin) return; if (!plugin) return;
const config = await this.resolveConfig(plugin, configScope); const config = await this.resolveConfig(plugin, configScope);
let fork = this.state[key]; let fork = this.state[key];
const displayPath = key.includes('node_modules')
? key.split('node_modules').pop()
: path.relative(process.cwd(), key);
logger.info( logger.info(
`%s plugin %c${configScope ? ' with scope %c' : ''}`, `%s plugin %c${configScope ? ' with scope %c' : ''}`,
fork ? 'reload' : 'apply', key.split('node_modules').pop(), configScope, fork ? 'reload' : 'apply', displayPath, configScope,
); );
if (fork) { if (fork) {
fork.update(config); fork.update(config);
+3 -8
View File
@@ -5,7 +5,7 @@ import path from 'path';
import AdmZip from 'adm-zip'; import AdmZip from 'adm-zip';
import fs from 'fs-extra'; import fs from 'fs-extra';
import yaml from 'js-yaml'; import yaml from 'js-yaml';
import { pick } from 'lodash'; import { keyBy, pick } from 'lodash';
import { Filter, ObjectId } from 'mongodb'; import { Filter, ObjectId } from 'mongodb';
import type { Readable } from 'stream'; import type { Readable } from 'stream';
import { ProblemConfigFile, ProblemType } from '@hydrooj/common'; import { ProblemConfigFile, ProblemType } from '@hydrooj/common';
@@ -409,12 +409,7 @@ export class ProblemModel {
const psdocs = await ProblemModel.getMultiStatus( const psdocs = await ProblemModel.getMultiStatus(
domainId, { uid, docId: { $in: Array.from(new Set(pids)) } }, domainId, { uid, docId: { $in: Array.from(new Set(pids)) } },
).toArray(); ).toArray();
const r: Record<string, ProblemStatusDoc> = {}; return keyBy(psdocs, 'docId');
for (const psdoc of psdocs) {
r[psdoc.docId] = psdoc;
r[`${psdoc.domainId}#${psdoc.docId}`] = psdoc;
}
return r;
} }
static async updateStatus( static async updateStatus(
@@ -468,7 +463,7 @@ export class ProblemModel {
throw new ValidationError('zip', null, e.message); throw new ValidationError('zip', null, e.message);
} }
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
zip.extractAllToAsync(tmpdir, true, (err) => { zip.extractAllToAsync(tmpdir, true, false, (err) => {
if (err) reject(err); if (err) reject(err);
resolve(null); resolve(null);
}); });
+1 -1
View File
@@ -252,7 +252,7 @@ export default class RecordModel {
RecordModel.collStat.deleteMany({ _id: { $in: rids } }), RecordModel.collStat.deleteMany({ _id: { $in: rids } }),
task.deleteMany({ rid: { $in: rids } }), task.deleteMany({ rid: { $in: rids } }),
]); ]);
await RecordModel.collHistory.insertMany(rdocs.map((rdoc) => ({ await RecordModel.collHistory.insertMany(rdocs.filter((rdoc) => rdoc.judgeAt).map((rdoc) => ({
...pick(rdoc, [ ...pick(rdoc, [
'compilerTexts', 'judgeTexts', 'testCases', 'subtasks', 'compilerTexts', 'judgeTexts', 'testCases', 'subtasks',
'score', 'time', 'memory', 'status', 'judgeAt', 'judger', 'score', 'time', 'memory', 'status', 'judgeAt', 'judger',
+15 -18
View File
@@ -178,7 +178,7 @@ async function cleanFiles() {
} }
} }
export function apply(ctx: Context) { export async function apply(ctx: Context) {
ctx.inject(['worker'], (c) => { ctx.inject(['worker'], (c) => {
c.worker.addHandler('storage.prune', cleanFiles); c.worker.addHandler('storage.prune', cleanFiles);
}); });
@@ -190,24 +190,21 @@ export function apply(ctx: Context) {
]); ]);
await StorageModel.del(problemFiles.concat(contestFiles).concat(trainingFiles).map((i) => i.path)); await StorageModel.del(problemFiles.concat(contestFiles).concat(trainingFiles).map((i) => i.path));
}); });
if (process.env.NODE_APP_INSTANCE !== '0') return; if (process.env.NODE_APP_INSTANCE !== '0') return;
ctx.on('ready', async () => { await db.ensureIndexes(
await db.ensureIndexes( StorageModel.coll,
StorageModel.coll, { key: { path: 1 }, name: 'path' },
{ key: { path: 1 }, name: 'path' }, { key: { path: 1, autoDelete: 1 }, sparse: true, name: 'autoDelete' },
{ key: { path: 1, autoDelete: 1 }, sparse: true, name: 'autoDelete' }, { key: { link: 1 }, sparse: true, name: 'link' },
{ key: { link: 1 }, sparse: true, name: 'link' }, );
); if (!await ScheduleModel.count({ type: 'schedule', subType: 'storage.prune' })) {
if (!await ScheduleModel.count({ type: 'schedule', subType: 'storage.prune' })) { await ScheduleModel.add({
await ScheduleModel.add({ type: 'schedule',
type: 'schedule', subType: 'storage.prune',
subType: 'storage.prune', executeAfter: moment().startOf('hour').toDate(),
executeAfter: moment().startOf('hour').toDate(), interval: [1, 'hour'],
interval: [1, 'hour'], });
}); }
}
});
} }
global.Hydro.model.storage = StorageModel; global.Hydro.model.storage = StorageModel;
+1 -1
View File
@@ -62,7 +62,7 @@ export class MongoService extends Service {
yield () => { this.client.close(); }; yield () => { this.client.close(); };
this.db = this.client.db(url.database || 'hydro'); this.db = this.client.db(url.database || 'hydro');
await bus.parallel('database/connect', this.db); await bus.parallel('database/connect', this.db);
this.ctx.setInterval(() => this.fixExpireAfter(), Time.hour); yield this.ctx.setInterval(() => this.fixExpireAfter(), Time.hour);
} }
public collection<K extends keyof Collections>(c: K) { public collection<K extends keyof Collections>(c: K) {
+9 -12
View File
@@ -98,17 +98,14 @@ export async function updateJudge(args) {
); );
} }
export function apply(ctx: Context) { export async function apply(ctx: Context) {
if (process.env.NODE_APP_INSTANCE !== '0') return; if (process.env.NODE_APP_INSTANCE !== '0') return;
ctx.on('app/started', async () => { const info = await sysinfo.get();
sysinfo.get().then((info) => { coll.updateOne(
coll.updateOne( { mid: info.mid, type: 'server' },
{ mid: info.mid, type: 'server' }, { $set: { ...info, updateAt: new Date(), type: 'server' } },
{ $set: { ...info, updateAt: new Date(), type: 'server' } }, { upsert: true },
{ upsert: true }, );
); feedback();
feedback(); return ctx.setInterval(update, 1800 * 1000); // eslint-disable-line
setInterval(update, 1800 * 1000);
});
});
} }
+7 -5
View File
@@ -18,7 +18,14 @@ export default class WorkerService extends Service {
constructor(ctx: Context) { constructor(ctx: Context) {
super(ctx, 'worker'); super(ctx, 'worker');
}
async *[Context.init]() {
this.consume(); this.consume();
yield () => {
this.consuming = false;
return this.promise;
};
} }
async getFirst() { async getFirst() {
@@ -79,9 +86,4 @@ export default class WorkerService extends Service {
}; };
}); });
} }
public async stop() {
this.consuming = false;
await this.promise;
}
} }
+4 -4
View File
@@ -22,8 +22,8 @@
"@fontsource/source-code-pro": "^5.2.5", "@fontsource/source-code-pro": "^5.2.5",
"@fontsource/ubuntu-mono": "^5.2.5", "@fontsource/ubuntu-mono": "^5.2.5",
"@hydrooj/utils": "workspace:^", "@hydrooj/utils": "workspace:^",
"@sentry/browser": "^9.9.0", "@sentry/browser": "^9.10.0",
"@sentry/webpack-plugin": "^3.2.2", "@sentry/webpack-plugin": "^3.2.3",
"@simplewebauthn/browser": "13.1.0", "@simplewebauthn/browser": "13.1.0",
"@svgr/webpack": "^8.1.0", "@svgr/webpack": "^8.1.0",
"@types/html-to-text": "^9.0.4", "@types/html-to-text": "^9.0.4",
@@ -67,7 +67,7 @@
"jquery.easing": "^1.4.1", "jquery.easing": "^1.4.1",
"jquery.transit": "^0.9.12", "jquery.transit": "^0.9.12",
"matchmedia-polyfill": "^0.3.2", "matchmedia-polyfill": "^0.3.2",
"md-editor-rt": "^5.4.4", "md-editor-rt": "^5.4.5",
"mini-css-extract-plugin": "^2.9.2", "mini-css-extract-plugin": "^2.9.2",
"moment": "^2.30.1", "moment": "^2.30.1",
"monaco-editor": "0.52.2", "monaco-editor": "0.52.2",
@@ -118,7 +118,7 @@
"web-streams-polyfill": "^4.1.0", "web-streams-polyfill": "^4.1.0",
"webpack": "^5.98.0", "webpack": "^5.98.0",
"webpack-bundle-analyzer": "^4.10.2", "webpack-bundle-analyzer": "^4.10.2",
"webpack-dev-server": "^5.2.0", "webpack-dev-server": "^5.2.1",
"webpack-manifest-plugin": "^5.0.1", "webpack-manifest-plugin": "^5.0.1",
"webpackbar": "^7.0.0" "webpackbar": "^7.0.0"
}, },
@@ -48,8 +48,8 @@
<script> <script>
var _htmlNode = document.documentElement; var _htmlNode = document.documentElement;
_htmlNode.className = _htmlNode.className.replace(' nojs', ' hasjs'); _htmlNode.className = _htmlNode.className.replace(' nojs', ' hasjs');
var UiContext = '{{ UiContext|json|jsesc|safe }}'; window.UiContext = '{{ UiContext|json|jsesc|safe }}';
var UserContext = '{{ UserContext|json|jsesc|safe }}'; window.UserContext = '{{ UserContext|json|jsesc|safe }}';
</script> </script>
{% endif %} {% endif %}
{% set UiContext = Object.create(UiContext) %} {% set UiContext = Object.create(UiContext) %}
@@ -65,8 +65,8 @@
{% block script %}{% endblock %} {% block script %}{% endblock %}
{% if not isIE(handler.request.headers['user-agent']) and not handler.session.legacy %} {% if not isIE(handler.request.headers['user-agent']) and not handler.session.legacy %}
<script> <script>
var UiContextNew = '{{ UiContext|json|jsesc|safe }}'; window.UiContextNew = '{{ UiContext|json|jsesc|safe }}';
var UserContextNew = '{{ UserContext|json|jsesc|safe }}'; window.UserContextNew = '{{ UserContext|json|jsesc|safe }}';
</script> </script>
{% endif %} {% endif %}
</body> </body>
+1 -1
View File
@@ -16,6 +16,6 @@
"@types/react": "^19.0.12", "@types/react": "^19.0.12",
"@types/react-dom": "^19.0.4", "@types/react-dom": "^19.0.4",
"@vitejs/plugin-react-swc": "^3.8.1", "@vitejs/plugin-react-swc": "^3.8.1",
"vite": "^6.2.2" "vite": "^6.2.3"
} }
} }