/** * This file defines error classes based on their semantic meaning. It abstracts away * HTTP status codes so they can be used in a RESTful way without worrying about a * consistent error interface. * * These classes descend from the base Error class, so they also automatically capture * stack traces--useful for debugging. */ import { Request, Response, NextFunction } from 'express'; import { Logger } from '../../Logger'; /** * Base error class. * * Supports HTTP status codes and a custom message. * From the ACM Membership Portal Backend repository */ export class HttpError extends Error { constructor(public status: number, message: string | Error) { super(message.toString()); this.name = this.constructor.name; this.status = status; this.message = message.toString(); } } export class UserError extends HttpError { constructor(message: string | Error) { super(200, message || 'User Error'); } } export class BadRequest extends HttpError { constructor(message: string | Error) { super(400, message || 'Bad Request'); } } export class Unauthorized extends HttpError { constructor(message: string | Error) { super(401, message || 'Unauthorized'); } } export class Forbidden extends HttpError { constructor(message: string | Error) { super(403, message || 'Permission denied'); } } export class NotFound extends HttpError { constructor(message: string | Error) { super(404, message || 'Resource not found'); } } export class Unprocessable extends HttpError { constructor(message: string | Error) { super(422, message || 'Unprocessable request'); } } export class InternalServerError extends HttpError { constructor(message: string | Error) { super(500, message || 'Internal server error'); } } export class NotImplemented extends HttpError { constructor(message: string | Error) { super(501, message || 'Not Implemented'); } } /** * General error handling middleware. Attaches to Express so that throwing or calling next() with * an error ends up here and all errors are handled uniformly. */ export const errorHandler = (log: Logger) => ( err: HttpError, req: Request, res: Response, // eslint-disable-next-line @typescript-eslint/no-unused-vars next: NextFunction ): void => { if (!err) err = new InternalServerError( 'An unknown error occurred in the errorHandler' ); if (!err.status) err = new InternalServerError(err.message); if (err.status >= 500) { log.error(`${err.status}`, err); } else { // otherwise just log the error message at the warning level log.warn(`${err.status}: ${err.message}`); } res.status(err.status).json({ error: { status: err.status, message: `${err.message}`, }, }); };