metrics.js 7.1 KB

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