metrics.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. import { Meteor } from 'meteor/meteor';
  2. import Users from '../users';
  3. function acceptedIpAddress(ipAddress) {
  4. const trustedIpAddress = process.env.METRICS_ACCEPTED_IP_ADDRESS;
  5. return (
  6. trustedIpAddress !== undefined &&
  7. trustedIpAddress.split(',').includes(ipAddress)
  8. );
  9. }
  10. function accessToken(req) {
  11. const valid_token = process.env.METRICS_ACCESS_TOKEN;
  12. let token;
  13. if (req.headers && req.headers.authorization) {
  14. var parts = req.headers.authorization.split(" ");
  15. if (parts.length === 2) {
  16. var scheme = parts[0];
  17. var credentials = parts[1];
  18. if (/^Bearer$/i.test(scheme)) {
  19. token = credentials;
  20. }
  21. }
  22. }
  23. if (!token && req.query && req.query.access_token) {
  24. token = req.query.access_token;
  25. }
  26. return (
  27. token !== undefined &&
  28. valid_token !== undefined &&
  29. token == valid_token
  30. );
  31. }
  32. const getBoardTitleWithMostActivities = (dateWithXdaysAgo, nbLimit) => {
  33. return Promise.await(
  34. Activities.rawCollection()
  35. .aggregate([
  36. {
  37. $match: { modifiedAt: { $gte: dateWithXdaysAgo }}
  38. },
  39. {
  40. $group: { _id: '$boardId', count: { $sum: 1 } }
  41. },
  42. {
  43. $sort: { count: -1 }
  44. },
  45. {
  46. $lookup: { from: 'boards', localField: '_id', foreignField: '_id', as: 'lookup'}
  47. },
  48. {
  49. $project: { "lookup.title":1, "count":1}
  50. }])
  51. .limit(nbLimit).toArray()
  52. );
  53. };
  54. const getBoards = (boardIds) => {
  55. const ret = ReactiveCache.getBoards({ _id: { $in: boardIds } });
  56. return ret;
  57. };
  58. Meteor.startup(() => {
  59. WebApp.connectHandlers.use('/metrics', (req, res, next) => {
  60. try {
  61. const ipAddress =
  62. req.headers['x-forwarded-for'] || req.socket.remoteAddress;
  63. // if(process.env.TRUST_PROXY_FORXARD)
  64. // {
  65. // const ipAddress = req.headers['x-forwarded-for'] || req.socket.remoteAddress
  66. // }else{
  67. // const ipAddress = req.socket.remoteAddress
  68. // }
  69. // List of trusted ip adress will be found in environment variable "METRICS_ACCEPTED_IP_ADDRESS" (separeted with commas)
  70. if (acceptedIpAddress(ipAddress) || (accessToken(req))) {
  71. let metricsRes = '';
  72. let resCount = 0;
  73. //connected users
  74. metricsRes += '# Number of connected users\n';
  75. // Get number of connected user by using meteor socketJs
  76. const allOpenedSockets = Meteor.server.stream_server.open_sockets;
  77. let connectedUserIds = [];
  78. allOpenedSockets.forEach(
  79. (socket) =>
  80. socket._meteorSession.userId !== null &&
  81. connectedUserIds.push(socket._meteorSession.userId),
  82. );
  83. resCount = connectedUserIds.length; // KPI 1
  84. metricsRes += 'wekan_connectedUsers ' + resCount + '\n';
  85. //registered users
  86. metricsRes += '# Number of registered users\n';
  87. // Get number of registered user
  88. resCount = ReactiveCache.getUsers({}).length; // KPI 2
  89. metricsRes += 'wekan_registeredUsers ' + resCount + '\n';
  90. resCount = 0;
  91. //board numbers
  92. metricsRes += '# Number of registered boards\n';
  93. // Get number of registered boards
  94. resCount = ReactiveCache.getBoards({ archived: false, type: 'board' }).length; // KPI 3
  95. metricsRes += 'wekan_registeredboards ' + resCount + '\n';
  96. resCount = 0;
  97. //board numbers by registered users
  98. metricsRes += '# Number of registered boards by registered users\n';
  99. // Get number of registered boards by registered users
  100. resCount =
  101. ReactiveCache.getBoards({ archived: false, type: 'board' }).length /
  102. ReactiveCache.getUsers({}).length; // KPI 4
  103. metricsRes +=
  104. 'wekan_registeredboardsBysRegisteredUsers ' + resCount + '\n';
  105. resCount = 0;
  106. //board numbers with only one member
  107. metricsRes += '# Number of registered boards\n';
  108. // Get board numbers with only one member
  109. resCount = ReactiveCache.getBoards({
  110. archived: false,
  111. type: 'board',
  112. members: { $size: 1 },
  113. }).length; // KPI 5
  114. metricsRes +=
  115. 'wekan_registeredboardsWithOnlyOneMember ' + resCount + '\n';
  116. resCount = 0;
  117. // KPI 6 : - store last login date
  118. // KPI 6 = count where date of last connection > x days
  119. // Cutting in label since 5 days / 10 days / 20 days / 30 days
  120. //Number of users with last connection dated 5 days ago
  121. metricsRes +=
  122. '# Number of users with last connection dated 5 days ago\n';
  123. // Get number of users with last connection dated 5 days ago
  124. let xdays = 5;
  125. let dateWithXdaysAgo = new Date(
  126. new Date() - xdays * 24 * 60 * 60 * 1000,
  127. );
  128. resCount = ReactiveCache.getUsers({
  129. lastConnectionDate: { $gte: dateWithXdaysAgo },
  130. }).length; // KPI 5
  131. metricsRes +=
  132. 'wekan_usersWithLastConnectionDated5DaysAgo ' + resCount + '\n';
  133. resCount = 0;
  134. metricsRes +=
  135. '# Number of users with last connection dated 10 days ago\n';
  136. // Get number of users with last connection dated 10 days ago
  137. xdays = 10;
  138. dateWithXdaysAgo = new Date(new Date() - xdays * 24 * 60 * 60 * 1000);
  139. resCount = ReactiveCache.getUsers({
  140. lastConnectionDate: { $gte: dateWithXdaysAgo },
  141. }).length; // KPI 5
  142. metricsRes +=
  143. 'wekan_usersWithLastConnectionDated10DaysAgo ' + resCount + '\n';
  144. resCount = 0;
  145. metricsRes +=
  146. '# Number of users with last connection dated 20 days ago\n';
  147. // Get number of users with last connection dated 20 days ago
  148. xdays = 20;
  149. dateWithXdaysAgo = new Date(new Date() - xdays * 24 * 60 * 60 * 1000);
  150. resCount = ReactiveCache.getUsers({
  151. lastConnectionDate: { $gte: dateWithXdaysAgo },
  152. }).length; // KPI 5
  153. metricsRes +=
  154. 'wekan_usersWithLastConnectionDated20DaysAgo ' + resCount + '\n';
  155. resCount = 0;
  156. metricsRes +=
  157. '# Number of users with last connection dated 30 days ago\n';
  158. // Get number of users with last connection dated 20 days ago
  159. xdays = 30;
  160. dateWithXdaysAgo = new Date(new Date() - xdays * 24 * 60 * 60 * 1000);
  161. resCount = ReactiveCache.getUsers({
  162. lastConnectionDate: { $gte: dateWithXdaysAgo },
  163. }).length; // KPI 5
  164. metricsRes +=
  165. 'wekan_usersWithLastConnectionDated30DaysAgo ' + resCount + '\n';
  166. resCount = 0;
  167. // TO DO:
  168. // connection average: ((disconnection date - last connection date) + (last average)) / 2
  169. // KPI 7 : sum of connection average / number of users (ignore users with 0 average)
  170. metricsRes +=
  171. '# Top 10 boards with most activities dated 30 days ago\n';
  172. //Get top 10 table with most activities in current month
  173. const boardTitleWithMostActivities = getBoardTitleWithMostActivities(
  174. dateWithXdaysAgo,
  175. xdays,
  176. );
  177. const boardWithMostActivities = boardTitleWithMostActivities.map(
  178. (board) => board.lookup[0].title,
  179. );
  180. boardWithMostActivities.forEach((title, index) => {
  181. metricsRes +=
  182. `wekan_top10BoardsWithMostActivities{n="${title}"} ${
  183. index + 1
  184. }` + '\n';
  185. });
  186. res.writeHead(200); // HTTP status
  187. res.end(metricsRes);
  188. } else {
  189. res.writeHead(401); // HTTP status
  190. res.end(
  191. 'IpAddress: ' +
  192. ipAddress +
  193. ' is not authorized to perform this action !!\n',
  194. );
  195. }
  196. } catch (e) {
  197. res.writeHead(500); // HTTP status
  198. res.end(e.toString());
  199. }
  200. });
  201. });