activity.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. import mongoose from 'mongoose';
  2. import {
  3. IActivity, SupportedActionType, ActionGroupSize, AllSupportedAction,
  4. AllSmallGroupActions, AllMediumGroupActions, AllLargeGroupActions, AllSupportedActionToNotified,
  5. } from '~/interfaces/activity';
  6. import { IPage } from '~/interfaces/page';
  7. import Activity from '~/server/models/activity';
  8. import loggerFactory from '../../utils/logger';
  9. import Crowi from '../crowi';
  10. const logger = loggerFactory('growi:service:ActivityService');
  11. const parseActionString = (actionsString: string): SupportedActionType[] => {
  12. if (actionsString == null) {
  13. return [];
  14. }
  15. const actions = actionsString.split(',').map(value => value.trim());
  16. return actions.filter(action => (AllSupportedAction as ReadonlyArray<string>).includes(action)) as SupportedActionType[];
  17. };
  18. class ActivityService {
  19. crowi!: Crowi;
  20. activityEvent: any;
  21. constructor(crowi: Crowi) {
  22. this.crowi = crowi;
  23. this.activityEvent = crowi.event('activity');
  24. this.getAvailableActions = this.getAvailableActions.bind(this);
  25. this.shoudUpdateActivity = this.shoudUpdateActivity.bind(this);
  26. this.initActivityEventListeners();
  27. }
  28. initActivityEventListeners(): void {
  29. this.activityEvent.on('update', async(activityId: string, parameters, target?: IPage) => {
  30. let activity: IActivity;
  31. const shoudUpdate = this.shoudUpdateActivity(parameters.action);
  32. if (shoudUpdate) {
  33. try {
  34. activity = await Activity.updateByParameters(activityId, parameters);
  35. }
  36. catch (err) {
  37. logger.error('Update activity failed', err);
  38. return;
  39. }
  40. this.activityEvent.emit('updated', activity, target);
  41. }
  42. });
  43. }
  44. getAvailableActions = function(): SupportedActionType[] {
  45. const auditLogActionGroupSize = this.crowi.configManager.getConfig('crowi', 'app:auditLogActionGroupSize') || ActionGroupSize.Small;
  46. const auditLogAdditionalActions = this.crowi.configManager.getConfig('crowi', 'app:auditLogAdditionalActions');
  47. const auditLogExcludeActions = this.crowi.configManager.getConfig('crowi', 'app:auditLogExcludeActions');
  48. // Set base action group
  49. const availableActions: SupportedActionType[] = [];
  50. switch (auditLogActionGroupSize) {
  51. case ActionGroupSize.Small:
  52. availableActions.push(...AllSmallGroupActions);
  53. break;
  54. case ActionGroupSize.Medium:
  55. availableActions.push(...AllMediumGroupActions);
  56. break;
  57. case ActionGroupSize.Large:
  58. availableActions.push(...AllLargeGroupActions);
  59. break;
  60. }
  61. // Push additionalActions
  62. const additionalActions = parseActionString(auditLogAdditionalActions);
  63. availableActions.push(...additionalActions);
  64. // Filter with excludeActions
  65. const excludeActions = parseActionString(auditLogExcludeActions);
  66. const filteredAvailableActions = availableActions.filter(action => !excludeActions.includes(action));
  67. // Push essentialActions
  68. filteredAvailableActions.push(...AllSupportedActionToNotified);
  69. return Array.from(new Set(filteredAvailableActions));
  70. }
  71. shoudUpdateActivity = function(action: SupportedActionType): boolean {
  72. console.log(this.getAvailableActions());
  73. return this.getAvailableActions().includes(action);
  74. }
  75. createTtlIndex = async function() {
  76. const configManager = this.crowi.configManager;
  77. const activityExpirationSeconds = configManager != null ? configManager.getConfig('crowi', 'app:activityExpirationSeconds') : 2592000;
  78. const collection = mongoose.connection.collection('activities');
  79. try {
  80. const targetField = 'createdAt_1';
  81. const indexes = await collection.indexes();
  82. const foundCreatedAt = indexes.find(i => i.name === targetField);
  83. const isNotSpec = foundCreatedAt?.expireAfterSeconds == null || foundCreatedAt?.expireAfterSeconds !== activityExpirationSeconds;
  84. const shoudDropIndex = foundCreatedAt != null && isNotSpec;
  85. const shoudCreateIndex = foundCreatedAt == null || shoudDropIndex;
  86. if (shoudDropIndex) {
  87. await collection.dropIndex(targetField);
  88. }
  89. if (shoudCreateIndex) {
  90. await collection.createIndex({ createdAt: 1 }, { expireAfterSeconds: activityExpirationSeconds });
  91. }
  92. }
  93. catch (err) {
  94. logger.error('Failed to create TTL Index', err);
  95. throw err;
  96. }
  97. };
  98. }
  99. module.exports = ActivityService;