wekan/models/server/metrics.js
Lauri Ojansivu 563a508e26 Added missing ) character.
Thanks to xet7 !
2025-02-11 22:12:57 +02:00

230 lines
7.6 KiB
JavaScript

import { Meteor } from 'meteor/meteor';
import Users from '../users';
function acceptedIpAddress(ipAddress) {
const trustedIpAddress = process.env.METRICS_ACCEPTED_IP_ADDRESS;
return (
trustedIpAddress !== undefined &&
trustedIpAddress.split(',').includes(ipAddress)
);
}
function accessToken(req) {
const valid_token = process.env.METRICS_ACCESS_TOKEN;
let token;
if (req.headers && req.headers.authorization) {
var parts = req.headers.authorization.split(" ");
if (parts.length === 2) {
var scheme = parts[0];
var credentials = parts[1];
if (/^Bearer$/i.test(scheme)) {
token = credentials;
}
}
}
if (!token && req.query && req.query.access_token) {
token = req.query.access_token;
}
return (
token !== undefined &&
valid_token !== undefined &&
token == valid_token
);
}
const getBoardTitleWithMostActivities = (dateWithXdaysAgo, nbLimit) => {
return Promise.await(
Activities.rawCollection()
.aggregate([
{
$match: { modifiedAt: { $gte: dateWithXdaysAgo }}
},
{
$group: { _id: '$boardId', count: { $sum: 1 } }
},
{
$sort: { count: -1 }
},
{
$lookup: { from: 'boards', localField: '_id', foreignField: '_id', as: 'lookup'}
},
{
$project: { "lookup.title":1, "count":1}
}])
.limit(nbLimit).toArray()
);
};
const getBoards = (boardIds) => {
const ret = ReactiveCache.getBoards({ _id: { $in: boardIds } });
return ret;
};
Meteor.startup(() => {
WebApp.connectHandlers.use('/metrics', (req, res, next) => {
try {
const ipAddress =
req.headers['x-forwarded-for'] || req.socket.remoteAddress;
// if(process.env.TRUST_PROXY_FORXARD)
// {
// const ipAddress = req.headers['x-forwarded-for'] || req.socket.remoteAddress
// }else{
// const ipAddress = req.socket.remoteAddress
// }
// List of trusted ip adress will be found in environment variable "METRICS_ACCEPTED_IP_ADDRESS" (separeted with commas)
if (acceptedIpAddress(ipAddress) || (accessToken(req))) {
let metricsRes = '';
let resCount = 0;
//connected users
metricsRes += '# Number of connected users\n';
// Get number of connected user by using meteor socketJs
const allOpenedSockets = Meteor.server.stream_server.open_sockets;
let connectedUserIds = [];
allOpenedSockets.forEach(
(socket) =>
socket._meteorSession.userId !== null &&
connectedUserIds.push(socket._meteorSession.userId),
);
resCount = connectedUserIds.length; // KPI 1
metricsRes += 'wekan_connectedUsers ' + resCount + '\n';
//registered users
metricsRes += '# Number of registered users\n';
// Get number of registered user
resCount = ReactiveCache.getUsers({}).length; // KPI 2
metricsRes += 'wekan_registeredUsers ' + resCount + '\n';
resCount = 0;
//board numbers
metricsRes += '# Number of registered boards\n';
// Get number of registered boards
resCount = ReactiveCache.getBoards({ archived: false, type: 'board' }).length; // KPI 3
metricsRes += 'wekan_registeredboards ' + resCount + '\n';
resCount = 0;
//board numbers by registered users
metricsRes += '# Number of registered boards by registered users\n';
// Get number of registered boards by registered users
resCount =
ReactiveCache.getBoards({ archived: false, type: 'board' }).length /
ReactiveCache.getUsers({}).length; // KPI 4
metricsRes +=
'wekan_registeredboardsBysRegisteredUsers ' + resCount + '\n';
resCount = 0;
//board numbers with only one member
metricsRes += '# Number of registered boards\n';
// Get board numbers with only one member
resCount = ReactiveCache.getBoards({
archived: false,
type: 'board',
members: { $size: 1 },
}).length; // KPI 5
metricsRes +=
'wekan_registeredboardsWithOnlyOneMember ' + resCount + '\n';
resCount = 0;
// KPI 6 : - store last login date
// KPI 6 = count where date of last connection > x days
// Cutting in label since 5 days / 10 days / 20 days / 30 days
//Number of users with last connection dated 5 days ago
metricsRes +=
'# Number of users with last connection dated 5 days ago\n';
// Get number of users with last connection dated 5 days ago
let xdays = 5;
let dateWithXdaysAgo = new Date(
new Date() - xdays * 24 * 60 * 60 * 1000,
);
resCount = ReactiveCache.getUsers({
lastConnectionDate: { $gte: dateWithXdaysAgo },
}).length; // KPI 5
metricsRes +=
'wekan_usersWithLastConnectionDated5DaysAgo ' + resCount + '\n';
resCount = 0;
metricsRes +=
'# Number of users with last connection dated 10 days ago\n';
// Get number of users with last connection dated 10 days ago
xdays = 10;
dateWithXdaysAgo = new Date(new Date() - xdays * 24 * 60 * 60 * 1000);
resCount = ReactiveCache.getUsers({
lastConnectionDate: { $gte: dateWithXdaysAgo },
}).length; // KPI 5
metricsRes +=
'wekan_usersWithLastConnectionDated10DaysAgo ' + resCount + '\n';
resCount = 0;
metricsRes +=
'# Number of users with last connection dated 20 days ago\n';
// Get number of users with last connection dated 20 days ago
xdays = 20;
dateWithXdaysAgo = new Date(new Date() - xdays * 24 * 60 * 60 * 1000);
resCount = ReactiveCache.getUsers({
lastConnectionDate: { $gte: dateWithXdaysAgo },
}).length; // KPI 5
metricsRes +=
'wekan_usersWithLastConnectionDated20DaysAgo ' + resCount + '\n';
resCount = 0;
metricsRes +=
'# Number of users with last connection dated 30 days ago\n';
// Get number of users with last connection dated 20 days ago
xdays = 30;
dateWithXdaysAgo = new Date(new Date() - xdays * 24 * 60 * 60 * 1000);
resCount = ReactiveCache.getUsers({
lastConnectionDate: { $gte: dateWithXdaysAgo },
}).length; // KPI 5
metricsRes +=
'wekan_usersWithLastConnectionDated30DaysAgo ' + resCount + '\n';
resCount = 0;
// TO DO:
// connection average: ((disconnection date - last connection date) + (last average)) / 2
// KPI 7 : sum of connection average / number of users (ignore users with 0 average)
metricsRes +=
'# Top 10 boards with most activities dated 30 days ago\n';
//Get top 10 table with most activities in current month
const boardTitleWithMostActivities = getBoardTitleWithMostActivities(
dateWithXdaysAgo,
xdays,
);
const boardWithMostActivities = boardTitleWithMostActivities.map(
(board) => board.lookup[0].title,
);
boardWithMostActivities.forEach((title, index) => {
metricsRes +=
`wekan_top10BoardsWithMostActivities{n="${title}"} ${
index + 1
}` + '\n';
});
res.writeHead(200); // HTTP status
res.end(metricsRes);
} else {
res.writeHead(401); // HTTP status
res.end(
'IpAddress: ' +
ipAddress +
' is not authorized to perform this action !!\n',
);
}
} catch (e) {
res.writeHead(500); // HTTP status
res.end(e.toString());
}
});
});