Просмотр исходного кода

Merge pull request #5766 from weseek/feat/93690-api-to-retrieve-paginated-activity

feat: API to retrieve paginated activity
Yuki Takei 4 лет назад
Родитель
Сommit
8b7d96525f

+ 15 - 0
packages/app/src/server/models/activity.ts

@@ -2,6 +2,7 @@ import { getOrCreateModel, getModelSafely } from '@growi/core';
 import {
   Types, Document, Model, Schema,
 } from 'mongoose';
+import mongoosePaginate from 'mongoose-paginate-v2';
 
 import { AllSupportedTargetModelType, AllSupportedEventModelType, AllSupportedActionType } from '~/interfaces/activity';
 
@@ -25,6 +26,7 @@ export interface ActivityDocument extends Document {
 }
 
 export interface ActivityModel extends Model<ActivityDocument> {
+  [x:string]: any
   getActionUsersFromActivities(activities: ActivityDocument[]): any[]
 }
 // TODO: add revision id
@@ -65,6 +67,7 @@ activitySchema.index({ target: 1, action: 1 });
 activitySchema.index({
   user: 1, target: 1, action: 1, createdAt: 1,
 }, { unique: true });
+activitySchema.plugin(mongoosePaginate);
 
 
 activitySchema.methods.getNotificationTargetUsers = async function() {
@@ -101,4 +104,16 @@ activitySchema.post('save', async(savedActivity: ActivityDocument) => {
   activityEvent.emit('create', targetUsers, savedActivity);
 });
 
+activitySchema.statics.getPaginatedActivity = async function(limit: number, offset: number) {
+  const paginateResult = await this.paginate(
+    {},
+    {
+      limit,
+      offset,
+      sort: { createdAt: -1 },
+    },
+  );
+  return paginateResult;
+};
+
 export default getOrCreateModel<ActivityDocument, ActivityModel>('Activity', activitySchema);

+ 53 - 0
packages/app/src/server/routes/apiv3/activity.ts

@@ -0,0 +1,53 @@
+import express, { Request, Router } from 'express';
+import rateLimit from 'express-rate-limit';
+import { query } from 'express-validator';
+
+import Activity from '~/server/models/activity';
+import loggerFactory from '~/utils/logger';
+
+import Crowi from '../../crowi';
+import { apiV3FormValidator } from '../../middlewares/apiv3-form-validator';
+
+import { ApiV3Response } from './interfaces/apiv3-response';
+
+const logger = loggerFactory('growi:routes:apiv3:activity');
+
+
+const validator = {
+  list: [
+    query('limit').optional().isInt({ max: 100 }).withMessage('limit must be a number less than or equal to 100'),
+    query('offset').optional().isInt().withMessage('page must be a number'),
+  ],
+};
+
+const apiLimiter = rateLimit({
+  windowMs: 15 * 60 * 1000, // 15 minutes
+  max: 10, // limit each IP to 10 requests per windowMs
+  message:
+    'Too many requests were sent from this IP. Please try a password reset request again on the password reset request form',
+});
+
+module.exports = (crowi: Crowi): Router => {
+  const adminRequired = require('../../middlewares/admin-required')(crowi);
+  const accessTokenParser = require('../../middlewares/access-token-parser')(crowi);
+  const loginRequiredStrictly = require('../../middlewares/login-required')(crowi);
+
+  const router = express.Router();
+
+  // eslint-disable-next-line max-len
+  router.get('/', apiLimiter, accessTokenParser, loginRequiredStrictly, adminRequired, validator.list, apiV3FormValidator, async(req: Request, res: ApiV3Response) => {
+    const limit = req.query.limit || await crowi.configManager?.getConfig('crowi', 'customize:showPageLimitationS') || 10;
+    const offset = req.query.offset || 1;
+
+    try {
+      const paginatedActivity = await Activity.getPaginatedActivity(limit, offset);
+      return res.apiv3({ paginatedActivity });
+    }
+    catch (err) {
+      logger.error('Failed to get paginated activity', err);
+      return res.apiv3Err(err);
+    }
+  });
+
+  return router;
+};

+ 3 - 1
packages/app/src/server/routes/apiv3/index.js

@@ -1,8 +1,9 @@
 import loggerFactory from '~/utils/logger';
-import * as userActivation from './user-activation';
+
 import injectUserRegistrationOrderByTokenMiddleware from '../../middlewares/inject-user-registration-order-by-token-middleware';
 
 import pageListing from './page-listing';
+import * as userActivation from './user-activation';
 
 const logger = loggerFactory('growi:routes:apiv3'); // eslint-disable-line no-unused-vars
 
@@ -33,6 +34,7 @@ module.exports = (crowi) => {
   routerForAdmin.use('/mongo', require('./mongo')(crowi));
   routerForAdmin.use('/slack-integration-settings', require('./slack-integration-settings')(crowi));
   routerForAdmin.use('/slack-integration-legacy-settings', require('./slack-integration-legacy-settings')(crowi));
+  routerForAdmin.use('/activity', require('./activity')(crowi));
 
 
   router.use('/in-app-notification', require('./in-app-notification')(crowi));

+ 1 - 1
packages/app/src/server/service/activity.ts

@@ -1,6 +1,6 @@
 import { getModelSafely } from '@growi/core';
-import Crowi from '../crowi';
 
+import Crowi from '../crowi';
 
 class ActivityService {