UNPKG

3.48 kBJavaScriptView Raw
1const errors = require('@feathersjs/errors');
2const path = require('path');
3
4const defaults = {
5 public: path.resolve(__dirname, 'public'),
6 logger: console
7};
8const defaultHtmlError = path.resolve(defaults.public, 'default.html');
9
10module.exports = function (options = {}) {
11 options = Object.assign({}, defaults, options);
12
13 if (typeof options.html === 'undefined') {
14 options.html = {
15 401: path.resolve(options.public, '401.html'),
16 404: path.resolve(options.public, '404.html'),
17 default: defaultHtmlError
18 };
19 }
20
21 if (typeof options.json === 'undefined') {
22 options.json = {};
23 }
24
25 return function (error, req, res, next) {
26 // Set the error code for HTTP processing semantics
27 error.code = !isNaN(parseInt(error.code, 10)) ? parseInt(error.code, 10) : 500;
28
29 // Log the error if it didn't come from a service method call
30 if (options.logger && typeof options.logger.error === 'function' && !res.hook) {
31 if (error.code >= 500) {
32 options.logger.error(error);
33 } else {
34 options.logger.info(error);
35 }
36 }
37
38 if (error.type !== 'FeathersError') {
39 let oldError = error;
40 error = new errors.GeneralError(oldError.message, {
41 errors: oldError.errors
42 });
43
44 if (oldError.stack) {
45 error.stack = oldError.stack;
46 }
47 }
48
49 const formatter = {};
50
51 // If the developer passed a custom function for ALL html errors
52 if (typeof options.html === 'function') {
53 formatter['text/html'] = options.html;
54 } else {
55 let file = options.html[error.code];
56 if (!file) {
57 file = options.html.default || defaultHtmlError;
58 }
59 // If the developer passed a custom function for individual html errors
60 if (typeof file === 'function') {
61 formatter['text/html'] = file;
62 } else {
63 formatter['text/html'] = function () {
64 res.set('Content-Type', 'text/html');
65 res.sendFile(file);
66 };
67 }
68 }
69
70 // If the developer passed a custom function for ALL json errors
71 if (typeof options.json === 'function') {
72 formatter['application/json'] = options.json;
73 } else {
74 let handler = options.json[error.code] || options.json.default;
75 // If the developer passed a custom function for individual json errors
76 if (typeof handler === 'function') {
77 formatter['application/json'] = handler;
78 } else {
79 // Don't show stack trace if it is a 404 error
80 if (error.code === 404) {
81 error.stack = null;
82 }
83
84 formatter['application/json'] = function () {
85 let output = Object.assign({}, error.toJSON());
86
87 if (process.env.NODE_ENV === 'production') {
88 delete output.stack;
89 }
90
91 res.set('Content-Type', 'application/json');
92 res.json(output);
93 };
94 }
95 }
96
97 res.status(error.code);
98
99 const contentType = req.headers['content-type'] || '';
100 const accepts = req.headers.accept || '';
101
102 // by default just send back json
103 if (contentType.indexOf('json') !== -1 || accepts.indexOf('json') !== -1) {
104 formatter['application/json'](error, req, res, next);
105 } else if (options.html && (contentType.indexOf('html') !== -1 || accepts.indexOf('html') !== -1)) {
106 formatter['text/html'](error, req, res, next);
107 } else {
108 // TODO (EK): Maybe just return plain text
109 formatter['application/json'](error, req, res, next);
110 }
111 };
112};