activity.ts 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. import { parseISO, addMinutes, isValid } from 'date-fns';
  2. import express, { Request, Router } from 'express';
  3. import rateLimit from 'express-rate-limit';
  4. import { query } from 'express-validator';
  5. import { ISearchFilter } from '~/interfaces/activity';
  6. import Activity from '~/server/models/activity';
  7. import loggerFactory from '~/utils/logger';
  8. import Crowi from '../../crowi';
  9. import { apiV3FormValidator } from '../../middlewares/apiv3-form-validator';
  10. import { ApiV3Response } from './interfaces/apiv3-response';
  11. const logger = loggerFactory('growi:routes:apiv3:activity');
  12. const validator = {
  13. list: [
  14. query('limit').optional().isInt({ max: 100 }).withMessage('limit must be a number less than or equal to 100'),
  15. query('offset').optional().isInt().withMessage('page must be a number'),
  16. query('searchFilter').optional().isString().withMessage('query must be a string'),
  17. ],
  18. };
  19. const apiLimiter = rateLimit({
  20. windowMs: 15 * 60 * 1000, // 15 minutes
  21. max: 30, // limit each IP to 30 requests per windowMs
  22. message:
  23. 'Too many requests sent from this IP, please try again after 15 minutes.',
  24. });
  25. module.exports = (crowi: Crowi): Router => {
  26. const adminRequired = require('../../middlewares/admin-required')(crowi);
  27. const accessTokenParser = require('../../middlewares/access-token-parser')(crowi);
  28. const loginRequiredStrictly = require('../../middlewares/login-required')(crowi);
  29. const router = express.Router();
  30. // eslint-disable-next-line max-len
  31. router.get('/', apiLimiter, accessTokenParser, loginRequiredStrictly, adminRequired, validator.list, apiV3FormValidator, async(req: Request, res: ApiV3Response) => {
  32. const auditLogEnabled = crowi.configManager?.getConfig('crowi', 'app:auditLogEnabled') || false;
  33. if (!auditLogEnabled) {
  34. const msg = 'AuditLog is not enabled';
  35. logger.error(msg);
  36. return res.apiv3Err(msg, 405);
  37. }
  38. const limit = req.query.limit || await crowi.configManager?.getConfig('crowi', 'customize:showPageLimitationS') || 10;
  39. const offset = req.query.offset || 1;
  40. const query = {};
  41. try {
  42. const parsedSearchFilter = JSON.parse(req.query.searchFilter as string) as ISearchFilter;
  43. // add username to query
  44. const canContainUsernameFilterToQuery = (
  45. parsedSearchFilter.usernames != null
  46. && parsedSearchFilter.usernames.length > 0
  47. && parsedSearchFilter.usernames.every(u => typeof u === 'string')
  48. );
  49. if (canContainUsernameFilterToQuery) {
  50. Object.assign(query, { 'snapshot.username': parsedSearchFilter.usernames });
  51. }
  52. // add action to query
  53. if (parsedSearchFilter.actions != null) {
  54. const availableActions = crowi.activityService.getAvailableActions(false);
  55. const searchableActions = parsedSearchFilter.actions.filter(action => availableActions.includes(action));
  56. Object.assign(query, { action: searchableActions });
  57. }
  58. // add date to query
  59. const startDate = parseISO(parsedSearchFilter?.dates?.startDate || '');
  60. const endDate = parseISO(parsedSearchFilter?.dates?.endDate || '');
  61. if (isValid(startDate) && isValid(endDate)) {
  62. Object.assign(query, {
  63. createdAt: {
  64. $gte: startDate,
  65. // + 23 hours 59 minutes
  66. $lt: addMinutes(endDate, 1439),
  67. },
  68. });
  69. }
  70. else if (isValid(startDate) && !isValid(endDate)) {
  71. Object.assign(query, {
  72. createdAt: {
  73. $gte: startDate,
  74. // + 23 hours 59 minutes
  75. $lt: addMinutes(startDate, 1439),
  76. },
  77. });
  78. }
  79. }
  80. catch (err) {
  81. logger.error('Invalid value', err);
  82. return res.apiv3Err(err, 400);
  83. }
  84. try {
  85. const paginationResult = await Activity.getPaginatedActivity(limit, offset, query);
  86. return res.apiv3({ paginationResult });
  87. }
  88. catch (err) {
  89. logger.error('Failed to get paginated activity', err);
  90. return res.apiv3Err(err, 500);
  91. }
  92. });
  93. return router;
  94. };