From f6a9dd35574b0be081313d2ab2593fc45bc9cf15 Mon Sep 17 00:00:00 2001 From: undefined Date: Sat, 25 Oct 2025 23:43:12 +0800 Subject: [PATCH] core: use serviceInstance for system model --- packages/hydrooj/src/entry/cli.ts | 5 +- packages/hydrooj/src/entry/common.ts | 4 +- packages/hydrooj/src/entry/worker.ts | 6 +- packages/hydrooj/src/handler/connection.ts | 2 +- packages/hydrooj/src/handler/domain.ts | 2 +- packages/hydrooj/src/handler/home.ts | 2 +- packages/hydrooj/src/handler/homework.ts | 2 +- packages/hydrooj/src/handler/judge.ts | 2 +- packages/hydrooj/src/handler/manage.ts | 2 +- packages/hydrooj/src/handler/misc.ts | 2 +- packages/hydrooj/src/handler/problem.ts | 2 +- packages/hydrooj/src/handler/record.ts | 2 +- packages/hydrooj/src/handler/training.ts | 2 +- packages/hydrooj/src/handler/user.ts | 2 +- packages/hydrooj/src/interface.ts | 2 +- packages/hydrooj/src/model/message.ts | 2 +- packages/hydrooj/src/model/problem.ts | 2 +- packages/hydrooj/src/model/record.ts | 2 +- packages/hydrooj/src/model/storage.ts | 2 +- packages/hydrooj/src/model/system.ts | 139 +++++++++--------- packages/hydrooj/src/model/user.ts | 2 +- packages/hydrooj/src/plugin-api.ts | 2 +- packages/hydrooj/src/script/checkUpdate.ts | 2 +- packages/hydrooj/src/service/check.ts | 2 +- packages/hydrooj/src/service/layers/base.ts | 2 +- packages/hydrooj/src/service/layers/domain.ts | 2 +- packages/hydrooj/src/service/migration.ts | 2 +- packages/hydrooj/src/service/server.ts | 2 +- packages/hydrooj/src/upgrade.ts | 2 +- packages/hydrooj/src/utils.ts | 43 ++++++ 30 files changed, 143 insertions(+), 104 deletions(-) diff --git a/packages/hydrooj/src/entry/cli.ts b/packages/hydrooj/src/entry/cli.ts index cf771cf64..6137fd070 100644 --- a/packages/hydrooj/src/entry/cli.ts +++ b/packages/hydrooj/src/entry/cli.ts @@ -4,6 +4,7 @@ import cac from 'cac'; import fs from 'fs-extra'; import { ObjectId } from 'mongodb'; import { Context } from '../context'; +import SystemModel from '../model/system'; import { load as loadOptions } from '../options'; import { MongoService } from '../service/db'; import { SettingService } from '../settings'; @@ -111,9 +112,9 @@ export async function load(ctx: Context) { const fail = []; await ctx.plugin(MongoService, loadOptions() || {}); await ctx.plugin(SettingService); - await require('../model/system').runConfig(); + await ctx.plugin(SystemModel.Service); ctx = await new Promise((resolve) => { - ctx.inject(['loader', 'setting', 'db'], (c) => { + ctx.inject(['loader', 'setting', 'db', 'model:system'], (c) => { resolve(c); }); }); diff --git a/packages/hydrooj/src/entry/common.ts b/packages/hydrooj/src/entry/common.ts index 66d958478..c4bcbdc63 100644 --- a/packages/hydrooj/src/entry/common.ts +++ b/packages/hydrooj/src/entry/common.ts @@ -52,7 +52,9 @@ export async function builtinModel(ctx: Context) { for (const t of models.filter((i) => i.endsWith('.ts'))) { const q = path.resolve(modelDir, t); const module = require(q); - if ('apply' in module || isClass(unwrapExports(module))) ctx.loader.reloadPlugin(q, ''); + if ('apply' in module) ctx.loader.reloadPlugin(q, ''); + const exports = unwrapExports(module); + if (isClass(exports) && !(Symbol.for('hydro.initialize') in exports)) ctx.loader.reloadPlugin(q, ''); } } diff --git a/packages/hydrooj/src/entry/worker.ts b/packages/hydrooj/src/entry/worker.ts index da48331e6..c53069bca 100644 --- a/packages/hydrooj/src/entry/worker.ts +++ b/packages/hydrooj/src/entry/worker.ts @@ -4,6 +4,7 @@ import cac from 'cac'; import fs from 'fs-extra'; import { Context } from '../context'; import { Logger } from '../logger'; +import SystemModel from '../model/system'; import { load } from '../options'; import { MongoService } from '../service/db'; import { SettingService } from '../settings'; @@ -30,10 +31,9 @@ export async function apply(ctx: Context) { await locale(pending, fail); await ctx.plugin(MongoService, load() || {}); await ctx.plugin(SettingService); - const modelSystem = require('../model/system'); - await modelSystem.runConfig(); + await ctx.plugin(SystemModel.Service); ctx = await new Promise((resolve) => { - ctx.inject(['loader', 'setting', 'db'], (c) => { + ctx.inject(['loader', 'setting', 'db', 'model:system'], (c) => { resolve(c); }); }); diff --git a/packages/hydrooj/src/handler/connection.ts b/packages/hydrooj/src/handler/connection.ts index 1074468d3..807d29b8b 100644 --- a/packages/hydrooj/src/handler/connection.ts +++ b/packages/hydrooj/src/handler/connection.ts @@ -1,6 +1,6 @@ import { Context } from '../context'; import { ForbiddenError } from '../error'; -import * as SystemModel from '../model/system'; +import SystemModel from '../model/system'; import TokenModel from '../model/token'; import UserModel from '../model/user'; import { ConnectionHandler } from '../service/server'; diff --git a/packages/hydrooj/src/handler/domain.ts b/packages/hydrooj/src/handler/domain.ts index 70266b4fc..92880bd8f 100644 --- a/packages/hydrooj/src/handler/domain.ts +++ b/packages/hydrooj/src/handler/domain.ts @@ -15,7 +15,7 @@ import domain from '../model/domain'; import MessageModel from '../model/message'; import * as oplog from '../model/oplog'; import { DOMAIN_SETTINGS, DOMAIN_SETTINGS_BY_KEY } from '../model/setting'; -import * as system from '../model/system'; +import system from '../model/system'; import user from '../model/user'; import { Handler, Mutation, param, post, Query, query, requireSudo, Types, diff --git a/packages/hydrooj/src/handler/home.ts b/packages/hydrooj/src/handler/home.ts index 4ee54e1ee..8f735a849 100644 --- a/packages/hydrooj/src/handler/home.ts +++ b/packages/hydrooj/src/handler/home.ts @@ -24,7 +24,7 @@ import message from '../model/message'; import ProblemModel from '../model/problem'; import * as setting from '../model/setting'; import storage from '../model/storage'; -import * as system from '../model/system'; +import system from '../model/system'; import token from '../model/token'; import * as training from '../model/training'; import user from '../model/user'; diff --git a/packages/hydrooj/src/handler/homework.ts b/packages/hydrooj/src/handler/homework.ts index 12fffcb30..6c66b5d40 100644 --- a/packages/hydrooj/src/handler/homework.ts +++ b/packages/hydrooj/src/handler/homework.ts @@ -13,7 +13,7 @@ import * as discussion from '../model/discussion'; import problem from '../model/problem'; import record from '../model/record'; import storage from '../model/storage'; -import * as system from '../model/system'; +import system from '../model/system'; import user from '../model/user'; import { Handler, param, post, Types, diff --git a/packages/hydrooj/src/handler/judge.ts b/packages/hydrooj/src/handler/judge.ts index 937f2e8a6..68cc559f9 100644 --- a/packages/hydrooj/src/handler/judge.ts +++ b/packages/hydrooj/src/handler/judge.ts @@ -21,7 +21,7 @@ import problem from '../model/problem'; import record from '../model/record'; import * as setting from '../model/setting'; import storage from '../model/storage'; -import * as system from '../model/system'; +import system from '../model/system'; import task, { Consumer } from '../model/task'; import user from '../model/user'; import bus from '../service/bus'; diff --git a/packages/hydrooj/src/handler/manage.ts b/packages/hydrooj/src/handler/manage.ts index 8e6f7909e..b5774998b 100644 --- a/packages/hydrooj/src/handler/manage.ts +++ b/packages/hydrooj/src/handler/manage.ts @@ -11,7 +11,7 @@ import { PRIV, STATUS } from '../model/builtin'; import domain from '../model/domain'; import record from '../model/record'; import * as setting from '../model/setting'; -import * as system from '../model/system'; +import system from '../model/system'; import user from '../model/user'; import { ConnectionHandler, Handler, param, requireSudo, Types, diff --git a/packages/hydrooj/src/handler/misc.ts b/packages/hydrooj/src/handler/misc.ts index 327ac4dfb..b458b983f 100644 --- a/packages/hydrooj/src/handler/misc.ts +++ b/packages/hydrooj/src/handler/misc.ts @@ -9,7 +9,7 @@ import { import { PRIV } from '../model/builtin'; import * as oplog from '../model/oplog'; import storage from '../model/storage'; -import * as system from '../model/system'; +import system from '../model/system'; import user, { User } from '../model/user'; import { Handler, param, post, requireSudo, Types, diff --git a/packages/hydrooj/src/handler/problem.ts b/packages/hydrooj/src/handler/problem.ts index 540a3c493..50d95b7be 100644 --- a/packages/hydrooj/src/handler/problem.ts +++ b/packages/hydrooj/src/handler/problem.ts @@ -32,7 +32,7 @@ import record from '../model/record'; import * as setting from '../model/setting'; import solution from '../model/solution'; import storage from '../model/storage'; -import * as system from '../model/system'; +import system from '../model/system'; import user from '../model/user'; import { Handler, param, post, Query, query, route, Types, diff --git a/packages/hydrooj/src/handler/record.ts b/packages/hydrooj/src/handler/record.ts index e05605bc5..cdd67dc06 100644 --- a/packages/hydrooj/src/handler/record.ts +++ b/packages/hydrooj/src/handler/record.ts @@ -14,7 +14,7 @@ import problem, { ProblemDoc } from '../model/problem'; import record from '../model/record'; import { langs } from '../model/setting'; import storage from '../model/storage'; -import * as system from '../model/system'; +import system from '../model/system'; import TaskModel from '../model/task'; import user from '../model/user'; import { diff --git a/packages/hydrooj/src/handler/training.ts b/packages/hydrooj/src/handler/training.ts index cde9aff5e..223a408fc 100644 --- a/packages/hydrooj/src/handler/training.ts +++ b/packages/hydrooj/src/handler/training.ts @@ -10,7 +10,7 @@ import { PERM, PRIV, STATUS } from '../model/builtin'; import * as oplog from '../model/oplog'; import problem from '../model/problem'; import storage from '../model/storage'; -import * as system from '../model/system'; +import system from '../model/system'; import * as training from '../model/training'; import user from '../model/user'; import { diff --git a/packages/hydrooj/src/handler/user.ts b/packages/hydrooj/src/handler/user.ts index 86ab26d22..9d5cb0a3f 100644 --- a/packages/hydrooj/src/handler/user.ts +++ b/packages/hydrooj/src/handler/user.ts @@ -23,7 +23,7 @@ import * as oplog from '../model/oplog'; import problem, { ProblemDoc } from '../model/problem'; import ScheduleModel from '../model/schedule'; import SolutionModel from '../model/solution'; -import * as system from '../model/system'; +import system from '../model/system'; import token from '../model/token'; import user, { deleteUserCache } from '../model/user'; import { diff --git a/packages/hydrooj/src/interface.ts b/packages/hydrooj/src/interface.ts index 77cd6d7eb..ab12ae17c 100644 --- a/packages/hydrooj/src/interface.ts +++ b/packages/hydrooj/src/interface.ts @@ -588,7 +588,7 @@ export interface Model { record: typeof import('./model/record').default; setting: typeof import('./model/setting'); solution: typeof import('./model/solution').default; - system: typeof import('./model/system'); + system: typeof import('./model/system').default; task: typeof import('./model/task').default; schedule: typeof import('./model/schedule').default; oplog: typeof import('./model/oplog'); diff --git a/packages/hydrooj/src/model/message.ts b/packages/hydrooj/src/model/message.ts index 6fdf332bf..2ffcd4d68 100644 --- a/packages/hydrooj/src/model/message.ts +++ b/packages/hydrooj/src/model/message.ts @@ -4,7 +4,7 @@ import bus from '../service/bus'; import db from '../service/db'; import { ArgMethod } from '../utils'; import { PRIV } from './builtin'; -import * as system from './system'; +import system from './system'; import user from './user'; class MessageModel { diff --git a/packages/hydrooj/src/model/problem.ts b/packages/hydrooj/src/model/problem.ts index 1aaebabe4..0f96f495f 100644 --- a/packages/hydrooj/src/model/problem.ts +++ b/packages/hydrooj/src/model/problem.ts @@ -30,7 +30,7 @@ import DomainModel from './domain'; import RecordModel from './record'; import SolutionModel from './solution'; import storage from './storage'; -import * as SystemModel from './system'; +import SystemModel from './system'; export interface ProblemDoc extends Document { } export type Field = keyof ProblemDoc; diff --git a/packages/hydrooj/src/model/record.ts b/packages/hydrooj/src/model/record.ts index 2e7cf0e56..5c2a33b55 100644 --- a/packages/hydrooj/src/model/record.ts +++ b/packages/hydrooj/src/model/record.ts @@ -14,7 +14,7 @@ import { ArgMethod, buildProjection, Time } from '../utils'; import { STATUS } from './builtin'; import DomainModel from './domain'; import problem from './problem'; -import * as SystemModel from './system'; +import SystemModel from './system'; import task from './task'; export default class RecordModel { diff --git a/packages/hydrooj/src/model/storage.ts b/packages/hydrooj/src/model/storage.ts index 0455ab6a4..a07ae614e 100644 --- a/packages/hydrooj/src/model/storage.ts +++ b/packages/hydrooj/src/model/storage.ts @@ -9,7 +9,7 @@ import mime from '../lib/mime'; import db from '../service/db'; import storage from '../service/storage'; import ScheduleModel from './schedule'; -import * as system from './system'; +import system from './system'; export class StorageModel { static coll = db.collection('storage'); diff --git a/packages/hydrooj/src/model/system.ts b/packages/hydrooj/src/model/system.ts index 735c2e955..89df10ccd 100644 --- a/packages/hydrooj/src/model/system.ts +++ b/packages/hydrooj/src/model/system.ts @@ -1,81 +1,74 @@ +import { Context, Service } from '../context'; import { SystemKeys } from '../interface'; -import bus from '../service/bus'; -import db from '../service/db'; -import { NumberKeys } from '../typeutils'; +import { serviceInstance } from '../utils'; import { SYSTEM_SETTINGS } from './setting'; -const coll = db.collection('system'); -const cache: Record = Object.create(null); +class SystemModelService extends Service { + static readonly inject = ['db']; + static readonly name = 'model:system'; -export function get(key: K): SystemKeys[K]; -export function get(key: string): any; -export function get(key: string): any { - return cache[key]; -} + coll = this.ctx.db.collection('system'); + cache: Record = Object.create(null); -export function getMany< - A extends keyof SystemKeys, B extends keyof SystemKeys, ->(keys: [A, B]): [SystemKeys[A], SystemKeys[B]]; -export function getMany< - A extends keyof SystemKeys, B extends keyof SystemKeys, C extends keyof SystemKeys, ->(keys: [A, B, C]): [SystemKeys[A], SystemKeys[B], SystemKeys[C]]; -export function getMany< - A extends keyof SystemKeys, B extends keyof SystemKeys, C extends keyof SystemKeys, - D extends keyof SystemKeys, ->(keys: [A, B, C, D]): [SystemKeys[A], SystemKeys[B], SystemKeys[C], SystemKeys[D]]; -export function getMany< - A extends keyof SystemKeys, B extends keyof SystemKeys, C extends keyof SystemKeys, - D extends keyof SystemKeys, E extends keyof SystemKeys, ->(keys: [A, B, C, D, E]): [SystemKeys[A], SystemKeys[B], SystemKeys[C], SystemKeys[D], SystemKeys[E]]; -export function getMany< - A extends keyof SystemKeys, B extends keyof SystemKeys, C extends keyof SystemKeys, - D extends keyof SystemKeys, E extends keyof SystemKeys, F extends keyof SystemKeys, ->(keys: [A, B, C, D, E, F]): [SystemKeys[A], SystemKeys[B], SystemKeys[C], SystemKeys[D], SystemKeys[E], SystemKeys[F]]; -export function getMany(keys: (keyof SystemKeys)[]): any[]; -export function getMany(keys: string[]): any[] { - return keys.map((key) => cache[key]); -} - -export async function set(_id: K, value: SystemKeys[K], broadcast?: boolean): Promise; -export async function set(_id: string, value: K, broadcast?: boolean): Promise; -export async function set(_id: string, value: any, broadcast = true) { - if (broadcast) bus.broadcast('system/setting', { [_id]: value }); - const res = await coll.findOneAndUpdate( - { _id }, - { $set: { value } }, - { upsert: true, returnDocument: 'after' }, - ); - cache[_id] = res.value; - return res.value; -} - -export async function inc>(_id: K) { - const res = await coll.findOneAndUpdate( - { _id }, - { $inc: { value: 1 } as any }, - { upsert: true, returnDocument: 'after' }, - ); - cache[_id] = res.value; - return res.value; -} - -export async function runConfig() { - for (const setting of SYSTEM_SETTINGS) { - if (setting.value) cache[setting.key] = setting.value; + constructor(ctx: Context) { + super(ctx, 'model:system'); + } + + get(key: K): SystemKeys[K]; + get(key: string): any; + get(key: string): any { + return this.cache[key]; + } + + getMany< + A extends keyof SystemKeys, B extends keyof SystemKeys, + >(keys: [A, B]): [SystemKeys[A], SystemKeys[B]]; + getMany< + A extends keyof SystemKeys, B extends keyof SystemKeys, C extends keyof SystemKeys, + >(keys: [A, B, C]): [SystemKeys[A], SystemKeys[B], SystemKeys[C]]; + getMany< + A extends keyof SystemKeys, B extends keyof SystemKeys, C extends keyof SystemKeys, + D extends keyof SystemKeys, + >(keys: [A, B, C, D]): [SystemKeys[A], SystemKeys[B], SystemKeys[C], SystemKeys[D]]; + getMany< + A extends keyof SystemKeys, B extends keyof SystemKeys, C extends keyof SystemKeys, + D extends keyof SystemKeys, E extends keyof SystemKeys, + >(keys: [A, B, C, D, E]): [SystemKeys[A], SystemKeys[B], SystemKeys[C], SystemKeys[D], SystemKeys[E]]; + getMany< + A extends keyof SystemKeys, B extends keyof SystemKeys, C extends keyof SystemKeys, + D extends keyof SystemKeys, E extends keyof SystemKeys, F extends keyof SystemKeys, + >(keys: [A, B, C, D, E, F]): [SystemKeys[A], SystemKeys[B], SystemKeys[C], SystemKeys[D], SystemKeys[E], SystemKeys[F]]; + getMany(keys: (keyof SystemKeys)[]): any[]; + getMany(keys: string[]): any[] { + return keys.map((key) => this.cache[key]); + } + + async set(_id: K, value: SystemKeys[K], broadcast?: boolean): Promise; + async set(_id: string, value: K, broadcast?: boolean): Promise; + async set(_id: string, value: any, broadcast = true) { + if (broadcast) this.ctx.broadcast('system/setting', { [_id]: value }); + const res = await this.coll.findOneAndUpdate( + { _id }, + { $set: { value } }, + { upsert: true, returnDocument: 'after' }, + ); + this.cache[_id] = res.value; + return res.value; + } + + async [Service.init]() { + for (const setting of SYSTEM_SETTINGS) { + if (setting.value) this.cache[setting.key] = setting.value; + } + const config = await this.coll.find().toArray(); + for (const i of config) this.cache[i._id] = i.value; + this.ctx.emit('database/config'); + return this.ctx.on('system/setting', (args) => { + for (const key in args) this.cache[key] = args[key]; + }); } - const config = await coll.find().toArray(); - for (const i of config) cache[i._id] = i.value; - await bus.emit('database/config'); } -bus.on('system/setting', (args) => { - for (const key in args) cache[key] = args[key]; -}); - -global.Hydro.model.system = { - runConfig, - get, - getMany, - inc, - set, -}; +const SystemModel = serviceInstance(SystemModelService); +export default SystemModel; +global.Hydro.model.system = SystemModel; diff --git a/packages/hydrooj/src/model/user.ts b/packages/hydrooj/src/model/user.ts index eae2aa125..6fac72d5d 100644 --- a/packages/hydrooj/src/model/user.ts +++ b/packages/hydrooj/src/model/user.ts @@ -16,7 +16,7 @@ import { ArgMethod, buildProjection, randomstring, sleep } from '../utils'; import { PERM, PRIV } from './builtin'; import domain from './domain'; import * as setting from './setting'; -import * as system from './system'; +import system from './system'; import token from './token'; export const coll: Collection = db.collection('user'); diff --git a/packages/hydrooj/src/plugin-api.ts b/packages/hydrooj/src/plugin-api.ts index a55c455a5..1ab4805b4 100644 --- a/packages/hydrooj/src/plugin-api.ts +++ b/packages/hydrooj/src/plugin-api.ts @@ -14,7 +14,7 @@ export * from './pipelineUtils'; export * from './error'; export * from './libs'; export * from './settings'; -export * as SystemModel from './model/system'; +export { default as SystemModel } from './model/system'; export * as TrainingModel from './model/training'; export * as OpcountModel from './model/opcount'; export * as OplogModel from './model/oplog'; diff --git a/packages/hydrooj/src/script/checkUpdate.ts b/packages/hydrooj/src/script/checkUpdate.ts index 034c84ae1..fbf10c49c 100644 --- a/packages/hydrooj/src/script/checkUpdate.ts +++ b/packages/hydrooj/src/script/checkUpdate.ts @@ -3,7 +3,7 @@ import Schema from 'schemastery'; import * as semver from 'semver'; import superagent from 'superagent'; import MessageModel from '../model/message'; -import * as system from '../model/system'; +import system from '../model/system'; async function getRemoteVersion(id: string) { try { diff --git a/packages/hydrooj/src/service/check.ts b/packages/hydrooj/src/service/check.ts index 002eacffc..525b52134 100644 --- a/packages/hydrooj/src/service/check.ts +++ b/packages/hydrooj/src/service/check.ts @@ -5,7 +5,7 @@ import { DisposableList } from 'cordis'; import inspector from 'inspector'; import { randomstring } from '@hydrooj/utils'; import { Context, Service } from '../context'; -import * as system from '../model/system'; +import system from '../model/system'; import db from './db'; import storage from './storage'; diff --git a/packages/hydrooj/src/service/layers/base.ts b/packages/hydrooj/src/service/layers/base.ts index 7484f4c09..c666213f8 100644 --- a/packages/hydrooj/src/service/layers/base.ts +++ b/packages/hydrooj/src/service/layers/base.ts @@ -3,7 +3,7 @@ import { cloneDeep, omit } from 'lodash'; import type { KoaContext } from '@hydrooj/framework'; import { randomPick, Time } from '@hydrooj/utils'; import { PERM } from '../../model/builtin'; -import * as system from '../../model/system'; +import system from '../../model/system'; import token from '../../model/token'; export interface UiContextBase { diff --git a/packages/hydrooj/src/service/layers/domain.ts b/packages/hydrooj/src/service/layers/domain.ts index a9f1c92ae..17b52a433 100644 --- a/packages/hydrooj/src/service/layers/domain.ts +++ b/packages/hydrooj/src/service/layers/domain.ts @@ -1,7 +1,7 @@ import { KoaContext, NotFoundError } from '@hydrooj/framework'; import BlackListModel from '../../model/blacklist'; import DomainModel from '../../model/domain'; -import * as system from '../../model/system'; +import system from '../../model/system'; export default async (ctx: KoaContext, next) => { const forceDomain = /^\/d\/([^/]+)\//.exec(ctx.request.path); diff --git a/packages/hydrooj/src/service/migration.ts b/packages/hydrooj/src/service/migration.ts index b923323bf..c6ac42fec 100644 --- a/packages/hydrooj/src/service/migration.ts +++ b/packages/hydrooj/src/service/migration.ts @@ -2,7 +2,7 @@ import cac from 'cac'; import { Logger } from '@hydrooj/utils'; import { Context, Service } from '../context'; -import * as system from '../model/system'; +import system from '../model/system'; const argv = cac().parse(); diff --git a/packages/hydrooj/src/service/server.ts b/packages/hydrooj/src/service/server.ts index 01b6499c9..79159f924 100644 --- a/packages/hydrooj/src/service/server.ts +++ b/packages/hydrooj/src/service/server.ts @@ -16,7 +16,7 @@ import { Logger } from '../logger'; import { PERM, PRIV } from '../model/builtin'; import * as opcount from '../model/opcount'; import * as OplogModel from '../model/oplog'; -import * as system from '../model/system'; +import system from '../model/system'; import db from './db'; import baseLayer from './layers/base'; import domainLayer from './layers/domain'; diff --git a/packages/hydrooj/src/upgrade.ts b/packages/hydrooj/src/upgrade.ts index 34ba84f31..56be3bd0b 100644 --- a/packages/hydrooj/src/upgrade.ts +++ b/packages/hydrooj/src/upgrade.ts @@ -17,7 +17,7 @@ import problem from './model/problem'; import RecordModel from './model/record'; import ScheduleModel from './model/schedule'; import StorageModel from './model/storage'; -import * as system from './model/system'; +import system from './model/system'; import TaskModel from './model/task'; import * as training from './model/training'; import user, { handleMailLower } from './model/user'; diff --git a/packages/hydrooj/src/utils.ts b/packages/hydrooj/src/utils.ts index c58c9badd..dba096e8e 100644 --- a/packages/hydrooj/src/utils.ts +++ b/packages/hydrooj/src/utils.ts @@ -1,4 +1,5 @@ import { isNullable } from 'cosmokit'; +import { Service } from './context'; export function buildProjection(fields: readonly T[]): Record { const o: Record = Object.create(null); @@ -28,4 +29,46 @@ export function unwrapExports(module: any) { return module.default ?? module; } +// EXPERIMENTAL: This function is subject to change +// Don't try to understand this wrapper +export function serviceInstance any>(ImplClass: T) { + let instance: any = null; + let state: 'pending' | 'initializing' | 'initialized' = 'pending'; + const onClass: (symbol | string)[] = ['name', 'Config', 'inject', 'prototype']; + const name = ImplClass.name; + + const proxyObj = new Proxy(ImplClass, { + get(_, key) { + if (key === 'Service') return proxyObj; + if (Reflect.has(ImplClass, key) || onClass.includes(key)) return Reflect.get(ImplClass, key); + if (state === 'pending') throw new Error(`${name} is not initialized yet (getting ${String(key)})`); + if (state === 'initializing') throw new Error(`${name} is still initializing (getting ${String(key)})`); + return Reflect.get(instance, key); + }, + has(_, key) { + if (key === Symbol.for('hydro.initialize')) return state !== 'pending'; + return Reflect.has(_, key); + }, + construct(_, args, target) { + if (state !== 'pending') throw new Error(`${name} is already constructed`); + state = 'initializing'; + const originalInit = ImplClass.prototype[Service.init]; + if (originalInit) { + ImplClass.prototype[Service.init] = new Proxy(originalInit, { + apply(...applyArgs) { + const result = Reflect.apply(...applyArgs); + if (result instanceof Promise) result.then(() => { state = 'initialized'; }); + else state = 'initialized'; + return result; + }, + }); + } + instance = Reflect.construct(ImplClass, args, target); + if (!Reflect.has(ImplClass, Service.init)) state = 'initialized'; + return instance; + }, + }) as (T extends (new (...args: any[]) => infer R) ? (R & { Service: (new (...args: any[]) => R) }) : never); + return proxyObj; +} + export * from '@hydrooj/utils/lib/utils';