activity.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. import type { Ref, IPage } from '@growi/core';
  2. import {
  3. Types, Document, Model, Schema, SortOrder,
  4. } from 'mongoose';
  5. import mongoosePaginate from 'mongoose-paginate-v2';
  6. import {
  7. IActivity, ISnapshot, AllSupportedActions, SupportedActionType,
  8. AllSupportedTargetModels, SupportedTargetModelType,
  9. AllSupportedEventModels, SupportedEventModelType,
  10. } from '~/interfaces/activity';
  11. import loggerFactory from '../../utils/logger';
  12. import { getOrCreateModel, getModelSafely } from '../util/mongoose-utils';
  13. import Subscription from './subscription';
  14. const logger = loggerFactory('growi:models:activity');
  15. export interface ActivityDocument extends Document {
  16. _id: Types.ObjectId
  17. user: Types.ObjectId
  18. ip: string
  19. endpoint: string
  20. targetModel: SupportedTargetModelType
  21. target: Types.ObjectId
  22. eventModel: SupportedEventModelType
  23. event: Types.ObjectId
  24. action: SupportedActionType
  25. snapshot: ISnapshot
  26. getNotificationTargetUsers(): Promise<any[]>
  27. }
  28. export interface ActivityModel extends Model<ActivityDocument> {
  29. [x:string]: any
  30. getActionUsersFromActivities(activities: ActivityDocument[]): any[]
  31. }
  32. const snapshotSchema = new Schema<ISnapshot>({
  33. username: { type: String, index: true },
  34. });
  35. // TODO: add revision id
  36. const activitySchema = new Schema<ActivityDocument, ActivityModel>({
  37. user: {
  38. type: Schema.Types.ObjectId,
  39. ref: 'User',
  40. index: true,
  41. },
  42. ip: {
  43. type: String,
  44. },
  45. endpoint: {
  46. type: String,
  47. },
  48. targetModel: {
  49. type: String,
  50. enum: AllSupportedTargetModels,
  51. },
  52. target: {
  53. type: Schema.Types.ObjectId,
  54. refPath: 'targetModel',
  55. },
  56. eventModel: {
  57. type: String,
  58. enum: AllSupportedEventModels,
  59. },
  60. event: {
  61. type: Schema.Types.ObjectId,
  62. },
  63. action: {
  64. type: String,
  65. enum: AllSupportedActions,
  66. required: true,
  67. },
  68. snapshot: snapshotSchema,
  69. }, {
  70. timestamps: {
  71. createdAt: true,
  72. updatedAt: false,
  73. },
  74. });
  75. activitySchema.index({ target: 1, action: 1 });
  76. activitySchema.index({
  77. user: 1, target: 1, action: 1, createdAt: 1,
  78. }, { unique: true });
  79. activitySchema.plugin(mongoosePaginate);
  80. activitySchema.post('save', function() {
  81. logger.debug('activity has been created', this);
  82. });
  83. activitySchema.methods.getNotificationTargetUsers = async function() {
  84. const User = getModelSafely('User') || require('~/server/models/user')();
  85. const { user: actionUser, target } = this;
  86. const subscribedUsers = await Subscription.getSubscription(target as unknown as Ref<IPage>);
  87. const notificationUsers = subscribedUsers.filter(item => (item.toString() !== actionUser._id.toString()));
  88. const activeNotificationUsers = await User.find({
  89. _id: { $in: notificationUsers },
  90. status: User.STATUS_ACTIVE,
  91. }).distinct('_id');
  92. return activeNotificationUsers;
  93. };
  94. activitySchema.statics.createByParameters = async function(parameters): Promise<IActivity> {
  95. const activity = await this.create(parameters) as unknown as IActivity;
  96. return activity;
  97. };
  98. // When using this method, ensure that activity updates are allowed using ActivityService.shoudUpdateActivity
  99. activitySchema.statics.updateByParameters = async function(activityId: string, parameters): Promise<ActivityDocument | null> {
  100. const activity = await this.findOneAndUpdate({ _id: activityId }, parameters, { new: true }).exec();
  101. return activity;
  102. };
  103. activitySchema.statics.findSnapshotUsernamesByUsernameRegexWithTotalCount = async function(
  104. q: string, option: { sortOpt: SortOrder, offset: number, limit: number},
  105. ): Promise<{usernames: string[], totalCount: number}> {
  106. const opt = option || {};
  107. const sortOpt = opt.sortOpt || 1;
  108. const offset = opt.offset || 0;
  109. const limit = opt.limit || 10;
  110. const conditions = { 'snapshot.username': { $regex: q, $options: 'i' } };
  111. const usernames = await this.aggregate()
  112. .skip(0)
  113. .limit(10000) // Narrow down the search target
  114. .match(conditions)
  115. .group({ _id: '$snapshot.username' })
  116. .sort({ _id: sortOpt }) // Sort "snapshot.username" in ascending order
  117. .skip(offset)
  118. .limit(limit);
  119. const totalCount = (await this.find(conditions).distinct('snapshot.username')).length;
  120. return { usernames: usernames.map(r => r._id), totalCount };
  121. };
  122. export default getOrCreateModel<ActivityDocument, ActivityModel>('Activity', activitySchema);