mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-19 17:38:19 +08:00
upgrade to cordis v3 (#733)
Co-authored-by: panda <panda_dtdyy@outlook.com>
This commit is contained in:
1 parent
498cd57016
commit
a56b1650be
33 files changed
+301
-262
No files matched your search
@@ -1,11 +1,10 @@
|
||||
import moment from 'moment-timezone';
|
||||
import { Filter, ObjectId } from 'mongodb';
|
||||
import { sleep } from '@hydrooj/utils/lib/utils';
|
||||
import { Context, Service } from '../context';
|
||||
import { Context } from '../context';
|
||||
import { Schedule } from '../interface';
|
||||
import { Logger } from '../logger';
|
||||
import * as bus from '../service/bus';
|
||||
import db from '../service/db';
|
||||
import type { WorkerService } from '../service/worker';
|
||||
import RecordModel from './record';
|
||||
|
||||
const logger = new Logger('model/schedule');
|
||||
@@ -27,73 +26,6 @@ async function getFirst(query: Filter<Schedule>) {
|
||||
return null;
|
||||
}
|
||||
|
||||
class Consumer {
|
||||
consuming: boolean;
|
||||
running?: any;
|
||||
|
||||
constructor(public filter: any, public func: Function, public destroyOnError = true) {
|
||||
this.consuming = true;
|
||||
this.consume();
|
||||
bus.on('app/exit', this.destroy);
|
||||
}
|
||||
|
||||
async consume() {
|
||||
while (this.consuming) {
|
||||
try {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const res = await getFirst(this.filter);
|
||||
if (res) {
|
||||
this.running = res;
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await this.func(res);
|
||||
this.running = null;
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
} else await sleep(1000);
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
if (this.destroyOnError) this.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async destroy() {
|
||||
this.consuming = false;
|
||||
}
|
||||
}
|
||||
|
||||
class WorkerService extends Service {
|
||||
private handlers: Record<string, Function> = {};
|
||||
public consumer = new Consumer(
|
||||
{ type: 'schedule', subType: { $in: Object.keys(this.handlers) } },
|
||||
async (doc) => {
|
||||
try {
|
||||
logger.debug('Worker task: %o', doc);
|
||||
const start = Date.now();
|
||||
await Promise.race([
|
||||
this.handlers[doc.subType](doc),
|
||||
sleep(1200000),
|
||||
]);
|
||||
const spent = Date.now() - start;
|
||||
if (spent > 500) logger.warn('Slow worker task (%d ms): %o', spent, doc);
|
||||
} catch (e) {
|
||||
logger.error('Worker task fail: ', e);
|
||||
logger.error('%o', doc);
|
||||
}
|
||||
},
|
||||
false,
|
||||
);
|
||||
|
||||
public addHandler(type: string, handler: Function) {
|
||||
this.handlers[type] = handler;
|
||||
this.consumer.filter = { type: 'schedule', subType: { $in: Object.keys(this.handlers) } };
|
||||
this.caller?.on('dispose', () => {
|
||||
delete this.handlers[type];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const Worker = new WorkerService(app, 'worker', false);
|
||||
|
||||
class ScheduleModel {
|
||||
static coll = coll;
|
||||
|
||||
@@ -123,28 +55,24 @@ class ScheduleModel {
|
||||
}
|
||||
|
||||
static getFirst = getFirst;
|
||||
static Worker = Worker;
|
||||
}
|
||||
|
||||
declare module '../context' {
|
||||
interface Context {
|
||||
worker: WorkerService;
|
||||
}
|
||||
/** @deprecated use ctx.inject(['worker'], cb) instead */
|
||||
static Worker: WorkerService;
|
||||
}
|
||||
|
||||
export async function apply(ctx: Context) {
|
||||
Context.service('worker', WorkerService);
|
||||
ctx.worker = Worker;
|
||||
|
||||
Worker.addHandler('task.daily', async () => {
|
||||
await RecordModel.coll.deleteMany({ contest: { $in: [RecordModel.RECORD_PRETEST, RecordModel.RECORD_GENERATE] } });
|
||||
await global.Hydro.script.rp?.run({}, new Logger('task/rp').debug);
|
||||
await global.Hydro.script.problemStat?.run({}, new Logger('task/problem').debug);
|
||||
if (global.Hydro.model.system.get('server.checkUpdate') && !(new Date().getDay() % 3)) {
|
||||
await global.Hydro.script.checkUpdate?.run({}, new Logger('task/checkUpdate').debug);
|
||||
}
|
||||
await ctx.parallel('task/daily');
|
||||
ctx.inject(['worker'], (c) => {
|
||||
ScheduleModel.Worker = c.worker;
|
||||
c.worker.addHandler('task.daily', async () => {
|
||||
await RecordModel.coll.deleteMany({ contest: { $in: [RecordModel.RECORD_PRETEST, RecordModel.RECORD_GENERATE] } });
|
||||
await global.Hydro.script.rp?.run({}, new Logger('task/rp').debug);
|
||||
await global.Hydro.script.problemStat?.run({}, new Logger('task/problem').debug);
|
||||
if (global.Hydro.model.system.get('server.checkUpdate') && !(new Date().getDay() % 3)) {
|
||||
await global.Hydro.script.checkUpdate?.run({}, new Logger('task/checkUpdate').debug);
|
||||
}
|
||||
await ctx.parallel('task/daily');
|
||||
});
|
||||
});
|
||||
|
||||
ctx.on('domain/delete', (domainId) => coll.deleteMany({ domainId }));
|
||||
|
||||
if (process.env.NODE_APP_INSTANCE !== '0') return;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable max-len */
|
||||
/* eslint-disable no-await-in-loop */
|
||||
import cordis from 'cordis';
|
||||
import * as cordis from 'cordis';
|
||||
import yaml from 'js-yaml';
|
||||
import { Dictionary } from 'lodash';
|
||||
import moment from 'moment-timezone';
|
||||
@@ -300,11 +300,10 @@ declare module '../context' {
|
||||
const T = <F extends (...args: any[]) => any>(origFunc: F, disposeFunc?) =>
|
||||
function method(this: cordis.Service, ...args: Parameters<F>) {
|
||||
const res = origFunc(...args);
|
||||
this.caller?.on('dispose', () => (disposeFunc ? disposeFunc(res) : res()));
|
||||
this[Context.current]?.on('dispose', () => (disposeFunc ? disposeFunc(res) : res()));
|
||||
};
|
||||
|
||||
export class SettingService extends Service {
|
||||
static readonly methods = ['PreferenceSetting', 'AccountSetting', 'DomainSetting', 'DomainUserSetting', 'SystemSetting'];
|
||||
PreferenceSetting = T(PreferenceSetting);
|
||||
AccountSetting = T(AccountSetting);
|
||||
DomainSetting = T(DomainSetting);
|
||||
@@ -312,16 +311,16 @@ export class SettingService extends Service {
|
||||
SystemSetting = T(SystemSetting);
|
||||
constructor(ctx: Context) {
|
||||
super(ctx, 'setting', true);
|
||||
ctx.mixin('setting', ['PreferenceSetting', 'AccountSetting', 'DomainSetting', 'DomainUserSetting', 'SystemSetting']);
|
||||
}
|
||||
|
||||
get(key: string) {
|
||||
return (this.caller ? this.caller.domain?.config?.[key.replace(/\./g, '$')] : null) ?? global.Hydro.model.system.get(key);
|
||||
return (this[Context.current] ? this[Context.current].domain?.config?.[key.replace(/\./g, '$')] : null) ?? global.Hydro.model.system.get(key);
|
||||
}
|
||||
}
|
||||
|
||||
export async function apply(ctx: Context) {
|
||||
Context.service('setting', SettingService);
|
||||
ctx.setting = new SettingService(ctx);
|
||||
ctx.plugin(SettingService);
|
||||
logger.info('Ensuring settings');
|
||||
const system = global.Hydro.model.system;
|
||||
for (const setting of SYSTEM_SETTINGS) {
|
||||
|
||||
@@ -3,8 +3,8 @@ import { escapeRegExp } from 'lodash';
|
||||
import moment from 'moment-timezone';
|
||||
import { nanoid } from 'nanoid';
|
||||
import type { Readable } from 'stream';
|
||||
import { Context } from '../context';
|
||||
import mime from '../lib/mime';
|
||||
import * as bus from '../service/bus';
|
||||
import db from '../service/db';
|
||||
import storage from '../service/storage';
|
||||
import ScheduleModel from './schedule';
|
||||
@@ -135,26 +135,35 @@ async function cleanFiles() {
|
||||
res = await StorageModel.coll.findOneAndDelete({ autoDelete: { $lte: new Date() } });
|
||||
}
|
||||
}
|
||||
ScheduleModel.Worker.addHandler('storage.prune', cleanFiles);
|
||||
bus.on('ready', async () => {
|
||||
if (process.env.NODE_APP_INSTANCE !== '0') return;
|
||||
await db.ensureIndexes(
|
||||
StorageModel.coll,
|
||||
{ key: { path: 1, autoDelete: 1 }, sparse: true, name: 'autoDelete' },
|
||||
);
|
||||
if (!await ScheduleModel.count({ type: 'schedule', subType: 'storage.prune' })) {
|
||||
await ScheduleModel.add({
|
||||
type: 'schedule',
|
||||
subType: 'storage.prune',
|
||||
executeAfter: moment().startOf('hour').toDate(),
|
||||
interval: [1, 'hour'],
|
||||
});
|
||||
}
|
||||
});
|
||||
bus.on('domain/delete', async (domainId) => {
|
||||
const files = await StorageModel.list(`problem/${domainId}`);
|
||||
await StorageModel.del(files.map((i) => i.path));
|
||||
});
|
||||
|
||||
export function apply(ctx: Context) {
|
||||
ctx.inject(['worker'], (c) => {
|
||||
c.worker.addHandler('storage.prune', cleanFiles);
|
||||
});
|
||||
ctx.on('domain/delete', async (domainId) => {
|
||||
const [problemFiles, contestFiles, trainingFiles] = await Promise.all([
|
||||
StorageModel.list(`problem/${domainId}`),
|
||||
StorageModel.list(`contest/${domainId}`),
|
||||
StorageModel.list(`training/${domainId}`),
|
||||
]);
|
||||
await StorageModel.del(problemFiles.concat(contestFiles).concat(trainingFiles).map((i) => i.path));
|
||||
});
|
||||
ctx.on('ready', async () => {
|
||||
if (process.env.NODE_APP_INSTANCE !== '0') return;
|
||||
await db.ensureIndexes(
|
||||
StorageModel.coll,
|
||||
{ key: { path: 1, autoDelete: 1 }, sparse: true, name: 'autoDelete' },
|
||||
);
|
||||
if (!await ScheduleModel.count({ type: 'schedule', subType: 'storage.prune' })) {
|
||||
await ScheduleModel.add({
|
||||
type: 'schedule',
|
||||
subType: 'storage.prune',
|
||||
executeAfter: moment().startOf('hour').toDate(),
|
||||
interval: [1, 'hour'],
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
global.Hydro.model.storage = StorageModel;
|
||||
export default StorageModel;
|
||||
Reference in new issue
Block a user