mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
core&ui: remove deprecated apis
This commit is contained in:
26 files changed
+90
-249
No files matched your search
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user