import http = require('http'); import { value } from '@artisan-framework/core'; import { detectErrorMessage, detectErrorStatus, isProd } from '../utils'; import { WebContext, WebProviderConfig, WEB_PROVIDER_CONFIG_KEY } from '../web-protocol'; import { WebErrorHandler } from './error-protocol'; import escapeHtml = require('escape-html'); const isDev = !isProd(); const HTML_ERROR = isDev ? ` Error - {{status}}

Error

Looks like something broke!

        
{{stack}}
        
      
` : ` Error - {{status}}

Error

Looks like something broke!

`; export class ArtisanWebErrorHandler implements WebErrorHandler { @value({ el: WEB_PROVIDER_CONFIG_KEY, default: {} }) private _config: WebProviderConfig; canHandle(): boolean { return true; } handle(err: any, ctx: WebContext): void { ctx.status = detectErrorStatus(err); ctx.set(err.headers); const _accepts = ctx.accepts('html', 'text', 'json'); const type = typeof _accepts === 'string' ? _accepts : 'text'; let message = http.STATUS_CODES[ctx.status]; if (isDev || err.expose) { const msg = detectErrorMessage(ctx, err); message = msg || message; } // redirect if (['html', 'text'].includes(type) && this._config.onError?.errorPage) { const _errorPage = this._config.onError?.errorPage; const errorPage = typeof _errorPage === 'function' ? _errorPage(err, ctx) : _errorPage; ctx.redirect(errorPage); } else { // setType ctx.type = type; if (type === 'html') { ctx.body = HTML_ERROR.replace('{{status}}', escapeHtml(`${ctx.status}`)).replace( '{{stack}}', escapeHtml(err.stack), ); } else if (type === 'text') { // unset all headers, and set those specified if (typeof ctx.res.getHeaderNames === 'function') { for (const name of ctx.res.getHeaderNames()) { ctx.res.removeHeader(name); } } else { (ctx.res as any)._headers = {}; } ctx.set(err.headers); ctx.body = message; } else { if (isDev) { ctx.body = JSON.stringify({ message, error: err.stack, }); } else { ctx.body = JSON.stringify({ message, }); } } } ctx.res.end(ctx.body); } }