-
-
Notifications
You must be signed in to change notification settings - Fork 282
Expand file tree
/
Copy patherror-handler.ts
More file actions
115 lines (105 loc) · 3.29 KB
/
error-handler.ts
File metadata and controls
115 lines (105 loc) · 3.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import { FastifyError } from '@fastify/error'
import { ErrorCode, isRenderableError, StorageBackendError, StorageError } from '@internal/errors'
import { FastifyInstance } from 'fastify'
import { DatabaseError } from 'pg'
/**
* The global error handler for all the uncaught exceptions within a request.
* We try our best to display meaningful information to our users
* and log any error that occurs
* @param app
* @param options
*/
export const setErrorHandler = (
app: FastifyInstance,
options?: {
respectStatusCode?: boolean
formatter?: (error: StorageError) => unknown
}
) => {
app.setErrorHandler<Error>(function (error, request, reply) {
const formatter = options?.formatter || ((e) => e)
// We assign the error received.
// it will be logged in the request log plugin
request.executionError = error
// database error
if (
error instanceof DatabaseError &&
[
'Authentication error', // supavisor specific
'Max client connections reached',
'remaining connection slots are reserved for non-replication superuser connections',
'no more connections allowed',
'sorry, too many clients already',
'server login has been failing, try again later',
].some((msg) => (error as DatabaseError).message.includes(msg))
) {
return reply.status(429).send(
formatter({
statusCode: `429`,
error: 'too_many_connections',
code: ErrorCode.SlowDown,
message: 'Too many connections issued to the database',
})
)
}
if (isRenderableError(error)) {
const renderableError = error.render()
const statusCode = options?.respectStatusCode
? parseInt(renderableError.statusCode, 10)
: error.userStatusCode
? error.userStatusCode
: renderableError.statusCode === '500'
? 500
: 400
if (
renderableError.code === ErrorCode.AbortedTerminate ||
(error instanceof StorageBackendError && error.shouldCloseConnection())
) {
reply.header('Connection', 'close')
reply.raw.once('finish', () => {
setTimeout(() => {
if (!request.raw.closed) {
request.raw.destroy()
}
}, 3000)
})
}
return reply.status(statusCode).send(
formatter({
...renderableError,
error: error.error || renderableError.code,
})
)
}
// Fastify errors
if ('statusCode' in error) {
const err = error as FastifyError
if (err.code === 'FST_ERR_CTP_INVALID_MEDIA_TYPE') {
return reply.status(400).send(
formatter({
statusCode: '415',
code: ErrorCode.InvalidMimeType,
error: 'invalid_mime_type',
message: 'Invalid Content-Type header',
})
)
}
return reply.status(err.statusCode || 500).send(
formatter({
statusCode: `${err.statusCode}`,
error: err.name,
code: ErrorCode.InternalError,
message: err.message,
})
)
}
return reply.status(500).send(
formatter({
statusCode: '500',
error: 'Internal',
message: 'Internal Server Error',
code: ErrorCode.InternalError,
})
)
})
}