import {RequestError, StatusCode, UnauthorizedError} from './'; import {IErrorMiddleware, IHttpRequest, IHttpResponse} from '.'; import {ErrorRequestHandler} from "express"; export interface IErrorResult { error: string; code: number; name: string; data?: T; } export interface GlobalErrorLogger { (err: Error, req: IHttpRequest, user?: any): void; } export interface GlobalErrorLoggerFactory { (url: string): GlobalErrorLogger; } export class ErrorHandlerMiddleware implements IErrorMiddleware { constructor( private readonly errorLogger?: GlobalErrorLogger ) { } public async handle(err: Error & {statusCode?: number}, req: IHttpRequest & {user?: unknown}, res: IHttpResponse): Promise { if (res.headersSent) { this.errorLogger?.(err, req, (req as any).user); return; } const { message, statusCode, name, ...data} = err; let errorResult: IErrorResult = { error: message, code: statusCode ?? StatusCode.ServerError, name, data, }; if (err instanceof UnauthorizedError) { errorResult.error = `Not authorized`; errorResult.data = undefined; } else if (err instanceof RequestError && err.statusCode === StatusCode.ServerError) { errorResult = { error: process.env.NODE_ENV !== 'production' ? `${name ?? 'Error'}: ${message}` : 'Server error', code: StatusCode.ServerError, name: 'Error', } } if (errorResult.code == StatusCode.ServerError) { this.errorLogger?.(err, req, req.user); } if (req.accepts(['text', 'application/json']) === 'application/json') { res .status(errorResult.code) .json(errorResult); return; } res .status(errorResult.code) .type('text/html') .send(`

${errorResult.code}


${errorResult.error}

`); } } // noinspection JSUnusedGlobalSymbols export function errorHandlerFactory(globalErrorLoggerFactory?: GlobalErrorLoggerFactory): ErrorRequestHandler { return (err, req, res, next) => { new ErrorHandlerMiddleware(globalErrorLoggerFactory?.(req.url)) .handle(err, req as any, res as any) .then(() => next()) .catch(next); } }