mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-10-28 09:36:16 +01:00
feat: ACP analytics API route (#7725)
* feat: added API route for retrieving analytics via REST API * feat: sets is now optional, can pass in multiple sets * fix: moved expand and added json button to panel header * fix: matching api params to socket method * fix: update json api button url on graph change * fix: updated default counts based on passed in units
This commit is contained in:
@@ -5,26 +5,35 @@
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
#analytics-panel .panel-heading i {
|
||||
&.fa-expand {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.fa-terminal::after {
|
||||
content: 'JSON';
|
||||
font-family: @font-family-sans-serif;
|
||||
font-weight: 600;
|
||||
color: @gray-dark;
|
||||
padding-left: .5em;
|
||||
}
|
||||
|
||||
padding: .75em;
|
||||
background-color: @gray-lighter;
|
||||
color: @gray-base;
|
||||
cursor: pointer;
|
||||
.transition(all .4s);
|
||||
|
||||
&.active {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
.graph-container {
|
||||
padding-right: 50px;
|
||||
position: relative;
|
||||
background: @body-bg;
|
||||
|
||||
.fa-expand {
|
||||
display: none;
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
padding: 5px;
|
||||
background-color: @gray-lighter;
|
||||
color: @gray-base;
|
||||
cursor: pointer;
|
||||
.transition(all .4s);
|
||||
|
||||
&.active {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.fa-expand {
|
||||
color: @gray-lighter;
|
||||
|
||||
@@ -461,6 +461,15 @@ define('admin/general/dashboard', ['semver', 'Chart', 'translator', 'benchpress'
|
||||
currentGraph.units = units;
|
||||
currentGraph.until = until;
|
||||
currentGraph.amount = amount;
|
||||
|
||||
// Update the View as JSON button url
|
||||
var apiEl = $('#view-as-json');
|
||||
var newHref = $.param({
|
||||
units: units,
|
||||
until: until,
|
||||
count: amount,
|
||||
});
|
||||
apiEl.attr('href', config.relative_path + '/api/admin/analytics?' + newHref);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -556,7 +565,7 @@ define('admin/general/dashboard', ['semver', 'Chart', 'translator', 'benchpress'
|
||||
}
|
||||
|
||||
function setupFullscreen() {
|
||||
var container = document.getElementById('analytics-traffic-container');
|
||||
var container = document.getElementById('analytics-panel');
|
||||
var $container = $(container);
|
||||
var btn = $container.find('.fa-expand');
|
||||
var fsMethod;
|
||||
|
||||
@@ -259,3 +259,5 @@ Analytics.getBlacklistAnalytics = function (callback) {
|
||||
hourly: async.apply(Analytics.getHourlyStatsForSet, 'analytics:blacklist', Date.now(), 24),
|
||||
}, callback);
|
||||
};
|
||||
|
||||
Analytics.async = require('./promisify')(Analytics);
|
||||
|
||||
@@ -4,10 +4,12 @@ var async = require('async');
|
||||
var nconf = require('nconf');
|
||||
var semver = require('semver');
|
||||
var winston = require('winston');
|
||||
const _ = require('lodash');
|
||||
|
||||
var versions = require('../../admin/versions');
|
||||
var db = require('../../database');
|
||||
var meta = require('../../meta');
|
||||
const analytics = require('../../analytics').async;
|
||||
var plugins = require('../../plugins');
|
||||
var user = require('../../user');
|
||||
var utils = require('../../utils');
|
||||
@@ -76,6 +78,40 @@ dashboardController.get = function (req, res, next) {
|
||||
], next);
|
||||
};
|
||||
|
||||
dashboardController.getAnalytics = async (req, res, next) => {
|
||||
// Basic validation
|
||||
const validUnits = ['days', 'hours'];
|
||||
const validSets = ['uniquevisitors', 'pageviews', 'pageviews:registered', 'pageviews:bot', 'pageviews:guest'];
|
||||
const until = req.query.until ? new Date(parseInt(req.query.until, 10)) : Date.now();
|
||||
const count = req.query.count || (req.query.units === 'hours' ? 24 : 30);
|
||||
if (isNaN(until) || !validUnits.includes(req.query.units)) {
|
||||
return next(new Error('[[error:invalid-data]]'));
|
||||
}
|
||||
|
||||
// Filter out invalid sets, if no sets, assume all sets
|
||||
let sets;
|
||||
if (req.query.sets) {
|
||||
sets = Array.isArray(req.query.sets) ? req.query.sets : [req.query.sets];
|
||||
sets = sets.filter(set => validSets.includes(set));
|
||||
} else {
|
||||
sets = validSets;
|
||||
}
|
||||
|
||||
const method = req.query.units === 'days' ? analytics.getDailyStatsForSet : analytics.getHourlyStatsForSet;
|
||||
let payload = await Promise.all(sets.map(async set => method('analytics:' + set, until, count)));
|
||||
payload = _.zipObject(sets, payload);
|
||||
|
||||
res.json({
|
||||
query: {
|
||||
set: req.query.set,
|
||||
units: req.query.units,
|
||||
until: until,
|
||||
count: count,
|
||||
},
|
||||
result: payload,
|
||||
});
|
||||
};
|
||||
|
||||
function getStats(callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
|
||||
@@ -5,6 +5,7 @@ var express = require('express');
|
||||
|
||||
function apiRoutes(router, middleware, controllers) {
|
||||
router.get('/users/csv', middleware.authenticate, controllers.admin.users.getCSV);
|
||||
router.get('/analytics', middleware.authenticate, controllers.admin.dashboard.getAnalytics);
|
||||
|
||||
var multipart = require('connect-multiparty');
|
||||
var multipartMiddleware = multipart();
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
<div class="row dashboard">
|
||||
<div class="col-lg-9">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">[[admin/general/dashboard:forum-traffic]]</div>
|
||||
<div class="panel panel-default" id="analytics-panel">
|
||||
<div class="panel-heading">
|
||||
[[admin/general/dashboard:forum-traffic]]
|
||||
<div class="pull-right">
|
||||
<a id="view-as-json" href="{config.relative_path}/api/admin/analytics&type=hourly"><i class="fa fa-terminal"></i></a>
|
||||
<i class="fa fa-expand"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="graph-container" id="analytics-traffic-container">
|
||||
<i class="fa fa-expand"></i>
|
||||
<canvas id="analytics-traffic" width="100%" height="400"></canvas>
|
||||
</div>
|
||||
<hr/>
|
||||
|
||||
Reference in New Issue
Block a user