PageRenderTime 40ms CodeModel.GetById 13ms app.highlight 23ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/handlers/error.js

https://github.com/KobeCena/jsbin
JavaScript | 132 lines | 100 code | 21 blank | 11 comment | 25 complexity | e1e47eb3cc80ecd2cc71204437879b89 MD5 | raw file
  1var utils = require('../utils'),
  2    errors = require('../errors'),
  3    BinHandler = require('./bin'),
  4    Observable = utils.Observable,
  5    crypto = require('crypto');
  6
  7// Handles application errors.
  8module.exports = Observable.extend({
  9  constructor: function ErrorHandler(sandbox) {
 10    Observable.apply(this, arguments);
 11
 12    this.sandbox = sandbox;
 13    this.helpers = sandbox.helpers;
 14    this.mailer = sandbox.mailer;
 15
 16    utils.bindAll(this, 'notFound', 'httpError', 'uncaughtError');
 17  },
 18
 19  // Handles all types of HTTPError and ensures that the correct type of
 20  // response is returned depending on the type of content requested. So if
 21  // you're expecting JSON you should get JSON.
 22  httpError: function (err, req, res, next) {
 23    err = this.coerceError(err);
 24
 25    if (err instanceof errors.NotFound && req.accepts('html')) {
 26      if (err instanceof errors.BinNotFound) {
 27        return (new BinHandler(this.sandbox)).notFound(req, res);
 28      } else {
 29        return this.renderErrorPage(err, req, res);
 30      }
 31    } else if (err instanceof errors.HTTPError) {
 32      return this.renderError(err, req, res);
 33    }
 34
 35    next(err);
 36  },
 37
 38  // Fall through handler for when no routes match.
 39  notFound: function (req, res, next) {
 40    var error = new errors.NotFound('Page Does Not Exist');
 41    if (req.accepts('html')) {
 42      this.renderErrorPage(error, req, res);
 43    } else {
 44      this.renderError(error, req, res);
 45    }
 46  },
 47
 48  // Displays a friendly 500 page in production if requesting html otherwise
 49  // returns an appropriate format.
 50  uncaughtError: function (err, req, res, next) {
 51    this.sendErrorReport(err, req);
 52
 53    if (req.accepts('html')) {
 54      this.renderErrorPage(err, req, res);
 55    } else {
 56      var error = new errors.HTTPError(500, 'Internal Server Error');
 57      this.renderError(error, req, res);
 58    }
 59  },
 60
 61  renderError: function (err, req, res) {
 62    res.status(err.status);
 63
 64    if (req.accepts(['html'])) {
 65      res.contentType('html');
 66      res.send(err.toHTMLString());
 67    } else if (req.accepts(['json'])) {
 68      res.json(err);
 69    } else {
 70      res.contentType('txt');
 71      res.send(err.toString());
 72    }
 73  },
 74
 75  renderErrorPage: function (err, req, res) {
 76    var status = err.status || 500;
 77
 78    res.status(status).render('error', {
 79      is_500: status !== 404, // Saves us having many error pages at the mo.
 80      is_404: status === 404,
 81      root: this.helpers.url('', true),
 82      dave: this.helpers.urlForStatic('/images/logo.png')
 83    });
 84  },
 85
 86  sendErrorReport: function (err, req) {
 87    var to = this.helpers.set('notify errors'),
 88        session = utils.extend({}, req.session),
 89        context, headers;
 90
 91    if (this.helpers.production && to && to.length) {
 92      // Don't send the users email address via email.
 93      if (session && session.user) {
 94        delete session.user.email;
 95      }
 96
 97      headers = utils.extend({}, req.headers);
 98      delete headers.cookie;
 99
100      context = {
101        name: err.name,
102        message: err.message,
103        hash: crypto.createHash('md5').update(err.stack).digest('hex').slice(0, 6),
104        stack: err.stack,
105        body: JSON.stringify(req.body, null, 2),
106        session: JSON.stringify(session, null, 2) || null,
107        url: this.helpers.url(req.url, true),
108        path: req.url,
109        headers: JSON.stringify(headers, null, 2) || null,
110        method: req.method
111      };
112
113      if (context.body === '{}') {
114        context.body = null;
115      }
116
117      this.mailer.errorReport(to, context);
118    }
119  },
120
121  // Checks to see if the error has a status property and if so converts
122  // it into an instance of HTTPError. Just returns this original error
123  // if no status is found.
124  coerceError: function (err) {
125    var status = typeof err === 'number' ? err : err.status;
126
127    if (!(err instanceof errors.HTTPError) && status) {
128      return errors.create(status, err.message);
129    }
130    return err;
131  }
132});