mirror of
				https://github.com/NodeBB/NodeBB.git
				synced 2025-11-03 20:45:58 +01:00 
			
		
		
		
	closes #4658
This commit is contained in:
		
							
								
								
									
										64
									
								
								public/src/admin/advanced/errors.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								public/src/admin/advanced/errors.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
			
		||||
"use strict";
 | 
			
		||||
/*global config, define, app, socket, ajaxify, bootbox, templates, Chart, utils */
 | 
			
		||||
 | 
			
		||||
define('admin/advanced/errors', ['Chart'], function(Chart) {
 | 
			
		||||
	var Errors = {};
 | 
			
		||||
 | 
			
		||||
	Errors.init = function() {
 | 
			
		||||
		var notFoundCanvas = document.getElementById('not-found'),
 | 
			
		||||
			tooBusyCanvas = document.getElementById('toobusy'),
 | 
			
		||||
			dailyLabels = utils.getDaysArray();
 | 
			
		||||
 | 
			
		||||
		dailyLabels.length = 7;
 | 
			
		||||
 | 
			
		||||
		if (utils.isMobile()) {
 | 
			
		||||
			Chart.defaults.global.showTooltips = false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var data = {
 | 
			
		||||
			'not-found': {
 | 
			
		||||
				labels: dailyLabels,
 | 
			
		||||
				datasets: [
 | 
			
		||||
					{
 | 
			
		||||
						label: "",
 | 
			
		||||
						fillColor: "rgba(186,139,175,0.2)",
 | 
			
		||||
						strokeColor: "rgba(186,139,175,1)",
 | 
			
		||||
						pointColor: "rgba(186,139,175,1)",
 | 
			
		||||
						pointStrokeColor: "#fff",
 | 
			
		||||
						pointHighlightFill: "#fff",
 | 
			
		||||
						pointHighlightStroke: "rgba(186,139,175,1)",
 | 
			
		||||
						data: ajaxify.data.analytics['not-found']
 | 
			
		||||
					}
 | 
			
		||||
				]
 | 
			
		||||
			},
 | 
			
		||||
			'toobusy': {
 | 
			
		||||
				labels: dailyLabels,
 | 
			
		||||
				datasets: [
 | 
			
		||||
					{
 | 
			
		||||
						label: "",
 | 
			
		||||
						fillColor: "rgba(151,187,205,0.2)",
 | 
			
		||||
						strokeColor: "rgba(151,187,205,1)",
 | 
			
		||||
						pointColor: "rgba(151,187,205,1)",
 | 
			
		||||
						pointStrokeColor: "#fff",
 | 
			
		||||
						pointHighlightFill: "#fff",
 | 
			
		||||
						pointHighlightStroke: "rgba(151,187,205,1)",
 | 
			
		||||
						data: ajaxify.data.analytics['toobusy']
 | 
			
		||||
					}
 | 
			
		||||
				]
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		notFoundCanvas.width = $(notFoundCanvas).parent().width();
 | 
			
		||||
		tooBusyCanvas.width = $(tooBusyCanvas).parent().width();
 | 
			
		||||
		new Chart(notFoundCanvas.getContext('2d')).Line(data['not-found'], {
 | 
			
		||||
			responsive: true,
 | 
			
		||||
			animation: false
 | 
			
		||||
		});
 | 
			
		||||
		new Chart(tooBusyCanvas.getContext('2d')).Line(data['toobusy'], {
 | 
			
		||||
			responsive: true,
 | 
			
		||||
			animation: false
 | 
			
		||||
		});
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	return Errors;
 | 
			
		||||
});
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
"use strict";
 | 
			
		||||
/*global config, define, app, socket, ajaxify, bootbox, templates, Chart, utils */
 | 
			
		||||
/*global config, define, app, socket, ajaxify, bootbox, templates, utils */
 | 
			
		||||
 | 
			
		||||
define('admin/manage/category-analytics', [], function() {
 | 
			
		||||
define('admin/manage/category-analytics', ['Chart'], function(Chart) {
 | 
			
		||||
	var CategoryAnalytics = {};
 | 
			
		||||
 | 
			
		||||
	CategoryAnalytics.init = function() {
 | 
			
		||||
 
 | 
			
		||||
@@ -187,4 +187,11 @@ var db = require('./database');
 | 
			
		||||
		}, callback);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	Analytics.getErrorAnalytics = function(callback) {
 | 
			
		||||
		async.parallel({
 | 
			
		||||
			'not-found': async.apply(Analytics.getDailyStatsForSet, 'analytics:errors:404', Date.now(), 7),
 | 
			
		||||
			'toobusy': async.apply(Analytics.getDailyStatsForSet, 'analytics:errors:503', Date.now(), 7)
 | 
			
		||||
		}, callback);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
}(exports));
 | 
			
		||||
@@ -14,6 +14,7 @@ var adminController = {
 | 
			
		||||
	},
 | 
			
		||||
	events: require('./admin/events'),
 | 
			
		||||
	logs: require('./admin/logs'),
 | 
			
		||||
	errors: require('./admin/errors'),
 | 
			
		||||
	database: require('./admin/database'),
 | 
			
		||||
	postCache: require('./admin/postCache'),
 | 
			
		||||
	plugins: require('./admin/plugins'),
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								src/controllers/admin/errors.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/controllers/admin/errors.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
var async = require('async');
 | 
			
		||||
 | 
			
		||||
var meta = require('../../meta'),
 | 
			
		||||
	analytics = require('../../analytics');
 | 
			
		||||
 | 
			
		||||
var errorsController = {};
 | 
			
		||||
 | 
			
		||||
errorsController.get = function(req, res) {
 | 
			
		||||
	async.parallel({
 | 
			
		||||
		'not-found': async.apply(meta.errors.get),
 | 
			
		||||
		analytics: async.apply(analytics.getErrorAnalytics)
 | 
			
		||||
	}, function(err, data) {
 | 
			
		||||
		res.render('admin/advanced/errors', data);
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
module.exports = errorsController;
 | 
			
		||||
@@ -373,12 +373,14 @@ Controllers.handle404 = function(req, res) {
 | 
			
		||||
	} else if (isLanguage.test(req.url)) {
 | 
			
		||||
		res.status(200).json({});
 | 
			
		||||
	} else if (req.path.startsWith(relativePath + '/uploads') || (req.get('accept') && req.get('accept').indexOf('text/html') === -1) || req.path === '/favicon.ico') {
 | 
			
		||||
		meta.errors.log404(req.path || '');
 | 
			
		||||
		res.sendStatus(404);
 | 
			
		||||
	} else if (req.accepts('html')) {
 | 
			
		||||
		if (process.env.NODE_ENV === 'development') {
 | 
			
		||||
			winston.warn('Route requested but not found: ' + req.url);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		meta.errors.log404(req.path.replace(/^\/api/, '') || '');
 | 
			
		||||
		res.status(404);
 | 
			
		||||
 | 
			
		||||
		if (res.locals.isAPI) {
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,7 @@ var async = require('async'),
 | 
			
		||||
	require('./meta/sounds')(Meta);
 | 
			
		||||
	require('./meta/settings')(Meta);
 | 
			
		||||
	require('./meta/logs')(Meta);
 | 
			
		||||
	require('./meta/errors')(Meta);
 | 
			
		||||
	require('./meta/tags')(Meta);
 | 
			
		||||
	require('./meta/dependencies')(Meta);
 | 
			
		||||
	Meta.templates = require('./meta/templates');
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										36
									
								
								src/meta/errors.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/meta/errors.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
var async = require('async'),
 | 
			
		||||
	winston = require('winston'),
 | 
			
		||||
	validator = require('validator');
 | 
			
		||||
 | 
			
		||||
var db = require('../database'),
 | 
			
		||||
	analytics = require('../analytics');
 | 
			
		||||
 | 
			
		||||
module.exports = function(Meta) {
 | 
			
		||||
 | 
			
		||||
	Meta.errors = {};
 | 
			
		||||
 | 
			
		||||
	Meta.errors.log404 = function(route, callback) {
 | 
			
		||||
		callback = callback || function() {};
 | 
			
		||||
		route = route.replace(/\/$/, '');	// remove trailing slashes
 | 
			
		||||
		analytics.increment('errors:404');
 | 
			
		||||
		db.sortedSetIncrBy('errors:404', 1, route, callback);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	Meta.errors.get = function(callback) {
 | 
			
		||||
		db.getSortedSetRevRangeByScoreWithScores('errors:404', 0, -1, '+inf', '-inf', function(err, data) {
 | 
			
		||||
			data = data.map(function(nfObject) {
 | 
			
		||||
				nfObject.value = validator.escape(nfObject.value);
 | 
			
		||||
				return nfObject;
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			callback(null, data);
 | 
			
		||||
		});
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	Meta.errors.clear = function(callback) {
 | 
			
		||||
		console.log('clear errors');
 | 
			
		||||
		callback();
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
@@ -302,6 +302,7 @@ middleware.privateUploads = function(req, res, next) {
 | 
			
		||||
 | 
			
		||||
middleware.busyCheck = function(req, res, next) {
 | 
			
		||||
	if (global.env === 'production' && (!meta.config.hasOwnProperty('eventLoopCheckEnabled') || parseInt(meta.config.eventLoopCheckEnabled, 10) === 1) && toobusy()) {
 | 
			
		||||
		analytics.increment('errors:503');
 | 
			
		||||
		res.status(503).type('text/html').sendFile(path.join(__dirname, '../../public/503.html'));
 | 
			
		||||
	} else {
 | 
			
		||||
		next();
 | 
			
		||||
 
 | 
			
		||||
@@ -82,6 +82,7 @@ function addRoutes(router, middleware, controllers) {
 | 
			
		||||
	router.get('/advanced/database', middlewares, controllers.admin.database.get);
 | 
			
		||||
	router.get('/advanced/events', middlewares, controllers.admin.events.get);
 | 
			
		||||
	router.get('/advanced/logs', middlewares, controllers.admin.logs.get);
 | 
			
		||||
	router.get('/advanced/errors', middlewares, controllers.admin.errors.get);
 | 
			
		||||
	router.get('/advanced/post-cache', middlewares, controllers.admin.postCache.get);
 | 
			
		||||
 | 
			
		||||
	router.get('/development/logger', middlewares, controllers.admin.logger.get);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										61
									
								
								src/views/admin/advanced/errors.tpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/views/admin/advanced/errors.tpl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
			
		||||
<div class="row">
 | 
			
		||||
	<div class="col-lg-9">
 | 
			
		||||
		<div class="row">
 | 
			
		||||
			<div class="col-sm-6 text-center">
 | 
			
		||||
				<div class="panel panel-default">
 | 
			
		||||
					<div class="panel-body">
 | 
			
		||||
						<div><canvas id="not-found" height="250"></canvas></div>
 | 
			
		||||
					</div>
 | 
			
		||||
					<div class="panel-footer"><small><strong>Figure 1</strong> – <code>404 Not Found</code> events per day</small></div>
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
			<div class="col-sm-6 text-center">
 | 
			
		||||
				<div class="panel panel-default">
 | 
			
		||||
					<div class="panel-body">
 | 
			
		||||
						<div><canvas id="toobusy" height="250"></canvas></div>
 | 
			
		||||
					</div>
 | 
			
		||||
					<div class="panel-footer"><small><strong>Figure 2</strong> – <code>503 Service Unavailable</code> events per day</small></div>
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
		<div class="panel panel-default">
 | 
			
		||||
			<div class="panel-heading"><i class="fa fa-exclamation-triangle"></i> 404 Not Found</div>
 | 
			
		||||
			<div class="panel-body">
 | 
			
		||||
				<table class="table table-striped">
 | 
			
		||||
					<thead>
 | 
			
		||||
						<th>Route</th>
 | 
			
		||||
						<th>Count</th>
 | 
			
		||||
					</thead>
 | 
			
		||||
					<tbody>
 | 
			
		||||
						<!-- BEGIN not-found -->
 | 
			
		||||
						<tr>
 | 
			
		||||
							<td>{../value}</td>
 | 
			
		||||
							<td>{../score}</td>
 | 
			
		||||
						</tr>
 | 
			
		||||
						<!-- END not-found -->
 | 
			
		||||
						<!-- IF !not-found.length -->
 | 
			
		||||
						<tr>
 | 
			
		||||
							<td colspan="2">
 | 
			
		||||
								<div class="alert alert-success">
 | 
			
		||||
									Hooray! There are no routes that were not found.
 | 
			
		||||
								</div>
 | 
			
		||||
							</td>
 | 
			
		||||
						</tr>
 | 
			
		||||
						<!-- ENDIF !not-found.length -->
 | 
			
		||||
					</tbody>
 | 
			
		||||
				</table>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
	<div class="col-lg-3 acp-sidebar">
 | 
			
		||||
		<div class="panel panel-default">
 | 
			
		||||
			<div class="panel-heading">Manage Error Log</div>
 | 
			
		||||
			<div class="panel-body">
 | 
			
		||||
				<div class="btn-group-vertical btn-block" role="group">
 | 
			
		||||
					<button class="btn btn-info" data-action="export"><i class="fa fa-download"></i> Export Error Log (CSV)</button>
 | 
			
		||||
					<button class="btn btn-danger" data-action="clear"><i class="fa fa-trash"></i> Clear Error Log</button>
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -95,6 +95,7 @@
 | 
			
		||||
			<li><a href="{relative_path}/admin/advanced/database">Database</a></li>
 | 
			
		||||
			<li><a href="{relative_path}/admin/advanced/events">Events</a></li>
 | 
			
		||||
			<li><a href="{relative_path}/admin/advanced/logs">Logs</a></li>
 | 
			
		||||
			<li><a href="{relative_path}/admin/advanced/errors">Errors</a></li>
 | 
			
		||||
			<li><a href="{relative_path}/admin/advanced/post-cache">Post Cache</a></li>
 | 
			
		||||
			<!-- IF env -->
 | 
			
		||||
			<li><a href="{relative_path}/admin/development/logger">Logger</a></li>
 | 
			
		||||
@@ -247,6 +248,7 @@
 | 
			
		||||
					<li><a href="{relative_path}/admin/advanced/database">Database</a></li>
 | 
			
		||||
					<li><a href="{relative_path}/admin/advanced/events">Events</a></li>
 | 
			
		||||
					<li><a href="{relative_path}/admin/advanced/logs">Logs</a></li>
 | 
			
		||||
					<li><a href="{relative_path}/admin/advanced/errors">Errors</a></li>
 | 
			
		||||
					<li><a href="{relative_path}/admin/advanced/post-cache">Post Cache</a></li>
 | 
			
		||||
					<!-- IF env -->
 | 
			
		||||
					<li><a href="{relative_path}/admin/development/logger">Logger</a></li>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user