| | import type { NextFunction, Response } from 'express' |
| |
|
| | import FailBot from '../lib/failbot' |
| | import { nextApp } from '@/frame/middleware/next' |
| | import { setFastlySurrogateKey, SURROGATE_ENUMS } from '@/frame/middleware/set-fastly-surrogate-key' |
| | import { errorCacheControl } from '@/frame/middleware/cache-control' |
| | import statsd from '@/observability/lib/statsd' |
| | import { ExtendedRequest } from '@/types' |
| |
|
| | const DEBUG_MIDDLEWARE_TESTS = Boolean(JSON.parse(process.env.DEBUG_MIDDLEWARE_TESTS || 'false')) |
| |
|
| | type ErrorWithCode = Error & { |
| | code: string |
| | statusCode?: number |
| | status?: string |
| | } |
| |
|
| | function shouldLogException(error: ErrorWithCode) { |
| | const IGNORED_ERRORS = [ |
| | |
| | 'ECONNRESET', |
| | ] |
| |
|
| | if (IGNORED_ERRORS.includes(error.code)) { |
| | return false |
| | } |
| |
|
| | |
| | return true |
| | } |
| |
|
| | async function logException(error: ErrorWithCode, req: ExtendedRequest) { |
| | if (process.env.NODE_ENV !== 'test' && shouldLogException(error)) { |
| | await FailBot.report(error, { |
| | path: req.path, |
| | url: req.url, |
| | }) |
| | } |
| | } |
| |
|
| | function timedOut(req: ExtendedRequest) { |
| | |
| | |
| | |
| | |
| | const incrementTags = [`path:${req.pagePath || req.path}`] |
| | if (req.context?.currentCategory) { |
| | incrementTags.push(`product:${req.context.currentCategory}`) |
| | } |
| | statsd.increment('middleware.timeout', 1, incrementTags) |
| | } |
| |
|
| | async function handleError( |
| | error: ErrorWithCode | number, |
| | req: ExtendedRequest, |
| | res: Response, |
| | next: NextFunction, |
| | ) { |
| | |
| | if (req.timedout) { |
| | timedOut(req) |
| | } |
| |
|
| | const responseDone = res.headersSent || req.aborted |
| |
|
| | if (req.path.startsWith('/assets') || req.path.startsWith('/_next/static')) { |
| | if (!responseDone) { |
| | |
| | |
| | |
| | |
| | |
| | |
| | errorCacheControl(res) |
| | |
| | |
| | |
| | setFastlySurrogateKey(res, SURROGATE_ENUMS.DEFAULT) |
| | } |
| | } else if (DEBUG_MIDDLEWARE_TESTS) { |
| | console.warn('An error occurred in some middleware handler', error) |
| | } |
| |
|
| | try { |
| | |
| | if (responseDone) { |
| | |
| | if (typeof error !== 'number') { |
| | await logException(error, req) |
| | } |
| |
|
| | |
| | next(error) |
| | return |
| | } |
| |
|
| | if (!req.context) { |
| | req.context = {} |
| | } |
| |
|
| | |
| | if (error === 404) { |
| | |
| | req.url = '/404' |
| | res.status(404) |
| | res.setHeader('x-pathname', req.path) |
| | res.locals = res.locals || {} |
| | res.locals.handledByAppRouter = true |
| | return nextApp.getRequestHandler()(req, res) |
| | } |
| | if (typeof error === 'number') { |
| | throw new Error("Don't use next(xxx) where xxx is any other number than 404") |
| | } |
| |
|
| | |
| | if (!process.env.MODA_PROD_SERVICE_ENV) { |
| | req.context.error = error |
| | } |
| |
|
| | |
| | |
| | if (error.statusCode) { |
| | res.sendStatus(error.statusCode) |
| | return |
| | } |
| |
|
| | res.statusCode = 500 |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | if (process.env.NODE_ENV === 'development') { |
| | next(error) |
| | return |
| | } else { |
| | nextApp.renderError(error, req, res, req.path) |
| |
|
| | |
| | await logException(error, req) |
| | } |
| | } catch (handlingError) { |
| | console.error('An error occurred in the error handling middleware!', handlingError) |
| | next(handlingError) |
| | return |
| | } |
| | } |
| |
|
| | export default handleError |
| |
|