refactor: abstract out some client side dashboard code into modules, analytics subpages for users, topics, and logins

This commit is contained in:
Julian Lam
2021-02-22 16:37:17 -05:00
parent 079a13d41a
commit f561799f74
16 changed files with 424 additions and 75 deletions

View File

@@ -123,10 +123,17 @@ async function getStats() {
getStatsForSet('topics:tid', 'topicCount'),
]);
results[0].name = '[[admin/dashboard:unique-visitors]]';
results[1].name = '[[admin/dashboard:logins]]';
results[1].href = `${nconf.get('relative_path')}/admin/dashboard/logins`;
results[2].name = '[[admin/dashboard:new-users]]';
results[2].href = `${nconf.get('relative_path')}/admin/dashboard/users`;
results[3].name = '[[admin/dashboard:posts]]';
results[4].name = '[[admin/dashboard:topics]]';
results[4].href = `${nconf.get('relative_path')}/admin/dashboard/topics`;
({ results } = await plugins.hooks.fire('filter:admin.getStats', {
results,
@@ -221,3 +228,66 @@ async function getLastRestart() {
lastrestart.timestampISO = utils.toISOString(lastrestart.timestamp);
return lastrestart;
}
dashboardController.getLogins = async (req, res) => {
let stats = await getStats();
const dataset = await analytics.getHourlyStatsForSet('analytics:logins', Date.now(), 24);
stats = stats.filter(stat => stat.name === '[[admin/dashboard:logins]]').map(({ ...stat }) => {
delete stat.href;
return stat;
});
const summary = {
day: stats[0].today,
week: stats[0].thisweek,
month: stats[0].thismonth,
};
res.render('admin/dashboard/logins', {
set: 'logins',
stats,
dataset,
summary,
});
};
dashboardController.getUsers = async (req, res) => {
let stats = await getStats();
const dataset = await analytics.getHourlyStatsForSet('analytics:registrations', Date.now(), 24);
stats = stats.filter(stat => stat.name === '[[admin/dashboard:new-users]]').map(({ ...stat }) => {
delete stat.href;
return stat;
});
const summary = {
day: stats[0].today,
week: stats[0].thisweek,
month: stats[0].thismonth,
};
res.render('admin/dashboard/users', {
set: 'registrations',
stats,
dataset,
summary,
});
};
dashboardController.getTopics = async (req, res) => {
let stats = await getStats();
const dataset = await analytics.getHourlyStatsForSet('analytics:topics', Date.now(), 24);
stats = stats.filter(stat => stat.name === '[[admin/dashboard:topics]]').map(({ ...stat }) => {
delete stat.href;
return stat;
});
const summary = {
day: stats[0].today,
week: stats[0].thisweek,
month: stats[0].thismonth,
};
res.render('admin/dashboard/topics', {
set: 'topics',
stats,
dataset,
summary,
});
};

View File

@@ -1,7 +1,9 @@
'use strict';
const user = require('../../user');
const meta = require('../../meta');
const privileges = require('../../privileges');
const analytics = require('../../analytics');
const helpers = require('../helpers');
@@ -17,3 +19,22 @@ Admin.updateSetting = async (req, res) => {
await meta.configs.set(req.params.setting, req.body.value);
helpers.formatApiResponse(200, res);
};
Admin.getAnalytics = async (req, res) => {
const ok = await user.isAdministrator(req.uid);
if (!ok) {
return helpers.formatApiResponse(403, res);
}
// Default returns views from past 24 hours, by hour
if (!req.query.amount) {
if (req.query.units === 'days') {
req.query.amount = 30;
} else {
req.query.amount = 24;
}
}
const getStats = req.query.units === 'days' ? analytics.getDailyStatsForSet : analytics.getHourlyStatsForSet;
helpers.formatApiResponse(200, res, await getStats(`analytics:${req.params.set}`, parseInt(req.query.until, 10) || Date.now(), req.query.amount));
};

View File

@@ -8,6 +8,9 @@ module.exports = function (app, name, middleware, controllers) {
helpers.setupAdminPageRoute(app, `/${name}`, middleware, middlewares, controllers.admin.routeIndex);
helpers.setupAdminPageRoute(app, `/${name}/dashboard`, middleware, middlewares, controllers.admin.dashboard.get);
helpers.setupAdminPageRoute(app, `/${name}/dashboard/logins`, middleware, middlewares, controllers.admin.dashboard.getLogins);
helpers.setupAdminPageRoute(app, `/${name}/dashboard/users`, middleware, middlewares, controllers.admin.dashboard.getUsers);
helpers.setupAdminPageRoute(app, `/${name}/dashboard/topics`, middleware, middlewares, controllers.admin.dashboard.getTopics);
helpers.setupAdminPageRoute(app, `/${name}/manage/categories`, middleware, middlewares, controllers.admin.categories.getAll);
helpers.setupAdminPageRoute(app, `/${name}/manage/categories/:category_id`, middleware, middlewares, controllers.admin.categories.get);

View File

@@ -12,5 +12,7 @@ module.exports = function () {
setupApiRoute(router, 'put', '/settings/:setting', [...middlewares, middleware.checkRequired.bind(null, ['value'])], controllers.write.admin.updateSetting);
setupApiRoute(router, 'get', '/analytics/:set', [...middlewares], controllers.write.admin.getAnalytics);
return router;
};

View File

@@ -9,6 +9,7 @@ const slugify = require('../slugify');
const plugins = require('../plugins');
const groups = require('../groups');
const meta = require('../meta');
const analytics = require('../analytics');
module.exports = function (User) {
User.create = async function (data) {
@@ -108,6 +109,7 @@ module.exports = function (User) {
await Promise.all([
db.incrObjectField('global', 'userCount'),
analytics.increment('registrations'),
db.sortedSetAddBulk(bulkAdd),
groups.join(groupsToJoin, userData.uid),
User.notifications.sendWelcomeNotification(userData.uid),

View File

@@ -1,80 +1,7 @@
<div class="row dashboard">
<div class="col-lg-9">
<div class="panel panel-default" id="analytics-panel">
<div class="panel-heading">
[[admin/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">
<canvas id="analytics-traffic" width="100%" height="400"></canvas>
</div>
<hr/>
<div class="row">
<div class="col-sm-3 hidden-xs text-center pageview-stats">
<div><strong id="pageViewsThirty">0</strong></div>
<div><a href="#" class="updatePageviewsGraph" data-action="updateGraph" data-units="days" data-amount="30">[[admin/dashboard:page-views-thirty]]</a></div>
</div>
<div class="col-sm-3 text-center pageview-stats">
<div><strong id="pageViewsSeven">0</strong></div>
<div><a href="#" class="updatePageviewsGraph" data-action="updateGraph" data-units="days" data-amount="7">[[admin/dashboard:page-views-seven]]</a></div>
</div>
<div class="col-sm-3 hidden-xs text-center pageview-stats">
<div><strong id="pageViewsPastDay">0</strong></div>
<div><a href="#" class="updatePageviewsGraph active" data-action="updateGraph" data-units="hours">[[admin/dashboard:page-views-last-day]]</a></div>
</div>
<div class="col-sm-3 text-center pageview-stats">
<div><strong><i class="fa fa-clock-o"></i></strong></div>
<div><a href="#" class="updatePageviewsGraph" data-action="updateGraph" data-units="custom">[[admin/dashboard:page-views-custom]]</a></div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th></th>
<th class="text-right">[[admin/dashboard:stats.yesterday]]</th>
<th class="text-right">[[admin/dashboard:stats.today]]</th>
<th></th>
<th class="text-right">[[admin/dashboard:stats.last-week]]</th>
<th class="text-right">[[admin/dashboard:stats.this-week]]</th>
<th></th>
<th class="text-right">[[admin/dashboard:stats.last-month]]</th>
<th class="text-right">[[admin/dashboard:stats.this-month]]</th>
<th></th>
<th class="text-right">[[admin/dashboard:stats.all]]</th>
</tr>
</thead>
<tbody>
<!-- BEGIN stats -->
<tr>
<td><strong>{stats.name}</strong></td>
<td class="text-right formatted-number">{stats.yesterday}</td>
<td class="text-right formatted-number">{stats.today}</td>
<td class="{stats.dayTextClass}"><small>{stats.dayIncrease}%</small></td>
<td class="text-right formatted-number">{stats.lastweek}</td>
<td class="text-right formatted-number">{stats.thisweek}</td>
<td class="{stats.weekTextClass}"><small>{stats.weekIncrease}%</small></td>
<td class="text-right formatted-number">{stats.lastmonth}</td>
<td class="text-right formatted-number">{stats.thismonth}</td>
<td class="{stats.monthTextClass}"><small>{stats.monthIncrease}%</small></td>
<td class="text-right formatted-number">{stats.alltime}</td>
</tr>
<!-- END stats -->
</tbody>
</table>
</div>
</div>
<!-- IMPORT admin/partials/dashboard/graph.tpl -->
<!-- IMPORT admin/partials/dashboard/stats.tpl -->
<div class="row">
<div class="col-lg-4">

View File

@@ -0,0 +1,6 @@
<div class="row dashboard">
<div class="col-xs-12">
<!-- IMPORT admin/partials/dashboard/graph.tpl -->
<!-- IMPORT admin/partials/dashboard/stats.tpl -->
</div>
</div>

View File

@@ -0,0 +1,6 @@
<div class="row dashboard">
<div class="col-xs-12">
<!-- IMPORT admin/partials/dashboard/graph.tpl -->
<!-- IMPORT admin/partials/dashboard/stats.tpl -->
</div>
</div>

View File

@@ -0,0 +1,6 @@
<div class="row dashboard">
<div class="col-xs-12">
<!-- IMPORT admin/partials/dashboard/graph.tpl -->
<!-- IMPORT admin/partials/dashboard/stats.tpl -->
</div>
</div>

View File

@@ -0,0 +1,33 @@
<div class="panel panel-default" id="analytics-panel">
<div class="panel-heading">
[[admin/dashboard:forum-traffic]]
<div class="pull-right">
<a id="view-as-json" href="{config.relative_path}/api/v3/admin/analytics/{set}?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">
<canvas id="analytics-traffic" width="100%" height="400"></canvas>
</div>
<hr/>
<div class="row">
<div class="col-sm-3 hidden-xs text-center pageview-stats">
<div><strong id="pageViewsThirty">{{{ if summary.month }}}{./summary.month}{{{ else }}}0{{{ end }}}</strong></div>
<div><a href="#" class="updatePageviewsGraph" data-action="updateGraph" data-units="days" data-amount="30">[[admin/dashboard:page-views-thirty]]</a></div>
</div>
<div class="col-sm-3 text-center pageview-stats">
<div><strong id="pageViewsSeven">{{{ if summary.week }}}{./summary.week}{{{ else }}}0{{{ end }}}</strong></div>
<div><a href="#" class="updatePageviewsGraph" data-action="updateGraph" data-units="days" data-amount="7">[[admin/dashboard:page-views-seven]]</a></div>
</div>
<div class="col-sm-3 hidden-xs text-center pageview-stats">
<div><strong id="pageViewsPastDay">{{{ if summary.day }}}{./summary.day}{{{ else }}}0{{{ end }}}</strong></div>
<div><a href="#" class="updatePageviewsGraph active" data-action="updateGraph" data-units="hours">[[admin/dashboard:page-views-last-day]]</a></div>
</div>
<div class="col-sm-3 text-center pageview-stats">
<div><strong><i class="fa fa-clock-o"></i></strong></div>
<div><a href="#" class="updatePageviewsGraph" data-action="updateGraph" data-units="custom">[[admin/dashboard:page-views-custom]]</a></div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,49 @@
<div class="row">
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th></th>
<th class="text-right">[[admin/dashboard:stats.yesterday]]</th>
<th class="text-right">[[admin/dashboard:stats.today]]</th>
<th></th>
<th class="text-right">[[admin/dashboard:stats.last-week]]</th>
<th class="text-right">[[admin/dashboard:stats.this-week]]</th>
<th></th>
<th class="text-right">[[admin/dashboard:stats.last-month]]</th>
<th class="text-right">[[admin/dashboard:stats.this-month]]</th>
<th></th>
<th class="text-right">[[admin/dashboard:stats.all]]</th>
</tr>
</thead>
<tbody>
<!-- BEGIN stats -->
<tr>
<td>
<strong>
{{{ if ../href }}}
<a href="{../href}">{../name}</a>
{{{ else }}}
{../name}
{{{ end }}}
</strong>
</td>
<td class="text-right formatted-number">{stats.yesterday}</td>
<td class="text-right formatted-number">{stats.today}</td>
<td class="{stats.dayTextClass}"><small>{stats.dayIncrease}%</small></td>
<td class="text-right formatted-number">{stats.lastweek}</td>
<td class="text-right formatted-number">{stats.thisweek}</td>
<td class="{stats.weekTextClass}"><small>{stats.weekIncrease}%</small></td>
<td class="text-right formatted-number">{stats.lastmonth}</td>
<td class="text-right formatted-number">{stats.thismonth}</td>
<td class="{stats.monthTextClass}"><small>{stats.monthIncrease}%</small></td>
<td class="text-right formatted-number">{stats.alltime}</td>
</tr>
<!-- END stats -->
</tbody>
</table>
</div>
</div>