This app provides monitoring and information features for the common freifunk user and the technical stuff of a freifunk community.
Code base is taken from a TUM Practical Course project and added here to see if Freifunk Altdorf can use it.
https://www.freifunk-altdorf.de
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
198 lines
4.5 KiB
198 lines
4.5 KiB
/*! |
|
* errorhandler |
|
* Copyright(c) 2010 Sencha Inc. |
|
* Copyright(c) 2011 TJ Holowaychuk |
|
* Copyright(c) 2014 Jonathan Ong |
|
* Copyright(c) 2014-2015 Douglas Christopher Wilson |
|
* MIT Licensed |
|
*/ |
|
|
|
'use strict' |
|
|
|
/** |
|
* Module dependencies. |
|
* @private |
|
*/ |
|
|
|
var accepts = require('accepts') |
|
var escapeHtml = require('escape-html') |
|
var fs = require('fs') |
|
var path = require('path') |
|
var util = require('util') |
|
|
|
/** |
|
* Module variables. |
|
* @private |
|
*/ |
|
|
|
var DOUBLE_SPACE_REGEXP = /\x20{2}/g |
|
var NEW_LINE_REGEXP = /\n/g |
|
var STYLESHEET = fs.readFileSync(path.join(__dirname, '/public/style.css'), 'utf8') |
|
var TEMPLATE = fs.readFileSync(path.join(__dirname, '/public/error.html'), 'utf8') |
|
var inspect = util.inspect |
|
var toString = Object.prototype.toString |
|
|
|
/* istanbul ignore next */ |
|
var defer = typeof setImmediate === 'function' |
|
? setImmediate |
|
: function (fn) { process.nextTick(fn.bind.apply(fn, arguments)) } |
|
|
|
/** |
|
* Error handler: |
|
* |
|
* Development error handler, providing stack traces |
|
* and error message responses for requests accepting text, html, |
|
* or json. |
|
* |
|
* Text: |
|
* |
|
* By default, and when _text/plain_ is accepted a simple stack trace |
|
* or error message will be returned. |
|
* |
|
* JSON: |
|
* |
|
* When _application/json_ is accepted, connect will respond with |
|
* an object in the form of `{ "error": error }`. |
|
* |
|
* HTML: |
|
* |
|
* When accepted connect will output a nice html stack trace. |
|
* |
|
* @return {Function} |
|
* @api public |
|
*/ |
|
|
|
exports = module.exports = function errorHandler (options) { |
|
// get environment |
|
var env = process.env.NODE_ENV || 'development' |
|
|
|
// get options |
|
var opts = options || {} |
|
|
|
// get log option |
|
var log = opts.log === undefined |
|
? env !== 'test' |
|
: opts.log |
|
|
|
if (typeof log !== 'function' && typeof log !== 'boolean') { |
|
throw new TypeError('option log must be function or boolean') |
|
} |
|
|
|
// default logging using console.error |
|
if (log === true) { |
|
log = logerror |
|
} |
|
|
|
return function errorHandler (err, req, res, next) { |
|
// respect err.statusCode |
|
if (err.statusCode) { |
|
res.statusCode = err.statusCode |
|
} |
|
|
|
// respect err.status |
|
if (err.status) { |
|
res.statusCode = err.status |
|
} |
|
|
|
// default status code to 500 |
|
if (res.statusCode < 400) { |
|
res.statusCode = 500 |
|
} |
|
|
|
// log the error |
|
var str = stringify(err) |
|
if (log) { |
|
defer(log, err, str, req, res) |
|
} |
|
|
|
// cannot actually respond |
|
if (res._header) { |
|
return req.socket.destroy() |
|
} |
|
|
|
// negotiate |
|
var accept = accepts(req) |
|
var type = accept.type('html', 'json', 'text') |
|
|
|
// Security header for content sniffing |
|
res.setHeader('X-Content-Type-Options', 'nosniff') |
|
|
|
// html |
|
if (type === 'html') { |
|
var isInspect = !err.stack && String(err) === toString.call(err) |
|
var errorHtml = !isInspect |
|
? escapeHtmlBlock(str.split('\n', 1)[0] || 'Error') |
|
: 'Error' |
|
var stack = !isInspect |
|
? String(str).split('\n').slice(1) |
|
: [str] |
|
var stackHtml = stack |
|
.map(function (v) { return '<li>' + escapeHtmlBlock(v) + '</li>' }) |
|
.join('') |
|
var body = TEMPLATE |
|
.replace('{style}', STYLESHEET) |
|
.replace('{stack}', stackHtml) |
|
.replace('{title}', escapeHtml(exports.title)) |
|
.replace('{statusCode}', res.statusCode) |
|
.replace(/\{error\}/g, errorHtml) |
|
res.setHeader('Content-Type', 'text/html; charset=utf-8') |
|
res.end(body) |
|
// json |
|
} else if (type === 'json') { |
|
var error = { message: err.message, stack: err.stack } |
|
for (var prop in err) error[prop] = err[prop] |
|
var json = JSON.stringify({ error: error }, null, 2) |
|
res.setHeader('Content-Type', 'application/json; charset=utf-8') |
|
res.end(json) |
|
// plain text |
|
} else { |
|
res.setHeader('Content-Type', 'text/plain; charset=utf-8') |
|
res.end(str) |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* Template title, framework authors may override this value. |
|
*/ |
|
|
|
exports.title = 'Connect' |
|
|
|
/** |
|
* Escape a block of HTML, preserving whitespace. |
|
* @api private |
|
*/ |
|
|
|
function escapeHtmlBlock (str) { |
|
return escapeHtml(str) |
|
.replace(DOUBLE_SPACE_REGEXP, ' ') |
|
.replace(NEW_LINE_REGEXP, '<br>') |
|
} |
|
|
|
/** |
|
* Stringify a value. |
|
* @api private |
|
*/ |
|
|
|
function stringify (val) { |
|
var stack = val.stack |
|
|
|
if (stack) { |
|
return String(stack) |
|
} |
|
|
|
var str = String(val) |
|
|
|
return str === toString.call(val) |
|
? inspect(val) |
|
: str |
|
} |
|
|
|
/** |
|
* Log error to console. |
|
* @api private |
|
*/ |
|
|
|
function logerror (err, str) { |
|
console.error(str || err.stack) |
|
}
|
|
|