import http from 'node:http'; import fs from 'node:fs'; import { onerror, type OnerrorOptions, type OnerrorError } from 'koa-onerror'; import type { ILifecycleBoot, EggCore, Context } from '@eggjs/core'; import { ErrorView } from './lib/error_view.js'; import { isProd, detectStatus, detectErrorMessage, accepts } from './lib/utils.js'; import type { OnerrorConfig } from './config/config.default.js'; export interface OnerrorErrorWithCode extends OnerrorError { code?: string; type?: string; errors?: any[]; } export default class Boot implements ILifecycleBoot { constructor(private app: EggCore) {} async didLoad() { // logging error const config = this.app.config.onerror; const viewTemplate = fs.readFileSync(config.templatePath, 'utf8'); const app = this.app; app.on('error', (err, ctx) => { if (!ctx) { ctx = app.currentContext || app.createAnonymousContext(); } if (config.appErrorFilter && !config.appErrorFilter(err, ctx)) return; const status = detectStatus(err); // 5xx if (status >= 500) { try { ctx.logger.error(err); } catch (ex) { app.logger.error(err); app.logger.error(ex); } return; } // 4xx try { ctx.logger.warn(err); } catch (ex) { app.logger.warn(err); app.logger.error(ex); } }); const errorOptions: OnerrorOptions = { // support customize accepts function accepts(this: Context) { const fn = config.accepts || accepts; return fn(this as any); }, html(err, ctx: Context) { const status = detectStatus(err); const errorPageUrl = typeof config.errorPageUrl === 'function' ? config.errorPageUrl(err, ctx) : config.errorPageUrl; // keep the real response status ctx.realStatus = status; // don't respond any error message in production env if (isProd(app)) { // 5xx if (status >= 500) { if (errorPageUrl) { const statusQuery = (errorPageUrl.indexOf('?') > 0 ? '&' : '?') + `real_status=${status}`; return ctx.redirect(errorPageUrl + statusQuery); } ctx.status = 500; ctx.body = `