From 5b42b6b369ca3eb917b1c983ffa51ffa46002eaa Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 19 Nov 2021 15:12:13 -0500 Subject: [PATCH] API route for returning tracked analytics keys (#10019) * feat: track metrics saved by NodeBB (and assoc. plugins), #9949 * feat: route to retrieve analytics keys, closes #9949 --- public/openapi/write.yaml | 2 ++ public/openapi/write/admin/analytics.yaml | 20 ++++++++++++++++++++ src/analytics.js | 18 ++++++++++++++++++ src/controllers/write/admin.js | 14 ++++++++------ src/routes/write/admin.js | 5 +++-- 5 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 public/openapi/write/admin/analytics.yaml diff --git a/public/openapi/write.yaml b/public/openapi/write.yaml index 734b64ff24..7fade16fab 100644 --- a/public/openapi/write.yaml +++ b/public/openapi/write.yaml @@ -142,6 +142,8 @@ paths: $ref: 'write/flags/flagId/notes/datetime.yaml' /admin/settings/{setting}: $ref: 'write/admin/settings/setting.yaml' + /admin/analytics: + $ref: 'write/admin/analytics.yaml' /admin/analytics/{set}: $ref: 'write/admin/analytics/set.yaml' /files/: diff --git a/public/openapi/write/admin/analytics.yaml b/public/openapi/write/admin/analytics.yaml new file mode 100644 index 0000000000..06f68a3778 --- /dev/null +++ b/public/openapi/write/admin/analytics.yaml @@ -0,0 +1,20 @@ +get: + tags: + - admin + summary: get analytics keys + description: This operation returns the list metrics tracked by NodeBB. It is only accessible to administrators. + responses: + '200': + description: Analytics keys retrieved + content: + application/json: + schema: + type: object + properties: + status: + $ref: ../../components/schemas/Status.yaml#/Status + response: + type: object + properties: + keys: + type: array \ No newline at end of file diff --git a/src/analytics.js b/src/analytics.js index fa848ad665..c2d0a010e1 100644 --- a/src/analytics.js +++ b/src/analytics.js @@ -53,6 +53,8 @@ Analytics.increment = function (keys, callback) { } }; +Analytics.getKeys = async () => db.getSortedSetRange('analyticsKeys', 0, -1); + Analytics.pageView = async function (payload) { pageViews += 1; @@ -90,6 +92,17 @@ Analytics.writeData = async function () { const month = new Date(); const dbQueue = []; + // Build list of metrics that were updated + let metrics = [ + 'pageviews', + 'pageviews:month', + ]; + metrics.forEach((metric) => { + const toAdd = ['registered', 'guest', 'bot'].map(type => `${metric}:${type}`); + metrics = [...metrics, ...toAdd]; + }); + metrics.push('uniquevisitors'); + today.setHours(today.getHours(), 0, 0, 0); month.setMonth(month.getMonth(), 1); month.setHours(0, 0, 0, 0); @@ -130,8 +143,13 @@ Analytics.writeData = async function () { for (const [key, value] of Object.entries(counters)) { dbQueue.push(db.sortedSetIncrBy(`analytics:${key}`, value, today.getTime())); + metrics.push(key); delete counters[key]; } + + // Update list of tracked metrics + dbQueue.push(db.sortedSetAdd('analyticsKeys', metrics.map(() => +Date.now()), metrics)); + try { await Promise.all(dbQueue); } catch (err) { diff --git a/src/controllers/write/admin.js b/src/controllers/write/admin.js index b667efcbad..8b9faa55ef 100644 --- a/src/controllers/write/admin.js +++ b/src/controllers/write/admin.js @@ -1,6 +1,5 @@ 'use strict'; -const user = require('../../user'); const meta = require('../../meta'); const privileges = require('../../privileges'); const analytics = require('../../analytics'); @@ -20,13 +19,16 @@ Admin.updateSetting = async (req, res) => { helpers.formatApiResponse(200, res); }; -Admin.getAnalytics = async (req, res) => { - const ok = await user.isAdministrator(req.uid); +Admin.getAnalyticsKeys = async (req, res) => { + let keys = await analytics.getKeys(); - if (!ok) { - return helpers.formatApiResponse(403, res); - } + // Sort keys alphabetically + keys = keys.sort((a, b) => (a < b ? -1 : 1)); + helpers.formatApiResponse(200, res, { keys }); +}; + +Admin.getAnalyticsData = async (req, res) => { // Default returns views from past 24 hours, by hour if (!req.query.amount) { if (req.query.units === 'days') { diff --git a/src/routes/write/admin.js b/src/routes/write/admin.js index 96cb386beb..0cda6327fb 100644 --- a/src/routes/write/admin.js +++ b/src/routes/write/admin.js @@ -8,11 +8,12 @@ const routeHelpers = require('../helpers'); const { setupApiRoute } = routeHelpers; module.exports = function () { - const middlewares = [middleware.ensureLoggedIn]; + const middlewares = [middleware.ensureLoggedIn, middleware.admin.checkPrivileges]; 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); + setupApiRoute(router, 'get', '/analytics', [...middlewares], controllers.write.admin.getAnalyticsKeys); + setupApiRoute(router, 'get', '/analytics/:set', [...middlewares], controllers.write.admin.getAnalyticsData); return router; };