From 9d64824cd3fca3e0e223a0008c68fdd16fa0ff57 Mon Sep 17 00:00:00 2001 From: undefined Date: Wed, 5 Mar 2025 12:25:42 +0800 Subject: [PATCH] core: server: use handler mixin --- framework/framework/server.ts | 59 +++++++++++++--- packages/hydrooj/src/service/server.ts | 97 +++++++++++--------------- 2 files changed, 92 insertions(+), 64 deletions(-) diff --git a/framework/framework/server.ts b/framework/framework/server.ts index 502ae4715..e84c6f216 100644 --- a/framework/framework/server.ts +++ b/framework/framework/server.ts @@ -717,12 +717,12 @@ ${c.response.status} ${endTime - startTime}ms ${c.response.length}`); } private registerLayer(name: 'serverLayers' | 'handlerLayers' | 'wsLayers', layer: any) { - this[name].push(layer); - const dispose = () => { - this[name] = this[name].filter((i) => i !== layer); - }; - this[CordisContext.origin]?.on('dispose', dispose); - return dispose; + this.ctx.effect(() => { + this[name].push(layer); + return () => { + this[name] = this[name].filter((i) => i !== layer); + }; + }); } public addServerLayer(name: string, func: any) { @@ -747,9 +747,50 @@ ${c.response.status} ${endTime - startTime}ms ${c.response.length}`); } public handlerMixin(MixinClass: Partial>) { - for (const val of Object.getOwnPropertyNames(MixinClass)) { - HandlerCommon.prototype[val] = MixinClass[val]; - } + this.ctx.effect(() => { + for (const val of Object.getOwnPropertyNames(MixinClass)) { + if (HandlerCommon.prototype[val]) { + logger.warn('HandlerCommon.prototype[%s] already exists.', val); + } + HandlerCommon.prototype[val] = MixinClass[val]; + } + return () => { + for (const val of Object.getOwnPropertyNames(MixinClass)) { + delete HandlerCommon.prototype[val]; + } + }; + }); + } + + public httpHandlerMixin(MixinClass: Partial>) { + this.ctx.effect(() => { + for (const val of Object.getOwnPropertyNames(MixinClass)) { + if (Handler.prototype[val]) { + logger.warn('Handler.prototype[%s] already exists.', val); + } + Handler.prototype[val] = MixinClass[val]; + } + return () => { + for (const val of Object.getOwnPropertyNames(MixinClass)) { + delete Handler.prototype[val]; + } + }; + }); + } + + public wsHandlerMixin(MixinClass: Partial>) { + this.ctx.effect(() => { + for (const val of Object.getOwnPropertyNames(MixinClass)) { + if (ConnectionHandler.prototype[val]) { + logger.warn('ConnectionHandler.prototype[%s] already exists.', val); + } + } + return () => { + for (const val of Object.getOwnPropertyNames(MixinClass)) { + delete ConnectionHandler.prototype[val]; + } + }; + }); } public registerRenderer(name: string, func: Renderer) { diff --git a/packages/hydrooj/src/service/server.ts b/packages/hydrooj/src/service/server.ts index 7a67d30eb..0dbd17307 100644 --- a/packages/hydrooj/src/service/server.ts +++ b/packages/hydrooj/src/service/server.ts @@ -73,62 +73,11 @@ export function requireSudo(target: any, funcName: string, obj: any) { return obj; } -export interface Handler { - domain: DomainDoc; -} export class Handler extends HandlerOriginal { - constructor(_, ctx) { - super(_, ctx); - this.ctx = ctx.extend({ domain: this.domain }); - this.renderHTML = ((orig) => function (name: string, args: Record) { - const s = name.split('.'); - let templateName = `${s[0]}.${args.domainId}.${s[1]}`; - if (!global.Hydro.ui.template[templateName]) templateName = name; - return orig(templateName, args); - })(this.renderHTML).bind(this); - } - - async onerror(error: HydroError) { - error.msg ||= () => error.message; - if (error instanceof UserFacingError && !process.env.DEV) error.stack = ''; - if (!(error instanceof NotFoundError) && !('nolog' in error)) { - // eslint-disable-next-line max-len - logger.error(`User: ${this.user._id}(${this.user.uname}) ${this.request.method}: /d/${this.domain._id}${this.request.path}`, error.msg(), error.params); - if (error.stack) logger.error(error.stack); - } - if (this.user?._id === 0 && (error instanceof PermissionError || error instanceof PrivilegeError)) { - this.response.redirect = this.url('user_login', { - query: { - redirect: (this.context.originalPath || this.request.path) + this.context.search, - }, - }); - } else { - this.response.status = error instanceof UserFacingError ? error.code : 500; - this.response.template = error instanceof UserFacingError ? 'error.html' : 'bsod.html'; - this.response.body = { - UserFacingError, - error: { message: error.msg(), params: error.params, stack: errorMessage(error.stack || '') }, - }; - } - } -} - -export interface ConnectionHandler { domain: DomainDoc; } export class ConnectionHandler extends ConnectionHandlerOriginal { - constructor(_, ctx) { - super(_, ctx); - this.ctx = ctx.extend({ domain: this.domain }); - } - - onerror(err: HydroError) { - if (![NotFoundError, PrivilegeError, PermissionError].some((i) => err instanceof i) || this.user?._id !== 0) { - logger.error(`Path:${this.request.path}, User:${this.user?._id}(${this.user?.uname})`); - logger.error(err); - } - super.onerror(err); - } + domain: DomainDoc; } export async function apply(ctx: Context) { @@ -270,15 +219,53 @@ export async function apply(ctx: Context) { return `${this.translate(str)} - ${name}`; }, }); + server.httpHandlerMixin({ + async onerror(error: HydroError) { + error.msg ||= () => error.message; + if (error instanceof UserFacingError && !process.env.DEV) error.stack = ''; + if (!(error instanceof NotFoundError) && !('nolog' in error)) { + // eslint-disable-next-line max-len + logger.error(`User: ${this.user._id}(${this.user.uname}) ${this.request.method}: /d/${this.domain._id}${this.request.path}`, error.msg(), error.params); + if (error.stack) logger.error(error.stack); + } + if (this.user?._id === 0 && (error instanceof PermissionError || error instanceof PrivilegeError)) { + this.response.redirect = this.url('user_login', { + query: { + redirect: (this.context.originalPath || this.request.path) + this.context.search, + }, + }); + } else { + this.response.status = error instanceof UserFacingError ? error.code : 500; + this.response.template = error instanceof UserFacingError ? 'error.html' : 'bsod.html'; + this.response.body = { + UserFacingError, + error: { message: error.msg(), params: error.params, stack: errorMessage(error.stack || '') }, + }; + } + }, + }); + server.wsHandlerMixin({ + async onerror(err: HydroError) { + if (![NotFoundError, PrivilegeError, PermissionError].some((i) => err instanceof i) || this.user?._id !== 0) { + logger.error(`Path:${this.request.path}, User:${this.user?._id}(${this.user?.uname})`); + logger.error(err); + } + await super.onerror(err); + }, + }); on('handler/create', async (h) => { h.user = h.context.HydroContext.user as any; h.domain = h.context.HydroContext.domain as any; h.translate = h.translate.bind(h); h.url = h.url.bind(h); - h.ctx = h.ctx.extend({ - domain: h.domain, - }); + h.ctx = h.ctx.extend({ domain: h.domain }); + h.renderHTML = ((orig) => function (name: string, args: Record) { + const s = name.split('.'); + let templateName = `${s[0]}.${args.domainId}.${s[1]}`; + if (!global.Hydro.ui.template[templateName]) templateName = name; + return orig(templateName, args); + })(h.renderHTML).bind(h); }); on('handler/create/http', async (h) => { if (!argv.options.benchmark && !h.notUsage) await h.limitRate('global', 5, 100);