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.
444 lines
9.7 KiB
444 lines
9.7 KiB
// dashboard.js
|
|
// Copyright (C) 2021 Digital Telepresence, LLC
|
|
// License: Apache-2.0
|
|
|
|
'use strict';
|
|
|
|
const mongoose = require('mongoose');
|
|
|
|
const Link = mongoose.model('Link');
|
|
const LinkVisit = mongoose.model('LinkVisit');
|
|
|
|
const moment = require('moment');
|
|
|
|
const { SiteService } = require('../../lib/site-lib');
|
|
|
|
class DashboardService extends SiteService {
|
|
|
|
static get CACHE_ENABLED ( ) { return process.env.LINKS_DASHBOARD_CACHE === 'enabled'; }
|
|
|
|
constructor (dtp) {
|
|
super(dtp, module.exports);
|
|
}
|
|
|
|
/*
|
|
*
|
|
* USER VISIT STATS
|
|
*
|
|
*/
|
|
async getUserVisitStats (user) {
|
|
const { cache: cacheService } = this.dtp.services;
|
|
let stats;
|
|
|
|
const cacheKey = `stats:user:${user._id}:visit`;
|
|
if (DashboardService.CACHE_ENABLED) {
|
|
stats = await cacheService.getObject(cacheKey);
|
|
if (stats) {
|
|
return stats;
|
|
}
|
|
}
|
|
|
|
this.log.info('getUserVisitStats', 'generating user visit stats report', { userId: user._id });
|
|
|
|
const END_DATE = new Date();
|
|
const START_DATE = moment(END_DATE).subtract(7, 'day').toDate();
|
|
|
|
const links = await Link.find({ user: user._id }).lean();
|
|
const linkIds = links.map((link) => link._id);
|
|
|
|
stats = await LinkVisit.aggregate([
|
|
{
|
|
$match: {
|
|
link: { $in: linkIds },
|
|
$and: [
|
|
{ created: { $gt: START_DATE } },
|
|
{ created: { $lt: END_DATE } },
|
|
],
|
|
},
|
|
},
|
|
{
|
|
$group: {
|
|
_id: {
|
|
year: { $year: '$created' },
|
|
month: { $month: '$created' },
|
|
day: { $dayOfMonth: '$created' },
|
|
hour: { $hour: '$created' },
|
|
},
|
|
count: { $sum: 1 },
|
|
},
|
|
},
|
|
{
|
|
$project: {
|
|
_id: false,
|
|
date: {
|
|
$dateFromParts: {
|
|
year: '$_id.year',
|
|
month: '$_id.month',
|
|
day: '$_id.day',
|
|
hour: '$_id.hour',
|
|
},
|
|
},
|
|
count: '$count',
|
|
},
|
|
},
|
|
{
|
|
$sort: { date: 1 },
|
|
},
|
|
]);
|
|
|
|
const response = {
|
|
start: START_DATE,
|
|
end: END_DATE,
|
|
stats,
|
|
};
|
|
await cacheService.setObjectEx(cacheKey, 60 * 5, response);
|
|
return response;
|
|
}
|
|
|
|
/*
|
|
*
|
|
* LINK VISIT STATS
|
|
*
|
|
*/
|
|
async getLinkVisitStats (link) {
|
|
const { cache: cacheService } = this.dtp.services;
|
|
let stats;
|
|
|
|
const cacheKey = `stats:link:${link._id}:visit`;
|
|
if (DashboardService.CACHE_ENABLED) {
|
|
stats = await cacheService.getObject(cacheKey);
|
|
if (stats) {
|
|
return stats;
|
|
}
|
|
}
|
|
|
|
this.log.info('getLinkVisitStats', 'generating link visit stats report', { linkId: link._id });
|
|
|
|
const END_DATE = new Date();
|
|
const START_DATE = moment(END_DATE).subtract(7, 'day').toDate();
|
|
|
|
stats = await LinkVisit.aggregate([
|
|
{
|
|
$match: {
|
|
link: link._id,
|
|
$and: [
|
|
{ created: { $gt: START_DATE } },
|
|
{ created: { $lt: END_DATE } },
|
|
],
|
|
},
|
|
},
|
|
{
|
|
$group: {
|
|
_id: {
|
|
year: { $year: '$created' },
|
|
month: { $month: '$created' },
|
|
day: { $dayOfMonth: '$created' },
|
|
hour: { $hour: '$created' },
|
|
},
|
|
count: { $sum: 1 },
|
|
},
|
|
},
|
|
{
|
|
$project: {
|
|
_id: false,
|
|
date: {
|
|
$dateFromParts: {
|
|
year: '$_id.year',
|
|
month: '$_id.month',
|
|
day: '$_id.day',
|
|
hour: '$_id.hour',
|
|
},
|
|
},
|
|
count: '$count',
|
|
},
|
|
},
|
|
{
|
|
$sort: { date: 1 },
|
|
},
|
|
]);
|
|
|
|
const response = {
|
|
start: START_DATE,
|
|
end: END_DATE,
|
|
stats,
|
|
};
|
|
await cacheService.setObjectEx(cacheKey, 60 * 5, response);
|
|
return response;
|
|
}
|
|
|
|
/*
|
|
*
|
|
* USER COUNTRY STATS
|
|
*
|
|
*/
|
|
async getUserCountryStats (user) {
|
|
const { cache: cacheService } = this.dtp.services;
|
|
let stats;
|
|
|
|
const cacheKey = `stats:user:${user._id}:country`;
|
|
if (DashboardService.CACHE_ENABLED) {
|
|
stats = await cacheService.getObject(cacheKey);
|
|
if (stats) {
|
|
return stats;
|
|
}
|
|
}
|
|
|
|
this.log.info('getUserCountryStats', 'generating user country stats report', { userId: user._id });
|
|
|
|
const END_DATE = new Date();
|
|
const START_DATE = moment(END_DATE).subtract(7, 'day').toDate();
|
|
|
|
const links = await Link.find({ user: user._id }).lean();
|
|
const linkIds = links.map((link) => link._id);
|
|
|
|
stats = await LinkVisit.aggregate([
|
|
{
|
|
$match: {
|
|
link: { $in: linkIds },
|
|
$and: [
|
|
{ created: { $gt: START_DATE } },
|
|
{ created: { $lt: END_DATE } },
|
|
],
|
|
},
|
|
},
|
|
{
|
|
$group: {
|
|
_id: {
|
|
country: '$geoip.country',
|
|
},
|
|
count: { $sum: 1 },
|
|
},
|
|
},
|
|
{
|
|
$project: {
|
|
_id: false,
|
|
country: '$_id.country',
|
|
count: '$count',
|
|
},
|
|
},
|
|
{
|
|
$sort: { count: -1, country: 1 },
|
|
},
|
|
{
|
|
$limit: 10,
|
|
},
|
|
]);
|
|
|
|
const response = {
|
|
start: START_DATE,
|
|
end: END_DATE,
|
|
stats,
|
|
};
|
|
await cacheService.setObjectEx(cacheKey, 60 * 5, response);
|
|
return response;
|
|
}
|
|
|
|
/*
|
|
*
|
|
* LINK COUNTRY STATS
|
|
*
|
|
*/
|
|
async getLinkCountryStats (link) {
|
|
const { cache: cacheService } = this.dtp.services;
|
|
let stats;
|
|
|
|
const cacheKey = `stats:link:${link._id}:country`;
|
|
if (DashboardService.CACHE_ENABLED) {
|
|
stats = await cacheService.getObject(cacheKey);
|
|
if (stats) {
|
|
return stats;
|
|
}
|
|
}
|
|
|
|
this.log.info('getLinkCountryStats', 'generating link country stats report', { linkId: link._id });
|
|
|
|
const END_DATE = new Date();
|
|
const START_DATE = moment(END_DATE).subtract(7, 'day').toDate();
|
|
|
|
stats = await LinkVisit.aggregate([
|
|
{
|
|
$match: {
|
|
link: link._id,
|
|
$and: [
|
|
{ created: { $gt: START_DATE } },
|
|
{ created: { $lt: END_DATE } },
|
|
],
|
|
},
|
|
},
|
|
{
|
|
$group: {
|
|
_id: {
|
|
country: '$geoip.country',
|
|
},
|
|
count: { $sum: 1 },
|
|
},
|
|
},
|
|
{
|
|
$project: {
|
|
_id: false,
|
|
country: '$_id.country',
|
|
count: '$count',
|
|
},
|
|
},
|
|
{
|
|
$sort: { count: -1, country: 1 },
|
|
},
|
|
{
|
|
$limit: 10,
|
|
},
|
|
]);
|
|
|
|
const response = {
|
|
start: START_DATE,
|
|
end: END_DATE,
|
|
stats,
|
|
};
|
|
await cacheService.setObjectEx(cacheKey, 60 * 5, response);
|
|
return response;
|
|
}
|
|
|
|
/*
|
|
*
|
|
* USER CITY STATS
|
|
*
|
|
*/
|
|
async getUserCityStats (user) {
|
|
const { cache: cacheService } = this.dtp.services;
|
|
let stats;
|
|
|
|
const cacheKey = `stats:user:${user._id}:city`;
|
|
if (DashboardService.CACHE_ENABLED) {
|
|
stats = await cacheService.getObject(cacheKey);
|
|
if (stats) {
|
|
return stats;
|
|
}
|
|
}
|
|
|
|
this.log.info('getUserCityStats', 'generating user city stats report', { userId: user._id });
|
|
|
|
const END_DATE = new Date();
|
|
const START_DATE = moment(END_DATE).subtract(7, 'day').toDate();
|
|
|
|
const links = await Link.find({ user: user._id }).lean();
|
|
const linkIds = links.map((link) => link._id);
|
|
|
|
stats = await LinkVisit.aggregate([
|
|
{
|
|
$match: {
|
|
link: { $in: linkIds },
|
|
$and: [
|
|
{ created: { $gt: START_DATE } },
|
|
{ created: { $lt: END_DATE } },
|
|
],
|
|
},
|
|
},
|
|
{
|
|
$group: {
|
|
_id: {
|
|
country: '$geoip.country',
|
|
region: '$geoip.region',
|
|
city: '$geoip.city',
|
|
},
|
|
count: { $sum: 1 },
|
|
},
|
|
},
|
|
{
|
|
$project: {
|
|
_id: false,
|
|
country: '$_id.country',
|
|
region: '$_id.region',
|
|
city: '$_id.city',
|
|
count: '$count',
|
|
},
|
|
},
|
|
{
|
|
$sort: { count: -1, city: 1, region: 1, country: 1 },
|
|
},
|
|
{
|
|
$limit: 10,
|
|
},
|
|
]);
|
|
|
|
const response = {
|
|
start: START_DATE,
|
|
end: END_DATE,
|
|
stats,
|
|
};
|
|
await cacheService.setObjectEx(cacheKey, 60 * 5, response);
|
|
return response;
|
|
}
|
|
|
|
/*
|
|
*
|
|
* LINK CITY STATS
|
|
*
|
|
*/
|
|
async getLinkCityStats (link) {
|
|
const { cache: cacheService } = this.dtp.services;
|
|
let stats;
|
|
|
|
const cacheKey = `stats:link:${link._id}:city`;
|
|
if (DashboardService.CACHE_ENABLED) {
|
|
stats = await cacheService.getObject(cacheKey);
|
|
if (stats) {
|
|
return stats;
|
|
}
|
|
}
|
|
|
|
this.log.info('getLinkCityStats', 'generating link city stats report', { linkId: link._id });
|
|
|
|
const END_DATE = new Date();
|
|
const START_DATE = moment(END_DATE).subtract(7, 'day').toDate();
|
|
|
|
stats = await LinkVisit.aggregate([
|
|
{
|
|
$match: {
|
|
link: link._id,
|
|
$and: [
|
|
{ created: { $gt: START_DATE } },
|
|
{ created: { $lt: END_DATE } },
|
|
],
|
|
},
|
|
},
|
|
{
|
|
$group: {
|
|
_id: {
|
|
country: '$geoip.country',
|
|
region: '$geoip.region',
|
|
city: '$geoip.city',
|
|
},
|
|
count: { $sum: 1 },
|
|
},
|
|
},
|
|
{
|
|
$project: {
|
|
_id: false,
|
|
country: '$_id.country',
|
|
region: '$_id.region',
|
|
city: '$_id.city',
|
|
count: '$count',
|
|
},
|
|
},
|
|
{
|
|
$sort: { count: -1, city: 1, region: 1, country: 1 },
|
|
},
|
|
{
|
|
$limit: 10,
|
|
},
|
|
]);
|
|
|
|
const response = {
|
|
start: START_DATE,
|
|
end: END_DATE,
|
|
stats,
|
|
};
|
|
await cacheService.setObjectEx(cacheKey, 60 * 5, response);
|
|
return response;
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
slug: 'dashboard',
|
|
name: 'dashboard',
|
|
create: (dtp) => { return new DashboardService(dtp); },
|
|
};
|