mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 09:36:08 +08:00
core&ui: remove deprecated apis
This commit is contained in:
26 files changed
+90
-249
No files matched your search
@@ -145,8 +145,10 @@ Hydro 用户群:1085853538
|
||||
- core: 移除 global.Hydro.service
|
||||
- core: 移除 global.Hydro.ui.template
|
||||
- core: 移除 global.Hydro.lib
|
||||
- core: 移除 String.prototype.translate (使用 app.i18n.translate)
|
||||
- core: 移除 String.prototype.translate (使用 ctx.i18n.translate)
|
||||
- core: 升级至 Mongo Driver 6
|
||||
- core: 移除 registerResolver, registerValue, registerUnion (使用 ctx.api.resolver/...)
|
||||
- core: 移除 paginate, rank (使用 ctx.db.paginate, ctx.db.ranked)
|
||||
|
||||
## Hydro 4.19.0 / UI 4.57.0
|
||||
|
||||
|
||||
+5
-1
@@ -20,7 +20,7 @@ const compilerOptionsBase = {
|
||||
module: 'preserve',
|
||||
esModuleInterop: true,
|
||||
moduleResolution: 'bundler',
|
||||
jsx: 'react',
|
||||
jsx: 'react-jsx',
|
||||
sourceMap: false,
|
||||
composite: true,
|
||||
strictBindCallApply: true,
|
||||
@@ -70,6 +70,10 @@ for (const name of ['plugins', 'modules']) {
|
||||
}
|
||||
// Write an empty file to make eslint happy
|
||||
fs.writeFileSync(path.resolve(process.cwd(), name, 'nop.ts'), 'export default {};\n');
|
||||
fs.writeFileSync(
|
||||
path.resolve(process.cwd(), name, 'vue.d.ts'),
|
||||
'declare module "*.vue" {\n const content: any;\n export default content;\n}\n',
|
||||
);
|
||||
}
|
||||
|
||||
const modules = [
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"license": "MIT",
|
||||
"main": "base.mjs",
|
||||
"dependencies": {
|
||||
"@eslint-react/eslint-plugin": "^1.38.2",
|
||||
"@eslint-react/eslint-plugin": "^1.38.3",
|
||||
"@eslint/compat": "^1.2.7",
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@stylistic/eslint-plugin": "^4.2.0",
|
||||
|
||||
@@ -336,7 +336,7 @@ export interface WebServiceConfig {
|
||||
enableSSE?: boolean;
|
||||
}
|
||||
|
||||
export class WebService<C extends CordisContext = CordisContext> extends Service<C> {
|
||||
export class WebService<C extends CordisContext = CordisContext> extends Service<never, C> {
|
||||
private registry: Record<string, any> = Object.create(null);
|
||||
private registrationCount = Counter();
|
||||
private serverLayers = [];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {
|
||||
_, Context, DiscussionNotFoundError, DocumentModel, Filter,
|
||||
Handler, NumberKeys, ObjectId, OplogModel, paginate,
|
||||
Handler, NumberKeys, ObjectId, OplogModel,
|
||||
param, PRIV, Types, UserModel,
|
||||
} from 'hydrooj';
|
||||
|
||||
@@ -116,7 +116,7 @@ class BlogUserHandler extends BlogHandler {
|
||||
@param('uid', Types.Int)
|
||||
@param('page', Types.PositiveInt, true)
|
||||
async get(domainId: string, uid: number, page = 1) {
|
||||
const [ddocs, dpcount] = await paginate(
|
||||
const [ddocs, dpcount] = await this.ctx.db.paginate(
|
||||
BlogModel.getMulti({ owner: uid }),
|
||||
page,
|
||||
10,
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
flex-wrap: wrap;
|
||||
overflow-y: auto;
|
||||
|
||||
@include form-styles();
|
||||
background-color: $input-background-color;
|
||||
outline: $input-outline;
|
||||
transition: outline-color .2s, border-color .2s;
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
declare module '*.svg?react' {
|
||||
import { ComponentType } from 'react';
|
||||
const content: ComponentType;
|
||||
export default content;
|
||||
}
|
||||
declare module '*.vue' {
|
||||
const content: any;
|
||||
export default content;
|
||||
}
|
||||
declare module '*.css' {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
declare module '*.styl' {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
declare module '*.scss' {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
"dependencies": {
|
||||
"@hydrooj/common": "workspace:^",
|
||||
"@hydrooj/utils": "workspace:^",
|
||||
"adm-zip": "0.5.14",
|
||||
"adm-zip": "0.5.16",
|
||||
"cac": "^6.7.14",
|
||||
"p-queue": "^8.1.0",
|
||||
"schemastery": "^3.16.1",
|
||||
|
||||
@@ -158,7 +158,7 @@ export default class VJ4 implements Session {
|
||||
if (entries.length > 512) throw new FormatError('Too many files');
|
||||
if (Math.sum(entries.map((i) => i.header.size)) > 256 * 1024 * 1024) throw new FormatError('File too large');
|
||||
await new Promise((resolve, reject) => {
|
||||
zip.extractAllToAsync(savePath, true, (e) => {
|
||||
zip.extractAllToAsync(savePath, true, false, (e) => {
|
||||
if (e) reject(e);
|
||||
else resolve(null);
|
||||
});
|
||||
@@ -183,7 +183,7 @@ export default class VJ4 implements Session {
|
||||
const entries = zip.getEntries();
|
||||
if (entries.length > 512) throw new FormatError('Too many files');
|
||||
await new Promise((resolve, reject) => {
|
||||
zip.extractAllToAsync(savePath, true, (e) => {
|
||||
zip.extractAllToAsync(savePath, true, false, (e) => {
|
||||
if (e) reject(e);
|
||||
else resolve(null);
|
||||
});
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
"@aws-sdk/lib-storage": "3.726.1",
|
||||
"@aws-sdk/s3-presigned-post": "3.726.1",
|
||||
"@aws-sdk/s3-request-presigner": "3.726.1",
|
||||
"@cordisjs/core": "4.0.0-alpha.7",
|
||||
"@cordisjs/logger": "1.0.0-alpha.4",
|
||||
"@cordisjs/core": "4.0.0-alpha.8",
|
||||
"@cordisjs/logger": "1.0.0-alpha.5",
|
||||
"@cordisjs/plugin-timer": "1.0.0-alpha.0",
|
||||
"@graphql-tools/schema": "^10.0.23",
|
||||
"@hydrooj/common": "workspace:^",
|
||||
@@ -26,7 +26,7 @@
|
||||
"@simplewebauthn/server": "13.1.1",
|
||||
"@undefined-moe/require-resolve-hook": "^1.1.0",
|
||||
"@yarnpkg/libzip": "^3.1.1",
|
||||
"adm-zip": "0.5.14",
|
||||
"adm-zip": "0.5.16",
|
||||
"cac": "^6.7.14",
|
||||
"csv-stringify": "^6.5.2",
|
||||
"emoji-regex": "^10.4.0",
|
||||
@@ -39,7 +39,7 @@
|
||||
"koa-static-cache": "^5.1.4",
|
||||
"lodash": "^4.17.21",
|
||||
"lru-cache": "^10.4.3",
|
||||
"mime-types": "^2.1.35",
|
||||
"mime-types": "^3.0.1",
|
||||
"moment-timezone": "^0.5.48",
|
||||
"mongodb": "^6.15.0",
|
||||
"mongodb-uri": "^0.9.7",
|
||||
|
||||
@@ -39,7 +39,7 @@ export interface Context extends cordis.Context {
|
||||
geoip?: GeoIP;
|
||||
}
|
||||
|
||||
export abstract class Service extends cordis.Service<Context> {
|
||||
export abstract class Service<T = never> extends cordis.Service<T, Context> {
|
||||
}
|
||||
|
||||
const T = <F extends (...args: any[]) => any>(origFunc: F, disposeFunc?) =>
|
||||
@@ -50,7 +50,7 @@ const T = <F extends (...args: any[]) => any>(origFunc: F, disposeFunc?) =>
|
||||
});
|
||||
};
|
||||
|
||||
export class ApiMixin extends cordis.Service<Context> {
|
||||
export class ApiMixin extends cordis.Service<never, Context> {
|
||||
addScript = T(addScript);
|
||||
setImmediate = T(setImmediate, clearImmediate);
|
||||
provideModule = T(provideModule);
|
||||
|
||||
@@ -8,7 +8,7 @@ import sanitize from 'sanitize-filename';
|
||||
import { JudgeResultBody, ProblemConfigFile, TestCase } from '@hydrooj/common';
|
||||
import { Context } from '../context';
|
||||
import {
|
||||
FileLimitExceededError, ForbiddenError, ProblemIsReferencedError, ValidationError,
|
||||
BadRequestError, FileLimitExceededError, ForbiddenError, ProblemIsReferencedError, ValidationError,
|
||||
} from '../error';
|
||||
import { RecordDoc, Task } from '../interface';
|
||||
import { Logger } from '../logger';
|
||||
@@ -102,40 +102,6 @@ export async function postJudge(rdoc: RecordDoc) {
|
||||
await bus.parallel('record/judge', rdoc, updated, pdoc);
|
||||
}
|
||||
|
||||
bus.on('record/judge', async (rdoc, updated, pdoc) => {
|
||||
if (!pdoc || rdoc.status !== STATUS.STATUS_HACK_SUCCESSFUL) return;
|
||||
if (rdoc.contest) return;
|
||||
try {
|
||||
const config = yaml.load(pdoc.config as string) as ProblemConfigFile;
|
||||
assert(config.subtasks instanceof Array);
|
||||
const file = await storage.get(`submission/${rdoc.files.hack.split('#')[0]}`);
|
||||
assert(file);
|
||||
const hackSubtask = config.subtasks[config.subtasks.length - 1];
|
||||
hackSubtask.cases ||= [];
|
||||
const input = `hack-${rdoc._id}-${hackSubtask.cases.length + 1}.in`;
|
||||
hackSubtask.cases.push({ input, output: '/dev/null' });
|
||||
await Promise.all([
|
||||
problem.addTestdata(rdoc.domainId, rdoc.pid, input, file),
|
||||
problem.addTestdata(rdoc.domainId, rdoc.pid, 'config.yaml', Buffer.from(yaml.dump(config))),
|
||||
]);
|
||||
// trigger rejudge
|
||||
const rdocs = await record.getMulti(rdoc.domainId, {
|
||||
pid: rdoc.pid,
|
||||
status: STATUS.STATUS_ACCEPTED,
|
||||
contest: { $nin: [record.RECORD_GENERATE, record.RECORD_PRETEST] },
|
||||
}).project({ _id: 1, contest: 1 }).toArray();
|
||||
const priority = await record.submissionPriority(rdoc.uid, -5000 - rdocs.length * 5 - 50);
|
||||
await record.judge(rdoc.domainId, rdocs.map((r) => r._id), priority, {}, { hackRejudge: input });
|
||||
} catch (e) {
|
||||
next({
|
||||
rid: rdoc._id.toString(),
|
||||
domainId: rdoc.domainId,
|
||||
key: 'next',
|
||||
message: { message: 'Unable to apply hack: {0}', params: [e.message] },
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export async function end(body: Partial<JudgeResultBody>) {
|
||||
const { $set, $push } = processPayload(body);
|
||||
const $unset: any = { progress: '' };
|
||||
@@ -219,7 +185,6 @@ export class JudgeConnectionHandler extends ConnectionHandler {
|
||||
query: any = { type: { $in: ['judge', 'generate'] } };
|
||||
concurrency = 1;
|
||||
consumer: Consumer = null;
|
||||
startTimeout: NodeJS.Timeout = null;
|
||||
tasks: Record<string, {
|
||||
resolve: (_: any) => void,
|
||||
queue: PQueue,
|
||||
@@ -230,10 +195,6 @@ export class JudgeConnectionHandler extends ConnectionHandler {
|
||||
async prepare() {
|
||||
logger.info('Judge daemon connected from ', this.request.ip);
|
||||
this.sendLanguageConfig();
|
||||
// TODO deprecated, just for compatibility
|
||||
this.startTimeout = setTimeout(() => {
|
||||
this.consumer ||= task.consume(this.query, this.newTask.bind(this), true, this.concurrency);
|
||||
}, 15000);
|
||||
}
|
||||
|
||||
@subscribe('system/setting')
|
||||
@@ -274,10 +235,6 @@ export class JudgeConnectionHandler extends ConnectionHandler {
|
||||
}
|
||||
} else if (msg.key === 'status') {
|
||||
await updateJudge(msg.info);
|
||||
} else if (msg.key === 'prio' && typeof msg.prio === 'number') {
|
||||
// TODO deprecated, use config instead
|
||||
this.query.priority = { $gt: msg.prio };
|
||||
this.consumer?.setQuery(this.query);
|
||||
} else if (msg.key === 'config') {
|
||||
if (Number.isSafeInteger(msg.prio)) {
|
||||
this.query.priority = { $gt: msg.prio };
|
||||
@@ -296,14 +253,13 @@ export class JudgeConnectionHandler extends ConnectionHandler {
|
||||
this.consumer?.setQuery(this.query);
|
||||
}
|
||||
} else if (msg.key === 'start') {
|
||||
clearTimeout(this.startTimeout);
|
||||
this.consumer ||= task.consume(this.query, this.newTask.bind(this), true, this.concurrency);
|
||||
if (this.consumer) throw new BadRequestError('Judge daemon already started');
|
||||
this.consumer = task.consume(this.query, this.newTask.bind(this), true, this.concurrency);
|
||||
logger.info('Judge daemon started');
|
||||
}
|
||||
}
|
||||
|
||||
async cleanup() {
|
||||
clearTimeout(this.startTimeout);
|
||||
this.consumer?.destroy();
|
||||
logger.info('Judge daemon disconnected from ', this.request.ip);
|
||||
await Promise.all(Object.values(this.tasks).map(async ({ t }) => {
|
||||
@@ -318,6 +274,39 @@ export async function apply(ctx: Context) {
|
||||
ctx.Route('judge_files_download', '/judge/files', JudgeFilesDownloadHandler, builtin.PRIV.PRIV_JUDGE);
|
||||
ctx.Route('judge_files_upload', '/judge/upload', JudgeFileUpdateHandler, builtin.PRIV.PRIV_JUDGE);
|
||||
ctx.Connection('judge_conn', '/judge/conn', JudgeConnectionHandler, builtin.PRIV.PRIV_JUDGE);
|
||||
ctx.on('record/judge', async (rdoc, updated, pdoc) => {
|
||||
if (!pdoc || rdoc.status !== STATUS.STATUS_HACK_SUCCESSFUL) return;
|
||||
if (rdoc.contest) return;
|
||||
try {
|
||||
const config = yaml.load(pdoc.config as string) as ProblemConfigFile;
|
||||
assert(Array.isArray(config.subtasks));
|
||||
const file = await storage.get(`submission/${rdoc.files.hack.split('#')[0]}`);
|
||||
assert(file);
|
||||
const hackSubtask = config.subtasks[config.subtasks.length - 1];
|
||||
hackSubtask.cases ||= [];
|
||||
const input = `hack-${rdoc._id}-${hackSubtask.cases.length + 1}.in`;
|
||||
hackSubtask.cases.push({ input, output: '/dev/null' });
|
||||
await Promise.all([
|
||||
problem.addTestdata(rdoc.domainId, rdoc.pid, input, file),
|
||||
problem.addTestdata(rdoc.domainId, rdoc.pid, 'config.yaml', Buffer.from(yaml.dump(config))),
|
||||
]);
|
||||
// trigger rejudge
|
||||
const rdocs = await record.getMulti(rdoc.domainId, {
|
||||
pid: rdoc.pid,
|
||||
status: STATUS.STATUS_ACCEPTED,
|
||||
contest: { $nin: [record.RECORD_GENERATE, record.RECORD_PRETEST] },
|
||||
}).project({ _id: 1, contest: 1 }).toArray();
|
||||
const priority = await record.submissionPriority(rdoc.uid, -5000 - rdocs.length * 5 - 50);
|
||||
await record.judge(rdoc.domainId, rdocs.map((r) => r._id), priority, {}, { hackRejudge: input });
|
||||
} catch (e) {
|
||||
next({
|
||||
rid: rdoc._id.toString(),
|
||||
domainId: rdoc.domainId,
|
||||
key: 'next',
|
||||
message: { message: 'Unable to apply hack: {0}', params: [e.message] },
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
apply.next = next;
|
||||
|
||||
@@ -42,14 +42,6 @@ export function inject(node: UIInjectableFields, name: string, args: Record<stri
|
||||
export function getNodes(name: UIInjectableFields) {
|
||||
return nodes[name];
|
||||
}
|
||||
/** @deprecated */
|
||||
export const Nav = (name, args, prefix, ...permPrivChecker) => {
|
||||
inject('Nav', name, { ...args, prefix }, ...permPrivChecker);
|
||||
};
|
||||
/** @deprecated */
|
||||
export const ProblemAdd = (name, args, icon = 'add', text = 'Create Problem') => {
|
||||
inject('ProblemAdd', name, { ...args, icon, text });
|
||||
};
|
||||
|
||||
inject('Nav', 'homepage', { prefix: 'homepage' });
|
||||
inject('Nav', 'problem_main', { prefix: 'problem' }, PERM.PERM_VIEW_PROBLEM);
|
||||
@@ -84,4 +76,3 @@ inject('DomainManage', 'domain_group', { family: 'Access Control', icon: 'user'
|
||||
global.Hydro.ui.inject = inject;
|
||||
global.Hydro.ui.nodes = nodes as any;
|
||||
global.Hydro.ui.getNodes = getNodes;
|
||||
Object.assign(global.Hydro.ui, { ProblemAdd, Nav });
|
||||
@@ -37,7 +37,6 @@ export const typeDisplay = {
|
||||
[document.TYPE_CONTEST]: 'contest',
|
||||
[document.TYPE_DISCUSSION_NODE]: 'node',
|
||||
[document.TYPE_TRAINING]: 'training',
|
||||
[document.TYPE_HOMEWORK]: 'homework',
|
||||
};
|
||||
|
||||
export const coll = db.collection('discussion.history');
|
||||
|
||||
@@ -29,8 +29,6 @@ export const TYPE_DISCUSSION_REPLY = 22 as const;
|
||||
export const TYPE_CONTEST = 30 as const;
|
||||
export const TYPE_CONTEST_CLARIFICATION = 31 as const;
|
||||
export const TYPE_TRAINING = 40 as const;
|
||||
/** @deprecated use `TYPE_CONTEST` with rule `homework` instead. */
|
||||
export const TYPE_HOMEWORK = 60 as const;
|
||||
|
||||
export interface DocType {
|
||||
[TYPE_PROBLEM]: ProblemDoc;
|
||||
@@ -486,7 +484,6 @@ global.Hydro.model.document = {
|
||||
TYPE_DISCUSSION,
|
||||
TYPE_DISCUSSION_NODE,
|
||||
TYPE_DISCUSSION_REPLY,
|
||||
TYPE_HOMEWORK,
|
||||
TYPE_PROBLEM,
|
||||
TYPE_PROBLEM_LIST,
|
||||
TYPE_PROBLEM_SOLUTION,
|
||||
|
||||
@@ -34,7 +34,6 @@ export { default as StorageModel } from './model/storage';
|
||||
export { default as TaskModel } from './model/task';
|
||||
export * from './model/builtin';
|
||||
export * as JudgeHandler from './handler/judge';
|
||||
export { registerResolver, registerValue, registerUnion } from './service/api';
|
||||
export { postJudge } from './handler/judge';
|
||||
export { Collections } from './service/db';
|
||||
export { Handler, ConnectionHandler, requireSudo } from './service/server';
|
||||
@@ -46,10 +45,6 @@ export { default as rating } from './lib/rating';
|
||||
export { default as avatar } from './lib/avatar';
|
||||
export { parseConfig as testdataConfig } from './lib/testdataConfig';
|
||||
export { sendMail } from './lib/mail';
|
||||
/** @deprecated use Handler.paginate instead */
|
||||
export const paginate = db.paginate.bind(db);
|
||||
/** @deprecated use db.ranked instead */
|
||||
export const rank = db.ranked.bind(db);
|
||||
export { UiContextBase } from './service/layers/base';
|
||||
export * from '@hydrooj/framework/decorators';
|
||||
export * from '@hydrooj/framework/validator';
|
||||
@@ -57,5 +52,6 @@ export * as StorageService from './service/storage';
|
||||
export { EventMap } from './service/bus';
|
||||
export { db, pwsh };
|
||||
|
||||
// to load ctx.scoreboard
|
||||
// to load services into to context
|
||||
export { } from './handler/contest';
|
||||
export { } from './service/api';
|
||||
@@ -99,24 +99,6 @@ class ApiHandler extends Handler {
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated use ctx.api.value() instead */
|
||||
export function registerValue(...args: any[]) {
|
||||
// @ts-ignore
|
||||
return app.api.value(...args);
|
||||
}
|
||||
|
||||
/** @deprecated use ctx.api.resolver() instead */
|
||||
export function registerResolver(...args: any[]) {
|
||||
// @ts-ignore
|
||||
return app.api.resolver(...args);
|
||||
}
|
||||
|
||||
/** @deprecated use ctx.api.union() instead */
|
||||
export function registerUnion(...args: any[]) {
|
||||
// @ts-ignore
|
||||
return app.api.resolver(...args);
|
||||
}
|
||||
|
||||
// TODO support dispose
|
||||
class ApiService extends Service {
|
||||
constructor(ctx: Context) {
|
||||
@@ -212,7 +194,7 @@ class ApiService extends Service {
|
||||
}
|
||||
|
||||
export const sideEffect = true;
|
||||
export const using = ['server'];
|
||||
export const inject = ['server'];
|
||||
|
||||
export function apply(ctx: Context) {
|
||||
ctx.plugin(ApiService);
|
||||
|
||||
@@ -32,8 +32,8 @@ import welcome from './welcome';
|
||||
|
||||
const logger = new Logger('upgrade');
|
||||
const unsupportedUpgrade = async function _26_27() {
|
||||
throw new Error('This upgrade was no longer supported in hydrooj@4. \
|
||||
Please use hydrooj@3 to perform these upgrades before upgrading to v4');
|
||||
throw new Error('This upgrade was no longer supported in hydrooj@5. \
|
||||
Please use hydrooj@4 to perform these upgrades before upgrading to v5');
|
||||
};
|
||||
|
||||
const onPrimary = { readPreference: 'primary' } as const;
|
||||
@@ -53,141 +53,7 @@ export const coreScripts: MigrationScript[] = [
|
||||
return true;
|
||||
},
|
||||
// Init
|
||||
...new Array(28).fill(unsupportedUpgrade), // oxlint-disable-line no-new-array
|
||||
async function _29_30() {
|
||||
return await iterateAllDomain((ddoc) => RecordModel.coll.updateMany({ domainId: ddoc._id }, { $set: { pdomain: ddoc._id } }));
|
||||
},
|
||||
// Add send_message priv to user
|
||||
async function _30_31() {
|
||||
return await iterateAllUser((udoc) => user.setPriv(udoc._id, udoc.priv | PRIV.PRIV_SEND_MESSAGE));
|
||||
},
|
||||
null,
|
||||
// Write builtin users to database
|
||||
async function _32_33() {
|
||||
if (!await user.getById('system', 0)) {
|
||||
await user.create('Guest@hydro.local', 'Guest', String.random(32), 0, '127.0.0.1', PRIV.PRIV_REGISTER_USER);
|
||||
}
|
||||
if (!await user.getById('system', 1)) {
|
||||
await user.create('Hydro@hydro.local', 'Hydro', String.random(32), 1, '127.0.0.1', PRIV.PRIV_USER_PROFILE);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
async function _33_34() {
|
||||
return await iterateAllProblem(['content'], async (pdoc) => {
|
||||
if (typeof pdoc.content !== 'string') return;
|
||||
await problem.edit(pdoc.domainId, pdoc.docId, { content: pdoc.content.replace(/%file%:\/\//g, 'file://') });
|
||||
});
|
||||
},
|
||||
async function _34_35() {
|
||||
return await iterateAllDomain((ddoc) => domain.edit(ddoc._id, { lower: ddoc._id.toLowerCase() }));
|
||||
},
|
||||
async function _35_36() {
|
||||
await RecordModel.coll.updateMany({}, { $unset: { effective: '' } });
|
||||
return true;
|
||||
},
|
||||
async function _36_37() {
|
||||
for await (const doc of document.collStatus.find()) {
|
||||
await document.collStatus.deleteMany({
|
||||
...pick(doc, ['docId', 'domainId', 'uid', 'docType']),
|
||||
_id: { $gt: doc._id },
|
||||
});
|
||||
}
|
||||
return true;
|
||||
},
|
||||
async function _37_38() {
|
||||
return await iterateAllProblem(['docId', 'domainId', 'config'], async (pdoc) => {
|
||||
logger.info('%s/%s', pdoc.domainId, pdoc.docId);
|
||||
if (typeof pdoc.config !== 'string') return;
|
||||
if (!pdoc.config.includes('type: subjective')) return;
|
||||
await problem.addTestdata(
|
||||
pdoc.domainId, pdoc.docId, 'config.yaml',
|
||||
Buffer.from(pdoc.config.replace('type: subjective', 'type: objective')),
|
||||
);
|
||||
});
|
||||
},
|
||||
async function _38_39() {
|
||||
return await iterateAllDomain(async (ddoc) => {
|
||||
ddoc.roles.root = '-1';
|
||||
await domain.setRoles(ddoc._id, ddoc.roles);
|
||||
});
|
||||
},
|
||||
async function _39_40() {
|
||||
return await iterateAllDomain(async ({ _id }) => {
|
||||
const ddocs = await discussion.getMulti(_id, { parentType: document.TYPE_PROBLEM }).toArray();
|
||||
for (const ddoc of ddocs) {
|
||||
const pdoc = await problem.get(_id, ddoc.parentId as any);
|
||||
await document.set(_id, document.TYPE_DISCUSSION, ddoc.docId, { parentId: pdoc.docId });
|
||||
}
|
||||
});
|
||||
},
|
||||
async function _40_41() {
|
||||
// Ignore drop index failure
|
||||
await db.collection('storage').dropIndex('path_1').catch(() => { });
|
||||
await db.collection('storage').updateMany(
|
||||
{ autoDelete: { $gte: moment().add(5, 'days').toDate() } },
|
||||
{ $unset: { autoDelete: '' } },
|
||||
);
|
||||
return true;
|
||||
},
|
||||
async function _41_42() {
|
||||
return await iterateAllDomain(async (ddoc) => {
|
||||
const cursor = discussion.getMulti(ddoc._id, { parentType: document.TYPE_CONTEST });
|
||||
while (await cursor.hasNext()) {
|
||||
const doc = await cursor.next();
|
||||
const tdoc = await document.coll.findOne({ docType: doc.parentType, docId: doc.parentId });
|
||||
if (!tdoc) await discussion.del(ddoc._id, doc.docId);
|
||||
}
|
||||
});
|
||||
},
|
||||
null,
|
||||
async function _43_44() {
|
||||
const processer = (i) => {
|
||||
i.status = i.accept ? STATUS.STATUS_ACCEPTED : STATUS.STATUS_WRONG_ANSWER;
|
||||
return i;
|
||||
};
|
||||
return await iterateAllDomain(async (ddoc) => {
|
||||
const tdocs = await contest.getMulti(ddoc._id, { rule: { $ne: 'acm' } }).toArray();
|
||||
for (const tdoc of tdocs) {
|
||||
const tsdocs = await contest.getMultiStatus(ddoc._id, { docId: tdoc.docId }).toArray();
|
||||
for (const tsdoc of tsdocs) {
|
||||
const $set: any = {};
|
||||
if (tsdoc.journal?.length) $set.journal = tsdoc.journal.map(processer);
|
||||
if (tsdoc.detail?.length) $set.detail = tsdoc.detail.map(processer);
|
||||
if (Object.keys($set).length) {
|
||||
await contest.setStatus(ddoc._id, tdoc.docId, tsdoc.uid, $set);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
async function _44_45() {
|
||||
return await iterateAllUser((udoc) => user.setById(udoc._id, { ip: [udoc.regip] }, { regip: '' }));
|
||||
},
|
||||
async function _45_46() {
|
||||
return await iterateAllDomain(async (ddoc) => {
|
||||
const ddocs = await discussion.getMulti(ddoc._id, {
|
||||
docType: document.TYPE_DISCUSSION,
|
||||
parentType: { $in: [document.TYPE_CONTEST, 60, document.TYPE_TRAINING] },
|
||||
parentId: { $type: 'string' },
|
||||
}).toArray();
|
||||
for (const doc of ddocs) {
|
||||
if (ObjectId.isValid(doc.parentId)) {
|
||||
await document.set(ddoc._id, document.TYPE_DISCUSSION, doc.docId, { parentId: new ObjectId(doc.parentId) });
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
null,
|
||||
async function _47_48() {
|
||||
await document.coll.updateMany({ docType: document.TYPE_HOMEWORK }, { $set: { docType: document.TYPE_CONTEST } });
|
||||
await document.collStatus.updateMany({ docType: document.TYPE_HOMEWORK }, { $set: { docType: document.TYPE_CONTEST } });
|
||||
await RecordModel.coll.deleteMany({ 'contest.tid': { $ne: null }, hidden: true });
|
||||
await RecordModel.coll.updateMany({}, { $unset: { hidden: '' } });
|
||||
await RecordModel.coll.updateMany({ 'contest.tid': { $exists: true } }, { $rename: { 'contest.tid': 'contest1' } });
|
||||
await RecordModel.coll.updateMany({ contest1: { $exists: true } }, { $rename: { contest1: 'contest' } });
|
||||
await RecordModel.coll.updateMany({ contest: null }, { $unset: { contest: '' } });
|
||||
return true;
|
||||
},
|
||||
...new Array(47).fill(unsupportedUpgrade), // oxlint-disable-line no-new-array
|
||||
async function _48_49() {
|
||||
await RecordModel.coll.updateMany({ input: { $exists: true } }, { $set: { contest: new ObjectId('000000000000000000000000') } });
|
||||
return true;
|
||||
|
||||
@@ -19,7 +19,7 @@ class ImportHojHandler extends Handler {
|
||||
}
|
||||
const tmp = path.resolve(tmpdir, String.random(32));
|
||||
await new Promise((resolve, reject) => {
|
||||
zip.extractAllToAsync(tmp, true, (err) => (err ? reject(err) : resolve(null)));
|
||||
zip.extractAllToAsync(tmp, true, false, (err) => (err ? reject(err) : resolve(null)));
|
||||
});
|
||||
let cnt = 0;
|
||||
try {
|
||||
|
||||
@@ -52,7 +52,7 @@ class ImportQduojHandler extends Handler {
|
||||
}
|
||||
const tmp = path.resolve(tmpdir, String.random(32));
|
||||
await new Promise((resolve, reject) => {
|
||||
zip.extractAllToAsync(tmp, true, (err) => (err ? reject(err) : resolve(null)));
|
||||
zip.extractAllToAsync(tmp, true, false, (err) => (err ? reject(err) : resolve(null)));
|
||||
});
|
||||
let cnt = 0;
|
||||
try {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { CustomSelectAutoComplete as CustomSelectAutoCompleteFC } from '@hydrooj/components';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import AutoComplete, { AutoCompleteOptions } from '.';
|
||||
|
||||
interface CustomSelectOptions {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import AutoComplete from '.';
|
||||
import DomainSelectAutoCompleteFC from './components/DomainSelectAutoComplete';
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import AutoComplete, { AutoCompleteOptions } from '.';
|
||||
import FileSelectAutoCompleteFC from './components/FileSelectAutoComplete';
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import AutoComplete from '.';
|
||||
import ProblemSelectAutoCompleteFC from './components/ProblemSelectAutoComplete';
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import AutoComplete, { AutoCompleteOptions } from '.';
|
||||
import UserSelectAutoCompleteFC from './components/UserSelectAutoComplete';
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@blueprintjs/core": "^5.17.6",
|
||||
"@cordisjs/core": "4.0.0-alpha.7",
|
||||
"@cordisjs/core": "4.0.0-alpha.8",
|
||||
"@fontsource/dm-mono": "^5.2.5",
|
||||
"@fontsource/fira-code": "^5.2.5",
|
||||
"@fontsource/inconsolata": "^5.2.5",
|
||||
@@ -23,7 +23,7 @@
|
||||
"@fontsource/ubuntu-mono": "^5.2.5",
|
||||
"@hydrooj/utils": "workspace:^",
|
||||
"@sentry/browser": "^9.10.0",
|
||||
"@sentry/webpack-plugin": "^3.2.3",
|
||||
"@sentry/webpack-plugin": "^3.2.4",
|
||||
"@simplewebauthn/browser": "13.1.0",
|
||||
"@svgr/webpack": "^8.1.0",
|
||||
"@types/html-to-text": "^9.0.4",
|
||||
@@ -35,7 +35,7 @@
|
||||
"@types/nunjucks": "^3.2.6",
|
||||
"@types/pickadate": "^3.5.35",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@types/react": "^18.3.19",
|
||||
"@types/react": "^18.3.20",
|
||||
"@types/react-dom": "^18.3.5",
|
||||
"@types/redux-logger": "^3.0.13",
|
||||
"@types/uuid": "^10.0.0",
|
||||
@@ -52,7 +52,7 @@
|
||||
"copy-webpack-plugin": "^13.0.0",
|
||||
"core-js": "^3.41.0",
|
||||
"core-js-compat": "^3.41.0",
|
||||
"css-loader": "^6.11.0",
|
||||
"css-loader": "^7.1.2",
|
||||
"diff": "^7.0.0",
|
||||
"diff-dom": "^5.1.4",
|
||||
"echarts": "^5.6.0",
|
||||
|
||||
Reference in new issue
Block a user