-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathindex.js
More file actions
159 lines (138 loc) · 5.93 KB
/
index.js
File metadata and controls
159 lines (138 loc) · 5.93 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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
'use strict';
var Hoek = require('@hapi/hoek');
var pkg = require('../package.json'); // require package.json for attributes
/**
* isFunction checks if a given value is a function.
* @param {Object} functionToCheck - the object we want to confirm is a function
* @returns {Boolean} true|false
*/
function isFunction(functionToCheck) {
const toString = Object.prototype.toString;
return functionToCheck
&& toString.call(functionToCheck) === '[object Function]'
|| toString.call(functionToCheck) === '[object AsyncFunction]';
}
/**
* Merges in custom options to teh default config for each status code
* @param {Object} config - the custom option object with status codes as keys
* and objects with settings as values
* @returns {Object} config to be used in plugin with defaults overwritten
* and or added to
*/
function createConfig (config) {
var mergedConfig = {
templateName: 'error_template',
statusCodes: {
401: { message: 'Please Login to view that page' },
400: { message: 'Sorry, we do not have that page.' },
404: { message: 'Sorry, that page is not available.' }
}
};
// Target status code configuration objects.
var statusCodes = config.statusCodes || config; // Backwards compatibility.
// Configure error template name.
mergedConfig.templateName = config.templateName || mergedConfig.templateName;
Object.keys(statusCodes).forEach(function (statusCode) {
if (!mergedConfig.statusCodes[statusCode]) {
mergedConfig.statusCodes[statusCode] = {};
}
// Configure status code settings.
Object.keys(statusCodes[statusCode]).forEach(function (setting) {
mergedConfig.statusCodes[statusCode][setting]
= statusCodes[statusCode][setting];
});
});
return mergedConfig;
};
/**
* Takes an error Object and Message and throws Hoek Error if not null
* @param {String} error - error Object or null
* @param {String|Object} [errorMessage] - Optional error message String/Object
* @returns {Boolean} false.
*/
function handleError (error, errorMessage) {
if (errorMessage) {
return Hoek.assert(!error, errorMessage);
}
return Hoek.assert(!error, error);
};
// export for use in files that do not have access to the request object
exports.handleError = handleError; // e.g. database-specific getters/setters
/**
* register defines our errorHandler plugin following the standard hapi plugin
* @param {Object} server - the server instance where the plugin is being used
* @param {Object} options - any configuration options passed into the plugin
* @returns {Function} reply.continue is called when the plugin is finished
*/
exports.plugin = {
pkg: pkg,
register: async function (server, options) {
// creates config for handler to be used in 'onPreResponse' function
var config = createConfig(options);
// make handleError available on request
server.ext('onRequest', function (request, reply) {
request.handleError = handleError; // github.com/dwyl/hapi-error/issues/23
return reply.continue;
});
// onPreResponse intercepts ALL errors
server.ext('onPreResponse', function (request, reply) {
var res = request.response;
var req = request.raw.req;
var msg = 'Sorry, something went wrong, please retrace your steps.';
var statusCode = 200; // default to "success"
var accept = request.raw.req.headers.accept;
var debug; // defined here to keep JSLint Happy.
if (res.isBoom) {
statusCode = res.output.payload.statusCode;
debug = {
method: req.method, // e.g GET/POST
url: request.url.path, // the path the person requested
headers: request.raw.req.headers, // all HTTP Headers
info: request.info, // all additional request info (useful to debug)
auth: request.auth, // any authentication details e.g. decoded JWT
payload: request.payload, // the complete request payload received
response: res.output.payload, // response before error intercepted
stackTrace: res.stack // the stack trace of the error
};
// ALWAYS Log the error
server.log('error', debug); // github.com/dwyl/hapi-error/issues/22
// Header check, should take priority
if (accept && accept.match(/json/)) { // support REST/JSON requests
return reply.response(res.output.payload).code(statusCode);
}
// custom redirect https://github.com/dwyl/hapi-error/issues/5
var currentCodeConfig = config.statusCodes[statusCode];
if (currentCodeConfig && currentCodeConfig.redirect) {
// if redirect is function invoke it with the request object
if (isFunction(currentCodeConfig.redirect)) {
const url = currentCodeConfig.redirect(request)
return reply.redirect(url);
}
else {
// if parameter is string, append redirect query
var redirectString = request.url.pathname + request.url.search;
return reply.redirect(currentCodeConfig.redirect + '?redirect=' + redirectString);
}
}
if (currentCodeConfig && currentCodeConfig.message) {
msg = isFunction(currentCodeConfig.message)
? currentCodeConfig.message(msg, request)
: currentCodeConfig.message
;
}
res = Object.assign(debug, {
errorTitle: res.output.payload.error,
statusCode: statusCode,
errorMessage: msg
});
// next avoids TypeError if view rendering is not used in app e.g API!
// see: https://github.com/dwyl/hapi-error/issues/49
if (!reply.view) {
return reply.response(res).code(statusCode);
}
return reply.view(config.templateName, res).code(statusCode); // e.g 401
}; // end if (res.isBoom)
return reply.continue; // continue processing the request
});
}
};