Explorar el Código

copy and paste notification model of crowi

kaori hace 4 años
padre
commit
9782e54b13
Se han modificado 1 ficheros con 208 adiciones y 0 borrados
  1. 208 0
      packages/app/src/server/models/notification.ts

+ 208 - 0
packages/app/src/server/models/notification.ts

@@ -0,0 +1,208 @@
+import {
+  Types, Document, Model, Schema, Query, model,
+} from 'mongoose';
+import Debug from 'debug';
+import { subDays } from 'date-fns';
+import ActivityDefine from 'server/util/activityDefine';
+import Crowi from '../crowi';
+import { ActivityDocument } from './activity';
+import { UserDocument } from './user';
+
+const STATUS_UNREAD = 'UNREAD';
+const STATUS_UNOPENED = 'UNOPENED';
+const STATUS_OPENED = 'OPENED';
+const STATUSES = [STATUS_UNREAD, STATUS_UNOPENED, STATUS_OPENED];
+
+export interface NotificationDocument extends Document {
+  _id: Types.ObjectId
+  user: Types.ObjectId
+  targetModel: string
+  target: Types.ObjectId
+  action: string
+  activities: Types.ObjectId[]
+  status: string
+  createdAt: Date
+}
+
+export interface NotificationModel extends Model<NotificationDocument> {
+  findLatestNotificationsByUser(user: Types.ObjectId, skip: number, offset: number): Promise<NotificationDocument[]>
+  upsertByActivity(user: Types.ObjectId, activity: ActivityDocument, createdAt?: Date | null): Promise<NotificationDocument | null>
+  removeActivity(activity: any): any
+  removeEmpty(): Query<any>
+  read(user: UserDocument): Promise<Query<any>>
+  open(user: UserDocument, id: Types.ObjectId): Promise<NotificationDocument | null>
+  getUnreadCountByUser(user: Types.ObjectId): Promise<number | undefined>
+
+  STATUS_UNREAD: string
+  STATUS_UNOPENED: string
+  STATUS_OPENED: string
+}
+
+export default (crowi: Crowi) => {
+  const debug = Debug('crowi:models:notification');
+  const notificationEvent = crowi.event('Notification');
+
+  const notificationSchema = new Schema<NotificationDocument, NotificationModel>({
+    user: {
+      type: Schema.Types.ObjectId,
+      ref: 'User',
+      index: true,
+      require: true,
+    },
+    targetModel: {
+      type: String,
+      require: true,
+      enum: ActivityDefine.getSupportTargetModelNames(),
+    },
+    target: {
+      type: Schema.Types.ObjectId,
+      refPath: 'targetModel',
+      require: true,
+    },
+    action: {
+      type: String,
+      require: true,
+      enum: ActivityDefine.getSupportActionNames(),
+    },
+    activities: [
+      {
+        type: Schema.Types.ObjectId,
+        ref: 'Activity',
+      },
+    ],
+    status: {
+      type: String,
+      default: STATUS_UNREAD,
+      enum: STATUSES,
+      index: true,
+      require: true,
+    },
+    createdAt: {
+      type: Date,
+      default: Date.now,
+    },
+  });
+  notificationSchema.virtual('actionUsers').get(function(this: NotificationDocument) {
+    const Activity = crowi.model('Activity');
+    return Activity.getActionUsersFromActivities((this.activities as any) as ActivityDocument[]);
+  });
+  const transform = (doc, ret) => {
+    // delete ret.activities
+  };
+  notificationSchema.set('toObject', { virtuals: true, transform });
+  notificationSchema.set('toJSON', { virtuals: true, transform });
+  notificationSchema.index({
+    user: 1, target: 1, action: 1, createdAt: 1,
+  });
+
+  notificationSchema.statics.findLatestNotificationsByUser = function(user, limit, offset) {
+    limit = limit || 10;
+
+    return Notification.find({ user })
+      .sort({ createdAt: -1 })
+      .skip(offset)
+      .limit(limit)
+      .populate(['user', 'target'])
+      .populate({ path: 'activities', populate: { path: 'user' } })
+      .exec();
+  };
+
+  notificationSchema.statics.upsertByActivity = async function(user, activity, createdAt = null) {
+    const {
+      _id: activityId, targetModel, target, action,
+    } = activity;
+
+    const now = createdAt || Date.now();
+    const lastWeek = subDays(now, 7);
+    const query = {
+      user, target, action, createdAt: { $gt: lastWeek },
+    };
+    const parameters = {
+      user,
+      targetModel,
+      target,
+      action,
+      status: STATUS_UNREAD,
+      createdAt: now,
+      $addToSet: { activities: activityId },
+    };
+
+    const options = {
+      upsert: true,
+      new: true,
+      setDefaultsOnInsert: true,
+      runValidators: true,
+    };
+
+    const notification = await Notification.findOneAndUpdate(query, parameters, options);
+
+    if (notification) {
+      notificationEvent.emit('update', notification.user);
+    }
+
+    return notification;
+  };
+
+  notificationSchema.statics.removeActivity = async function(activity) {
+    const { _id, target, action } = activity;
+    const query = { target, action };
+    const parameters = { $pull: { activities: _id } };
+
+    const result = await Notification.updateMany(query, parameters);
+
+    await Notification.removeEmpty();
+    return result;
+  };
+
+  notificationSchema.statics.removeEmpty = function() {
+    return Notification.deleteMany({ activities: { $size: 0 } });
+  };
+
+  notificationSchema.statics.read = async function(user) {
+    const query = { user, status: STATUS_UNREAD };
+    const parameters = { status: STATUS_UNOPENED };
+
+    return Notification.updateMany(query, parameters);
+  };
+
+  notificationSchema.statics.open = async function(user, id) {
+    const query = { _id: id, user: user._id };
+    const parameters = { status: STATUS_OPENED };
+    const options = { new: true };
+
+    const notification = await Notification.findOneAndUpdate(query, parameters, options);
+    if (notification) {
+      notificationEvent.emit('update', notification.user);
+    }
+    return notification;
+  };
+
+  notificationSchema.statics.getUnreadCountByUser = async function(user) {
+    const query = { user, status: STATUS_UNREAD };
+
+    try {
+      const count = await Notification.countDocuments(query);
+
+      return count;
+    }
+    catch (err) {
+      debug('Error on getUnreadCountByUser', err);
+      throw err;
+    }
+  };
+
+  notificationEvent.on('update', (user) => {
+    const io = crowi.getIo();
+    if (io) {
+      io.sockets.emit('notification updated', { user });
+    }
+  });
+
+  notificationSchema.statics.STATUS_UNOPENED = STATUS_UNOPENED;
+  notificationSchema.statics.STATUS_UNREAD = STATUS_UNREAD;
+  notificationSchema.statics.STATUS_OPENED = STATUS_OPENED;
+
+  const Notification = model<NotificationDocument, NotificationModel>('Notification', notificationSchema);
+
+  return Notification;
+};