Bladeren bron

Merge pull request #6162 from weseek/feat/97283-create-activit-by-general-user-operation

feat: Create activity by general user operation
Shun Miyazawa 3 jaren geleden
bovenliggende
commit
3c8de3e2ff

+ 22 - 1
packages/app/src/components/Admin/AuditLog/SelectActionDropdown.tsx

@@ -3,7 +3,8 @@ import React, { FC, useMemo, useCallback } from 'react';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
 
 
 import {
 import {
-  SupportedActionType, SupportedActionCategoryType, SupportedActionCategory, PageActions, CommentActions, UserActions, AdminActions,
+  SupportedActionType, SupportedActionCategoryType, SupportedActionCategory,
+  PageActions, CommentActions, TagActions, ShareLinkActions, AttachmentActions, InAppNotificationActions, SearchActions, UserActions, AdminActions,
 } from '~/interfaces/activity';
 } from '~/interfaces/activity';
 
 
 type Props = {
 type Props = {
@@ -30,6 +31,26 @@ export const SelectActionDropdown: FC<Props> = (props: Props) => {
           actionCategory: SupportedActionCategory.COMMENT,
           actionCategory: SupportedActionCategory.COMMENT,
           actions: CommentActions.filter(action => availableActions.includes(action)),
           actions: CommentActions.filter(action => availableActions.includes(action)),
         },
         },
+        {
+          actionCategory: SupportedActionCategory.TAG,
+          actions: TagActions.filter(action => availableActions.includes(action)),
+        },
+        {
+          actionCategory: SupportedActionCategory.ATTACHMENT,
+          actions: AttachmentActions.filter(action => availableActions.includes(action)),
+        },
+        {
+          actionCategory: SupportedActionCategory.SHARE_LINK,
+          actions: ShareLinkActions.filter(action => availableActions.includes(action)),
+        },
+        {
+          actionCategory: SupportedActionCategory.IN_APP_NOTIFICATION,
+          actions: InAppNotificationActions.filter(action => availableActions.includes(action)),
+        },
+        {
+          actionCategory: SupportedActionCategory.SEARCH,
+          actions: SearchActions.filter(action => availableActions.includes(action)),
+        },
         {
         {
           actionCategory: SupportedActionCategory.USER,
           actionCategory: SupportedActionCategory.USER,
           actions: UserActions.filter(action => availableActions.includes(action)),
           actions: UserActions.filter(action => availableActions.includes(action)),

+ 98 - 6
packages/app/src/interfaces/activity.ts

@@ -9,7 +9,14 @@ const MODEL_COMMENT = 'Comment';
 // Action
 // Action
 const ACTION_UNSETTLED = 'UNSETTLED';
 const ACTION_UNSETTLED = 'UNSETTLED';
 const ACTION_USER_REGISTRATION_SUCCESS = 'USER_REGISTRATION_SUCCESS';
 const ACTION_USER_REGISTRATION_SUCCESS = 'USER_REGISTRATION_SUCCESS';
-const ACTION_USER_LOGIN_SUCCESS = 'USER_LOGIN_SUCCESS';
+const ACTION_USER_LOGIN_WITH_LOCAL = 'USER_LOGIN_WITH_LOCAL';
+const ACTION_USER_LOGIN_WITH_LDAP = 'USER_LOGIN_WITH_LDAP';
+const ACTION_USER_LOGIN_WITH_GOOGLE = 'USER_LOGIN_WITH_GOOGLE';
+const ACTION_USER_LOGIN_WITH_GITHUB = 'USER_LOGIN_WITH_GITHUB';
+const ACTION_USER_LOGIN_WITH_TWITTER = 'USER_LOGIN_WITH_TWITTER';
+const ACTION_USER_LOGIN_WITH_OIDC = 'USER_LOGIN_WITH_OIDC';
+const ACTION_USER_LOGIN_WITH_SAML = 'USER_LOGIN_WITH_SAML';
+const ACTION_USER_LOGIN_WITH_BASIC = 'USER_LOGIN_WITH_BASIC';
 const ACTION_USER_LOGIN_FAILURE = 'USER_LOGIN_FAILURE';
 const ACTION_USER_LOGIN_FAILURE = 'USER_LOGIN_FAILURE';
 const ACTION_USER_LOGOUT = 'USER_LOGOUT';
 const ACTION_USER_LOGOUT = 'USER_LOGOUT';
 const ACTION_USER_PERSONAL_SETTINGS_UPDATE = 'USER_PERSONAL_SETTINGS_UPDATE';
 const ACTION_USER_PERSONAL_SETTINGS_UPDATE = 'USER_PERSONAL_SETTINGS_UPDATE';
@@ -21,6 +28,10 @@ const ACTION_USER_API_TOKEN_UPDATE = 'USER_API_TOKEN_UPDATE';
 const ACTION_USER_EDITOR_SETTINGS_UPDATE = 'USER_EDITOR_SETTINGS_UPDATE';
 const ACTION_USER_EDITOR_SETTINGS_UPDATE = 'USER_EDITOR_SETTINGS_UPDATE';
 const ACTION_USER_IN_APP_NOTIFICATION_SETTINGS_UPDATE = 'USER_IN_APP_NOTIFICATION_SETTINGS_UPDATE';
 const ACTION_USER_IN_APP_NOTIFICATION_SETTINGS_UPDATE = 'USER_IN_APP_NOTIFICATION_SETTINGS_UPDATE';
 const ACTION_PAGE_VIEW = 'PAGE_VIEW';
 const ACTION_PAGE_VIEW = 'PAGE_VIEW';
+const ACTION_PAGE_USER_HOME_VIEW = 'PAGE_USER_HOME_VIEW';
+const ACTION_PAGE_NOT_FOUND = 'PAGE_NOT_FOUND';
+const ACTION_PAGE_FORBIDDEN = 'PAGE_FORBIDDEN';
+const ACTION_PAGE_NOT_CREATABLE = 'PAGE_NOT_CREATABLE';
 const ACTION_PAGE_LIKE = 'PAGE_LIKE';
 const ACTION_PAGE_LIKE = 'PAGE_LIKE';
 const ACTION_PAGE_UNLIKE = 'PAGE_UNLIKE';
 const ACTION_PAGE_UNLIKE = 'PAGE_UNLIKE';
 const ACTION_PAGE_BOOKMARK = 'PAGE_BOOKMARK';
 const ACTION_PAGE_BOOKMARK = 'PAGE_BOOKMARK';
@@ -32,9 +43,27 @@ const ACTION_PAGE_DUPLICATE = 'PAGE_DUPLICATE';
 const ACTION_PAGE_DELETE = 'PAGE_DELETE';
 const ACTION_PAGE_DELETE = 'PAGE_DELETE';
 const ACTION_PAGE_DELETE_COMPLETELY = 'PAGE_DELETE_COMPLETELY';
 const ACTION_PAGE_DELETE_COMPLETELY = 'PAGE_DELETE_COMPLETELY';
 const ACTION_PAGE_REVERT = 'PAGE_REVERT';
 const ACTION_PAGE_REVERT = 'PAGE_REVERT';
+const ACTION_PAGE_EMPTY_TRASH = 'PAGE_EMPTY_TRASH';
+const ACTION_PAGE_SUBSCRIBE = 'PAGE_SUBSCRIBE';
+const ACTION_PAGE_UNSUBSCRIBE = 'PAGE_UNSUBSCRIBE';
+const ACTION_PAGE_EXPORT = 'PAGE_EXPORT';
+const ACTION_TAG_UPDATE = 'TAG_UPDATE';
+const ACTION_IN_APP_NOTIFICATION_ALL_STATUSES_OPEN = 'IN_APP_NOTIFICATION_ALL_STATUSES_OPEN';
 const ACTION_COMMENT_CREATE = 'COMMENT_CREATE';
 const ACTION_COMMENT_CREATE = 'COMMENT_CREATE';
 const ACTION_COMMENT_UPDATE = 'COMMENT_UPDATE';
 const ACTION_COMMENT_UPDATE = 'COMMENT_UPDATE';
 const ACTION_COMMENT_REMOVE = 'COMMENT_REMOVE';
 const ACTION_COMMENT_REMOVE = 'COMMENT_REMOVE';
+const ACTION_SHARE_LINK_CREATE = 'SHARE_LINK_CREATE';
+const ACTION_SHARE_LINK_DELETE = 'SHARE_LINK_DELETE';
+const ACTION_SHARE_LINK_DELETE_BY_PAGE = 'SHARE_LINK_DELETE_BY_PAGE';
+const ACTION_SHARE_LINK_ALL_DELETE = 'SHARE_LINK_ALL_DELETE';
+const ACTION_SHARE_LINK_PAGE_VIEW = 'SHARE_LINK_PAGE_VIEW';
+const ACTION_SHARE_LINK_EXPIRED_PAGE_VIEW = 'SHARE_LINK_EXPIRED_PAGE_VIEW';
+const ACTION_SHARE_LINK_NOT_FOUND = 'SHARE_LINK_NOT_FOUND';
+const ACTION_ATTACHMENT_ADD = 'ATTACHMENT_ADD';
+const ACTION_ATTACHMENT_REMOVE = 'ATTACHMENT_REMOVE';
+const ACTION_ATTACHMENT_DOWNLOAD = 'ACTION_ATTACHMENT_DOWNLOAD';
+const ACTION_SEARCH_PAGE = 'SEARCH_PAGE';
+const ACTION_SEARCH_PAGE_VIEW = 'SEARCH_PAGE_VIEW';
 const ACTION_ADMIN_APP_SETTINGS_UPDATE = 'ADMIN_APP_SETTING_UPDATE';
 const ACTION_ADMIN_APP_SETTINGS_UPDATE = 'ADMIN_APP_SETTING_UPDATE';
 const ACTION_ADMIN_SITE_URL_UPDATE = 'ADMIN_SITE_URL_UPDATE';
 const ACTION_ADMIN_SITE_URL_UPDATE = 'ADMIN_SITE_URL_UPDATE';
 const ACTION_ADMIN_MAIL_SMTP_UPDATE = 'ADMIN_MAIL_SMTP_UPDATE';
 const ACTION_ADMIN_MAIL_SMTP_UPDATE = 'ADMIN_MAIL_SMTP_UPDATE';
@@ -47,7 +76,6 @@ const ACTION_ADMIN_MAINTENANCEMODE_DISABLED = 'ADMIN_MAINTENANCEMODE_DISABLED';
 const ACTION_ADMIN_SECURITY_SETTINGS_UPDATE = 'ADMIN_SECURITY_SETTINGS_UPDATE';
 const ACTION_ADMIN_SECURITY_SETTINGS_UPDATE = 'ADMIN_SECURITY_SETTINGS_UPDATE';
 const ACTION_ADMIN_PERMIT_SHARE_LINK = 'ADMIN_PERMIT_SHARE_LINK';
 const ACTION_ADMIN_PERMIT_SHARE_LINK = 'ADMIN_PERMIT_SHARE_LINK';
 const ACTION_ADMIN_REJECT_SHARE_LINK = 'ADMIN_REJECT_SHARE_LINK';
 const ACTION_ADMIN_REJECT_SHARE_LINK = 'ADMIN_REJECT_SHARE_LINK';
-const ACTION_ADMIN_DELETE_ALL_SHARE_LINK = 'ADMIN_DELETE_ALL_SHARE_LINK';
 const ACTION_ADMIN_AUTH_ID_PASS_ENABLED = 'ADMIN_AUTH_ID_PASS_ENABLED';
 const ACTION_ADMIN_AUTH_ID_PASS_ENABLED = 'ADMIN_AUTH_ID_PASS_ENABLED';
 const ACTION_ADMIN_AUTH_ID_PASS_DISABLED = 'ADMIN_AUTH_ID_PASS_DISABLED';
 const ACTION_ADMIN_AUTH_ID_PASS_DISABLED = 'ADMIN_AUTH_ID_PASS_DISABLED';
 const ACTION_ADMIN_AUTH_ID_PASS_UPDATE = 'ADMIN_AUTH_ID_PASS_UPDATE';
 const ACTION_ADMIN_AUTH_ID_PASS_UPDATE = 'ADMIN_AUTH_ID_PASS_UPDATE';
@@ -109,6 +137,11 @@ export const SupportedEventModel = {
 export const SupportedActionCategory = {
 export const SupportedActionCategory = {
   PAGE: 'Page',
   PAGE: 'Page',
   COMMENT: 'Comment',
   COMMENT: 'Comment',
+  TAG: 'Tag',
+  ATTACHMENT: 'Attachment',
+  SHARE_LINK: 'ShareLink',
+  IN_APP_NOTIFICATION: 'InAppNotification',
+  SEARCH: 'Search',
   USER: 'User',
   USER: 'User',
   ADMIN: 'Admin',
   ADMIN: 'Admin',
 } as const;
 } as const;
@@ -116,7 +149,14 @@ export const SupportedActionCategory = {
 export const SupportedAction = {
 export const SupportedAction = {
   ACTION_UNSETTLED,
   ACTION_UNSETTLED,
   ACTION_USER_REGISTRATION_SUCCESS,
   ACTION_USER_REGISTRATION_SUCCESS,
-  ACTION_USER_LOGIN_SUCCESS,
+  ACTION_USER_LOGIN_WITH_LOCAL,
+  ACTION_USER_LOGIN_WITH_LDAP,
+  ACTION_USER_LOGIN_WITH_GOOGLE,
+  ACTION_USER_LOGIN_WITH_GITHUB,
+  ACTION_USER_LOGIN_WITH_TWITTER,
+  ACTION_USER_LOGIN_WITH_OIDC,
+  ACTION_USER_LOGIN_WITH_SAML,
+  ACTION_USER_LOGIN_WITH_BASIC,
   ACTION_USER_LOGIN_FAILURE,
   ACTION_USER_LOGIN_FAILURE,
   ACTION_USER_LOGOUT,
   ACTION_USER_LOGOUT,
   ACTION_USER_PERSONAL_SETTINGS_UPDATE,
   ACTION_USER_PERSONAL_SETTINGS_UPDATE,
@@ -128,6 +168,10 @@ export const SupportedAction = {
   ACTION_USER_EDITOR_SETTINGS_UPDATE,
   ACTION_USER_EDITOR_SETTINGS_UPDATE,
   ACTION_USER_IN_APP_NOTIFICATION_SETTINGS_UPDATE,
   ACTION_USER_IN_APP_NOTIFICATION_SETTINGS_UPDATE,
   ACTION_PAGE_VIEW,
   ACTION_PAGE_VIEW,
+  ACTION_PAGE_USER_HOME_VIEW,
+  ACTION_PAGE_FORBIDDEN,
+  ACTION_PAGE_NOT_FOUND,
+  ACTION_PAGE_NOT_CREATABLE,
   ACTION_PAGE_LIKE,
   ACTION_PAGE_LIKE,
   ACTION_PAGE_UNLIKE,
   ACTION_PAGE_UNLIKE,
   ACTION_PAGE_BOOKMARK,
   ACTION_PAGE_BOOKMARK,
@@ -139,9 +183,27 @@ export const SupportedAction = {
   ACTION_PAGE_DELETE,
   ACTION_PAGE_DELETE,
   ACTION_PAGE_DELETE_COMPLETELY,
   ACTION_PAGE_DELETE_COMPLETELY,
   ACTION_PAGE_REVERT,
   ACTION_PAGE_REVERT,
+  ACTION_PAGE_EMPTY_TRASH,
+  ACTION_PAGE_SUBSCRIBE,
+  ACTION_PAGE_UNSUBSCRIBE,
+  ACTION_PAGE_EXPORT,
+  ACTION_TAG_UPDATE,
+  ACTION_IN_APP_NOTIFICATION_ALL_STATUSES_OPEN,
   ACTION_COMMENT_CREATE,
   ACTION_COMMENT_CREATE,
   ACTION_COMMENT_UPDATE,
   ACTION_COMMENT_UPDATE,
   ACTION_COMMENT_REMOVE,
   ACTION_COMMENT_REMOVE,
+  ACTION_SHARE_LINK_CREATE,
+  ACTION_SHARE_LINK_DELETE,
+  ACTION_SHARE_LINK_DELETE_BY_PAGE,
+  ACTION_SHARE_LINK_ALL_DELETE,
+  ACTION_SHARE_LINK_PAGE_VIEW,
+  ACTION_SHARE_LINK_EXPIRED_PAGE_VIEW,
+  ACTION_SHARE_LINK_NOT_FOUND,
+  ACTION_ATTACHMENT_ADD,
+  ACTION_ATTACHMENT_REMOVE,
+  ACTION_ATTACHMENT_DOWNLOAD,
+  ACTION_SEARCH_PAGE,
+  ACTION_SEARCH_PAGE_VIEW,
   ACTION_ADMIN_APP_SETTINGS_UPDATE,
   ACTION_ADMIN_APP_SETTINGS_UPDATE,
   ACTION_ADMIN_SITE_URL_UPDATE,
   ACTION_ADMIN_SITE_URL_UPDATE,
   ACTION_ADMIN_MAIL_SMTP_UPDATE,
   ACTION_ADMIN_MAIL_SMTP_UPDATE,
@@ -154,7 +216,6 @@ export const SupportedAction = {
   ACTION_ADMIN_SECURITY_SETTINGS_UPDATE,
   ACTION_ADMIN_SECURITY_SETTINGS_UPDATE,
   ACTION_ADMIN_PERMIT_SHARE_LINK,
   ACTION_ADMIN_PERMIT_SHARE_LINK,
   ACTION_ADMIN_REJECT_SHARE_LINK,
   ACTION_ADMIN_REJECT_SHARE_LINK,
-  ACTION_ADMIN_DELETE_ALL_SHARE_LINK,
   ACTION_ADMIN_AUTH_ID_PASS_ENABLED,
   ACTION_ADMIN_AUTH_ID_PASS_ENABLED,
   ACTION_ADMIN_AUTH_ID_PASS_DISABLED,
   ACTION_ADMIN_AUTH_ID_PASS_DISABLED,
   ACTION_ADMIN_AUTH_ID_PASS_UPDATE,
   ACTION_ADMIN_AUTH_ID_PASS_UPDATE,
@@ -225,7 +286,14 @@ export const ActionGroupSize = {
 } as const;
 } as const;
 
 
 export const SmallActionGroup = {
 export const SmallActionGroup = {
-  ACTION_USER_LOGIN_SUCCESS,
+  ACTION_USER_LOGIN_WITH_LOCAL,
+  ACTION_USER_LOGIN_WITH_LDAP,
+  ACTION_USER_LOGIN_WITH_GOOGLE,
+  ACTION_USER_LOGIN_WITH_GITHUB,
+  ACTION_USER_LOGIN_WITH_TWITTER,
+  ACTION_USER_LOGIN_WITH_OIDC,
+  ACTION_USER_LOGIN_WITH_SAML,
+  ACTION_USER_LOGIN_WITH_BASIC,
   ACTION_USER_LOGIN_FAILURE,
   ACTION_USER_LOGIN_FAILURE,
   ACTION_USER_LOGOUT,
   ACTION_USER_LOGOUT,
   ACTION_PAGE_CREATE,
   ACTION_PAGE_CREATE,
@@ -255,14 +323,29 @@ export const MediumActionGroup = {
   ACTION_PAGE_DELETE,
   ACTION_PAGE_DELETE,
   ACTION_PAGE_DELETE_COMPLETELY,
   ACTION_PAGE_DELETE_COMPLETELY,
   ACTION_PAGE_REVERT,
   ACTION_PAGE_REVERT,
+  ACTION_PAGE_EMPTY_TRASH,
+  ACTION_PAGE_SUBSCRIBE,
+  ACTION_PAGE_UNSUBSCRIBE,
+  ACTION_PAGE_EXPORT,
+  ACTION_TAG_UPDATE,
+  ACTION_IN_APP_NOTIFICATION_ALL_STATUSES_OPEN,
   ACTION_COMMENT_CREATE,
   ACTION_COMMENT_CREATE,
   ACTION_COMMENT_UPDATE,
   ACTION_COMMENT_UPDATE,
   ACTION_COMMENT_REMOVE,
   ACTION_COMMENT_REMOVE,
+  ACTION_SHARE_LINK_CREATE,
+  ACTION_SHARE_LINK_DELETE,
+  ACTION_SHARE_LINK_DELETE_BY_PAGE,
+  ACTION_ATTACHMENT_ADD,
+  ACTION_ATTACHMENT_REMOVE,
+  ACTION_ATTACHMENT_DOWNLOAD,
+  ACTION_SEARCH_PAGE,
+  ACTION_SEARCH_PAGE_VIEW,
 } as const;
 } as const;
 
 
 // MediumActionGroup + All Actions by Admin Users - PAGE_VIEW
 // MediumActionGroup + All Actions by Admin Users - PAGE_VIEW
 export const LargeActionGroup = {
 export const LargeActionGroup = {
   ...MediumActionGroup,
   ...MediumActionGroup,
+  ACTION_SHARE_LINK_ALL_DELETE,
   ACTION_ADMIN_APP_SETTINGS_UPDATE,
   ACTION_ADMIN_APP_SETTINGS_UPDATE,
   ACTION_ADMIN_SITE_URL_UPDATE,
   ACTION_ADMIN_SITE_URL_UPDATE,
   ACTION_ADMIN_MAIL_SMTP_UPDATE,
   ACTION_ADMIN_MAIL_SMTP_UPDATE,
@@ -275,7 +358,6 @@ export const LargeActionGroup = {
   ACTION_ADMIN_SECURITY_SETTINGS_UPDATE,
   ACTION_ADMIN_SECURITY_SETTINGS_UPDATE,
   ACTION_ADMIN_PERMIT_SHARE_LINK,
   ACTION_ADMIN_PERMIT_SHARE_LINK,
   ACTION_ADMIN_REJECT_SHARE_LINK,
   ACTION_ADMIN_REJECT_SHARE_LINK,
-  ACTION_ADMIN_DELETE_ALL_SHARE_LINK,
   ACTION_ADMIN_AUTH_ID_PASS_ENABLED,
   ACTION_ADMIN_AUTH_ID_PASS_ENABLED,
   ACTION_ADMIN_AUTH_ID_PASS_DISABLED,
   ACTION_ADMIN_AUTH_ID_PASS_DISABLED,
   ACTION_ADMIN_AUTH_ID_PASS_UPDATE,
   ACTION_ADMIN_AUTH_ID_PASS_UPDATE,
@@ -341,11 +423,21 @@ export const AllLargeGroupActions = Object.values(LargeActionGroup);
 // Action categories(for SelectActionDropdown.tsx)
 // Action categories(for SelectActionDropdown.tsx)
 const pageRegExp = new RegExp(`^${SupportedActionCategory.PAGE.toUpperCase()}_`);
 const pageRegExp = new RegExp(`^${SupportedActionCategory.PAGE.toUpperCase()}_`);
 const commentRegExp = new RegExp(`^${SupportedActionCategory.COMMENT.toUpperCase()}_`);
 const commentRegExp = new RegExp(`^${SupportedActionCategory.COMMENT.toUpperCase()}_`);
+const tagRegExp = new RegExp(`^${SupportedActionCategory.TAG.toUpperCase()}_`);
+const attachmentRegExp = RegExp(`^${SupportedActionCategory.ATTACHMENT.toUpperCase()}_`);
+const shareLinkRegExp = RegExp(`^${SupportedActionCategory.SHARE_LINK.toUpperCase()}_`);
+const inAppNotificationRegExp = RegExp(`^${SupportedActionCategory.IN_APP_NOTIFICATION.toUpperCase()}_`);
+const searchRegExp = RegExp(`^${SupportedActionCategory.SEARCH.toUpperCase()}_`);
 const userRegExp = new RegExp(`^${SupportedActionCategory.USER.toUpperCase()}_`);
 const userRegExp = new RegExp(`^${SupportedActionCategory.USER.toUpperCase()}_`);
 const adminRegExp = new RegExp(`^${SupportedActionCategory.ADMIN.toUpperCase()}_`);
 const adminRegExp = new RegExp(`^${SupportedActionCategory.ADMIN.toUpperCase()}_`);
 
 
 export const PageActions = AllSupportedActions.filter(action => action.match(pageRegExp));
 export const PageActions = AllSupportedActions.filter(action => action.match(pageRegExp));
 export const CommentActions = AllSupportedActions.filter(action => action.match(commentRegExp));
 export const CommentActions = AllSupportedActions.filter(action => action.match(commentRegExp));
+export const TagActions = AllSupportedActions.filter(action => action.match(tagRegExp));
+export const AttachmentActions = AllSupportedActions.filter(action => action.match(attachmentRegExp));
+export const ShareLinkActions = AllSupportedActions.filter(action => action.match(shareLinkRegExp));
+export const InAppNotificationActions = AllSupportedActions.filter(action => action.match(inAppNotificationRegExp));
+export const SearchActions = AllSupportedActions.filter(action => action.match(searchRegExp));
 export const UserActions = AllSupportedActions.filter(action => action.match(userRegExp));
 export const UserActions = AllSupportedActions.filter(action => action.match(userRegExp));
 export const AdminActions = AllSupportedActions.filter(action => action.match(adminRegExp));
 export const AdminActions = AllSupportedActions.filter(action => action.match(adminRegExp));
 
 

+ 13 - 1
packages/app/src/server/routes/apiv3/in-app-notification.ts

@@ -1,6 +1,10 @@
+import { SupportedAction } from '~/interfaces/activity';
+import { generateAddActivityMiddleware } from '~/server/middlewares/add-activity';
+
 import { IInAppNotification } from '../../../interfaces/in-app-notification';
 import { IInAppNotification } from '../../../interfaces/in-app-notification';
 
 
 const express = require('express');
 const express = require('express');
+
 const { serializeUserSecurely } = require('../../models/serializers/user-serializer');
 const { serializeUserSecurely } = require('../../models/serializers/user-serializer');
 
 
 const router = express.Router();
 const router = express.Router();
@@ -10,9 +14,14 @@ module.exports = (crowi) => {
   const accessTokenParser = require('../../middlewares/access-token-parser')(crowi);
   const accessTokenParser = require('../../middlewares/access-token-parser')(crowi);
   const loginRequiredStrictly = require('../../middlewares/login-required')(crowi);
   const loginRequiredStrictly = require('../../middlewares/login-required')(crowi);
   const csrf = require('../../middlewares/csrf')(crowi);
   const csrf = require('../../middlewares/csrf')(crowi);
+  const addActivity = generateAddActivityMiddleware(crowi);
+
   const inAppNotificationService = crowi.inAppNotificationService;
   const inAppNotificationService = crowi.inAppNotificationService;
+
   const User = crowi.model('User');
   const User = crowi.model('User');
 
 
+  const activityEvent = crowi.event('activity');
+
   router.get('/list', accessTokenParser, loginRequiredStrictly, async(req, res) => {
   router.get('/list', accessTokenParser, loginRequiredStrictly, async(req, res) => {
     const user = req.user;
     const user = req.user;
 
 
@@ -101,11 +110,14 @@ module.exports = (crowi) => {
     }
     }
   });
   });
 
 
-  router.put('/all-statuses-open', accessTokenParser, loginRequiredStrictly, csrf, async(req, res) => {
+  router.put('/all-statuses-open', accessTokenParser, loginRequiredStrictly, csrf, addActivity, async(req, res) => {
     const user = req.user;
     const user = req.user;
 
 
     try {
     try {
       await inAppNotificationService.updateAllNotificationsAsOpened(user);
       await inAppNotificationService.updateAllNotificationsAsOpened(user);
+
+      activityEvent.emit('update', res.locals.activity._id, { action: SupportedAction.ACTION_IN_APP_NOTIFICATION_ALL_STATUSES_OPEN });
+
       return res.apiv3();
       return res.apiv3();
     }
     }
     catch (err) {
     catch (err) {

+ 25 - 2
packages/app/src/server/routes/apiv3/page.js

@@ -1,7 +1,7 @@
 import { pagePathUtils } from '@growi/core';
 import { pagePathUtils } from '@growi/core';
 
 
 import { SupportedAction, SupportedTargetModel } from '~/interfaces/activity';
 import { SupportedAction, SupportedTargetModel } from '~/interfaces/activity';
-import { AllSubscriptionStatusType } from '~/interfaces/subscription';
+import { AllSubscriptionStatusType, SubscriptionStatusType } from '~/interfaces/subscription';
 import { generateAddActivityMiddleware } from '~/server/middlewares/add-activity';
 import { generateAddActivityMiddleware } from '~/server/middlewares/add-activity';
 import Subscription from '~/server/models/subscription';
 import Subscription from '~/server/models/subscription';
 import UserGroup from '~/server/models/user-group';
 import UserGroup from '~/server/models/user-group';
@@ -609,6 +609,17 @@ module.exports = (crowi) => {
       'Content-Disposition': `attachment;filename*=UTF-8''${fileName}.${format}`,
       'Content-Disposition': `attachment;filename*=UTF-8''${fileName}.${format}`,
     });
     });
 
 
+    const parameters = {
+      ip:  req.ip,
+      endpoint: req.originalUrl,
+      action: SupportedAction.ACTION_PAGE_EXPORT,
+      user: req.user?._id,
+      snapshot: {
+        username: req.user?.username,
+      },
+    };
+    await crowi.activityService.createActivity(parameters);
+
     return stream.pipe(res);
     return stream.pipe(res);
   });
   });
 
 
@@ -780,12 +791,24 @@ module.exports = (crowi) => {
    *          500:
    *          500:
    *            description: Internal server error.
    *            description: Internal server error.
    */
    */
-  router.put('/subscribe', accessTokenParser, loginRequiredStrictly, csrf, validator.subscribe, apiV3FormValidator, async(req, res) => {
+  router.put('/subscribe', accessTokenParser, loginRequiredStrictly, csrf, addActivity, validator.subscribe, apiV3FormValidator, async(req, res) => {
     const { pageId, status } = req.body;
     const { pageId, status } = req.body;
     const userId = req.user._id;
     const userId = req.user._id;
 
 
     try {
     try {
       const subscription = await Subscription.subscribeByPageId(userId, pageId, status);
       const subscription = await Subscription.subscribeByPageId(userId, pageId, status);
+
+      const parameters = {};
+      if (SubscriptionStatusType.SUBSCRIBE === status) {
+        Object.assign(parameters, { action: SupportedAction.ACTION_PAGE_SUBSCRIBE });
+      }
+      else if (SubscriptionStatusType.UNSUBSCRIBE === status) {
+        Object.assign(parameters, { action: SupportedAction.ACTION_PAGE_UNSUBSCRIBE });
+      }
+      if ('action' in parameters) {
+        activityEvent.emit('update', res.locals.activity._id, parameters);
+      }
+
       return res.apiv3({ subscription });
       return res.apiv3({ subscription });
     }
     }
     catch (err) {
     catch (err) {

+ 11 - 1
packages/app/src/server/routes/apiv3/pages.js

@@ -597,7 +597,7 @@ module.exports = (crowi) => {
    *          200:
    *          200:
    *            description: Succeeded to remove all trash pages
    *            description: Succeeded to remove all trash pages
    */
    */
-  router.delete('/empty-trash', accessTokenParser, loginRequired, csrf, apiV3FormValidator, async(req, res) => {
+  router.delete('/empty-trash', accessTokenParser, loginRequired, csrf, addActivity, apiV3FormValidator, async(req, res) => {
     const options = {};
     const options = {};
 
 
     const pagesInTrash = await crowi.pageService.findChildrenByParentPathOrIdAndViewer('/trash', req.user);
     const pagesInTrash = await crowi.pageService.findChildrenByParentPathOrIdAndViewer('/trash', req.user);
@@ -609,14 +609,20 @@ module.exports = (crowi) => {
       return res.apiv3Err(new ErrorV3(msg), 500);
       return res.apiv3Err(new ErrorV3(msg), 500);
     }
     }
 
 
+    const parameters = { action: SupportedAction.ACTION_PAGE_EMPTY_TRASH };
+
     // when some pages are not deletable
     // when some pages are not deletable
     if (deletablePages.length < pagesInTrash.length) {
     if (deletablePages.length < pagesInTrash.length) {
       try {
       try {
         const options = { isCompletely: true, isRecursively: true };
         const options = { isCompletely: true, isRecursively: true };
         await crowi.pageService.deleteMultiplePages(deletablePages, req.user, options);
         await crowi.pageService.deleteMultiplePages(deletablePages, req.user, options);
+
+        activityEvent.emit('update', res.locals.activity._id, parameters);
+
         return res.apiv3({ deletablePages });
         return res.apiv3({ deletablePages });
       }
       }
       catch (err) {
       catch (err) {
+        logger.error(err);
         return res.apiv3Err(new ErrorV3('Failed to update page.', 'unknown'), 500);
         return res.apiv3Err(new ErrorV3('Failed to update page.', 'unknown'), 500);
       }
       }
     }
     }
@@ -624,9 +630,13 @@ module.exports = (crowi) => {
     else {
     else {
       try {
       try {
         const pages = await crowi.pageService.emptyTrashPage(req.user, options);
         const pages = await crowi.pageService.emptyTrashPage(req.user, options);
+
+        activityEvent.emit('update', res.locals.activity._id, parameters);
+
         return res.apiv3({ pages });
         return res.apiv3({ pages });
       }
       }
       catch (err) {
       catch (err) {
+        logger.error(err);
         return res.apiv3Err(new ErrorV3('Failed to update page.', 'unknown'), 500);
         return res.apiv3Err(new ErrorV3('Failed to update page.', 'unknown'), 500);
       }
       }
     }
     }

+ 18 - 6
packages/app/src/server/routes/apiv3/share-links.js

@@ -31,9 +31,11 @@ module.exports = (crowi) => {
   const loginRequired = require('../../middlewares/login-required')(crowi);
   const loginRequired = require('../../middlewares/login-required')(crowi);
   const adminRequired = require('../../middlewares/admin-required')(crowi);
   const adminRequired = require('../../middlewares/admin-required')(crowi);
   const csrf = require('../../middlewares/csrf')(crowi);
   const csrf = require('../../middlewares/csrf')(crowi);
+  const addActivity = generateAddActivityMiddleware(crowi);
+
   const ShareLink = crowi.model('ShareLink');
   const ShareLink = crowi.model('ShareLink');
   const Page = crowi.model('Page');
   const Page = crowi.model('Page');
-  const addActivity = generateAddActivityMiddleware(crowi);
+
   const activityEvent = crowi.event('activity');
   const activityEvent = crowi.event('activity');
 
 
   /**
   /**
@@ -134,7 +136,7 @@ module.exports = (crowi) => {
    *            description: Succeeded to create one share link
    *            description: Succeeded to create one share link
    */
    */
 
 
-  router.post('/', loginRequired, linkSharingRequired, csrf, validator.shareLinkStatus, apiV3FormValidator, async(req, res) => {
+  router.post('/', loginRequired, linkSharingRequired, csrf, addActivity, validator.shareLinkStatus, apiV3FormValidator, async(req, res) => {
     const { relatedPage, expiredAt, description } = req.body;
     const { relatedPage, expiredAt, description } = req.body;
 
 
     const page = await Page.findByIdAndViewer(relatedPage, req.user);
     const page = await Page.findByIdAndViewer(relatedPage, req.user);
@@ -149,6 +151,9 @@ module.exports = (crowi) => {
 
 
     try {
     try {
       const postedShareLink = await ShareLink.create({ relatedPage, expiredAt, description });
       const postedShareLink = await ShareLink.create({ relatedPage, expiredAt, description });
+
+      activityEvent.emit('update', res.locals.activity._id, { action: SupportedAction.ACTION_SHARE_LINK_CREATE });
+
       return res.apiv3(postedShareLink, 201);
       return res.apiv3(postedShareLink, 201);
     }
     }
     catch (err) {
     catch (err) {
@@ -183,7 +188,7 @@ module.exports = (crowi) => {
   *          200:
   *          200:
   *            description: Succeeded to delete o all share links related one page
   *            description: Succeeded to delete o all share links related one page
   */
   */
-  router.delete('/', loginRequired, csrf, validator.deleteShareLinks, apiV3FormValidator, async(req, res) => {
+  router.delete('/', loginRequired, csrf, addActivity, validator.deleteShareLinks, apiV3FormValidator, async(req, res) => {
     const { relatedPage } = req.query;
     const { relatedPage } = req.query;
     const page = await Page.findByIdAndViewer(relatedPage, req.user);
     const page = await Page.findByIdAndViewer(relatedPage, req.user);
 
 
@@ -195,6 +200,9 @@ module.exports = (crowi) => {
 
 
     try {
     try {
       const deletedShareLink = await ShareLink.remove({ relatedPage });
       const deletedShareLink = await ShareLink.remove({ relatedPage });
+
+      activityEvent.emit('update', res.locals.activity._id, { action: SupportedAction.ACTION_SHARE_LINK_DELETE_BY_PAGE });
+
       return res.apiv3(deletedShareLink);
       return res.apiv3(deletedShareLink);
     }
     }
     catch (err) {
     catch (err) {
@@ -220,8 +228,9 @@ module.exports = (crowi) => {
     try {
     try {
       const deletedShareLink = await ShareLink.deleteMany({});
       const deletedShareLink = await ShareLink.deleteMany({});
       const { deletedCount } = deletedShareLink;
       const { deletedCount } = deletedShareLink;
-      const parameters = { action: SupportedAction.ACTION_ADMIN_DELETE_ALL_SHARE_LINK };
-      activityEvent.emit('update', res.locals.activity._id, parameters);
+
+      activityEvent.emit('update', res.locals.activity._id, { action: SupportedAction.ACTION_SHARE_LINK_ALL_DELETE });
+
       return res.apiv3({ deletedCount });
       return res.apiv3({ deletedCount });
     }
     }
     catch (err) {
     catch (err) {
@@ -253,7 +262,7 @@ module.exports = (crowi) => {
   *          200:
   *          200:
   *            description: Succeeded to delete one share link
   *            description: Succeeded to delete one share link
   */
   */
-  router.delete('/:id', loginRequired, csrf, validator.deleteShareLink, apiV3FormValidator, async(req, res) => {
+  router.delete('/:id', loginRequired, csrf, addActivity, validator.deleteShareLink, apiV3FormValidator, async(req, res) => {
     const { id } = req.params;
     const { id } = req.params;
     const { user } = req;
     const { user } = req;
 
 
@@ -273,6 +282,9 @@ module.exports = (crowi) => {
 
 
       // remove
       // remove
       await shareLinkToDelete.remove();
       await shareLinkToDelete.remove();
+
+      activityEvent.emit('update', res.locals.activity._id, { action: SupportedAction.ACTION_SHARE_LINK_DELETE });
+
       return res.apiv3({ deletedShareLink: shareLinkToDelete });
       return res.apiv3({ deletedShareLink: shareLinkToDelete });
     }
     }
     catch (err) {
     catch (err) {

+ 19 - 0
packages/app/src/server/routes/attachment.js

@@ -1,5 +1,7 @@
+import { SupportedAction } from '~/interfaces/activity';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 
+
 /* eslint-disable no-use-before-define */
 /* eslint-disable no-use-before-define */
 
 
 
 
@@ -135,6 +137,8 @@ module.exports = function(crowi, app) {
   const GlobalNotificationSetting = crowi.model('GlobalNotificationSetting');
   const GlobalNotificationSetting = crowi.model('GlobalNotificationSetting');
   const { attachmentService, globalNotificationService } = crowi;
   const { attachmentService, globalNotificationService } = crowi;
 
 
+  const activityEvent = crowi.event('activity');
+
   /**
   /**
    * Check the user is accessible to the related page
    * Check the user is accessible to the related page
    *
    *
@@ -214,6 +218,17 @@ module.exports = function(crowi, app) {
       return res.json(ApiResponse.error(e.message));
       return res.json(ApiResponse.error(e.message));
     }
     }
 
 
+    const parameters = {
+      ip:  req.ip,
+      endpoint: req.originalUrl,
+      action: SupportedAction.ACTION_ATTACHMENT_DOWNLOAD,
+      user: req.user?._id,
+      snapshot: {
+        username: req.user?.username,
+      },
+    };
+    await crowi.activityService.createActivity(parameters);
+
     return fileStream.pipe(res);
     return fileStream.pipe(res);
   }
   }
 
 
@@ -472,6 +487,8 @@ module.exports = function(crowi, app) {
       pageCreated,
       pageCreated,
     };
     };
 
 
+    activityEvent.emit('update', res.locals.activity._id, { action: SupportedAction.ACTION_ATTACHMENT_ADD });
+
     res.json(ApiResponse.success(result));
     res.json(ApiResponse.success(result));
 
 
     if (pageCreated) {
     if (pageCreated) {
@@ -641,6 +658,8 @@ module.exports = function(crowi, app) {
       return res.status(500).json(ApiResponse.error('Error while deleting file'));
       return res.status(500).json(ApiResponse.error('Error while deleting file'));
     }
     }
 
 
+    activityEvent.emit('update', res.locals.activity._id, { action: SupportedAction.ACTION_ATTACHMENT_REMOVE });
+
     return res.json(ApiResponse.success({}));
     return res.json(ApiResponse.success({}));
   };
   };
 
 

+ 4 - 4
packages/app/src/server/routes/index.js

@@ -106,7 +106,7 @@ module.exports = function(crowi, app) {
   app.get('/passport/github/callback'             , loginPassport.loginPassportGitHubCallback   , loginPassport.loginFailure);
   app.get('/passport/github/callback'             , loginPassport.loginPassportGitHubCallback   , loginPassport.loginFailure);
   app.get('/passport/twitter/callback'            , loginPassport.loginPassportTwitterCallback  , loginPassport.loginFailure);
   app.get('/passport/twitter/callback'            , loginPassport.loginPassportTwitterCallback  , loginPassport.loginFailure);
   app.get('/passport/oidc/callback'               , loginPassport.loginPassportOidcCallback     , loginPassport.loginFailure);
   app.get('/passport/oidc/callback'               , loginPassport.loginPassportOidcCallback     , loginPassport.loginFailure);
-  app.post('/passport/saml/callback'              , loginPassport.loginPassportSamlCallback     , loginPassport.loginFailure);
+  app.post('/passport/saml/callback'              , addActivity, loginPassport.loginPassportSamlCallback, loginPassport.loginFailure);
 
 
   app.post('/_api/login/testLdap'    , apiLimiter , loginRequiredStrictly , loginFormValidator.loginRules() , loginFormValidator.loginValidation , loginPassport.testLdapCredentials);
   app.post('/_api/login/testLdap'    , apiLimiter , loginRequiredStrictly , loginFormValidator.loginRules() , loginFormValidator.loginValidation , loginPassport.testLdapCredentials);
 
 
@@ -184,15 +184,15 @@ module.exports = function(crowi, app) {
   apiV1Router.post('/pages.duplicate'    , accessTokenParser, loginRequiredStrictly, csrf, page.api.duplicate);
   apiV1Router.post('/pages.duplicate'    , accessTokenParser, loginRequiredStrictly, csrf, page.api.duplicate);
   apiV1Router.get('/tags.list'           , accessTokenParser, loginRequired, tag.api.list);
   apiV1Router.get('/tags.list'           , accessTokenParser, loginRequired, tag.api.list);
   apiV1Router.get('/tags.search'         , accessTokenParser, loginRequired, tag.api.search);
   apiV1Router.get('/tags.search'         , accessTokenParser, loginRequired, tag.api.search);
-  apiV1Router.post('/tags.update'        , accessTokenParser, loginRequiredStrictly, tag.api.update);
+  apiV1Router.post('/tags.update'        , accessTokenParser, loginRequiredStrictly, addActivity, tag.api.update);
   apiV1Router.get('/comments.get'        , accessTokenParser , loginRequired , comment.api.get);
   apiV1Router.get('/comments.get'        , accessTokenParser , loginRequired , comment.api.get);
   apiV1Router.post('/comments.add'       , comment.api.validators.add(), accessTokenParser , loginRequiredStrictly , csrf, addActivity, comment.api.add);
   apiV1Router.post('/comments.add'       , comment.api.validators.add(), accessTokenParser , loginRequiredStrictly , csrf, addActivity, comment.api.add);
   apiV1Router.post('/comments.update'    , comment.api.validators.add(), accessTokenParser , loginRequiredStrictly , csrf, addActivity, comment.api.update);
   apiV1Router.post('/comments.update'    , comment.api.validators.add(), accessTokenParser , loginRequiredStrictly , csrf, addActivity, comment.api.update);
   apiV1Router.post('/comments.remove'    , accessTokenParser , loginRequiredStrictly , csrf, addActivity, comment.api.remove);
   apiV1Router.post('/comments.remove'    , accessTokenParser , loginRequiredStrictly , csrf, addActivity, comment.api.remove);
 
 
-  apiV1Router.post('/attachments.add'                  , uploads.single('file'), autoReap, accessTokenParser, loginRequiredStrictly ,csrf, attachment.api.add);
+  apiV1Router.post('/attachments.add'                  , uploads.single('file'), autoReap, accessTokenParser, loginRequiredStrictly ,csrf, addActivity ,attachment.api.add);
   apiV1Router.post('/attachments.uploadProfileImage'   , uploads.single('file'), autoReap, accessTokenParser, loginRequiredStrictly ,csrf, attachment.api.uploadProfileImage);
   apiV1Router.post('/attachments.uploadProfileImage'   , uploads.single('file'), autoReap, accessTokenParser, loginRequiredStrictly ,csrf, attachment.api.uploadProfileImage);
-  apiV1Router.post('/attachments.remove'               , accessTokenParser , loginRequiredStrictly , csrf, attachment.api.remove);
+  apiV1Router.post('/attachments.remove'               , accessTokenParser , loginRequiredStrictly , csrf, addActivity ,attachment.api.remove);
   apiV1Router.post('/attachments.removeProfileImage'   , accessTokenParser , loginRequiredStrictly , csrf, attachment.api.removeProfileImage);
   apiV1Router.post('/attachments.removeProfileImage'   , accessTokenParser , loginRequiredStrictly , csrf, attachment.api.removeProfileImage);
   apiV1Router.get('/attachments.limit'   , accessTokenParser , loginRequiredStrictly, attachment.api.limit);
   apiV1Router.get('/attachments.limit'   , accessTokenParser , loginRequiredStrictly, attachment.api.limit);
 
 

+ 67 - 7
packages/app/src/server/routes/login-passport.js

@@ -33,9 +33,6 @@ module.exports = function(crowi, app) {
     // remove session.redirectTo
     // remove session.redirectTo
     delete req.session.redirectTo;
     delete req.session.redirectTo;
 
 
-    const parameters = { action: SupportedAction.ACTION_USER_LOGIN_SUCCESS };
-    activityEvent.emit('update', res.locals.activity._id, parameters);
-
     return res.safeRedirect(redirectTo);
     return res.safeRedirect(redirectTo);
   };
   };
 
 
@@ -143,6 +140,10 @@ module.exports = function(crowi, app) {
     // login
     // login
     await req.logIn(user, (err) => {
     await req.logIn(user, (err) => {
       if (err) { debug(err.message); return next() }
       if (err) { debug(err.message); return next() }
+
+      const parameters = { action: SupportedAction.ACTION_USER_LOGIN_WITH_LDAP };
+      activityEvent.emit('update', res.locals.activity._id, parameters);
+
       return loginSuccessHandler(req, res, user);
       return loginSuccessHandler(req, res, user);
     });
     });
   };
   };
@@ -235,6 +236,9 @@ module.exports = function(crowi, app) {
       req.logIn(user, (err) => {
       req.logIn(user, (err) => {
         if (err) { debug(err.message); return next() }
         if (err) { debug(err.message); return next() }
 
 
+        const parameters = { action: SupportedAction.ACTION_USER_LOGIN_WITH_LOCAL };
+        activityEvent.emit('update', res.locals.activity._id, parameters);
+
         return loginSuccessHandler(req, res, user);
         return loginSuccessHandler(req, res, user);
       });
       });
     })(req, res, next);
     })(req, res, next);
@@ -303,8 +307,20 @@ module.exports = function(crowi, app) {
     const user = await externalAccount.getPopulatedUser();
     const user = await externalAccount.getPopulatedUser();
 
 
     // login
     // login
-    req.logIn(user, (err) => {
+    req.logIn(user, async(err) => {
       if (err) { debug(err.message); return next() }
       if (err) { debug(err.message); return next() }
+
+      const parameters = {
+        ip:  req.ip,
+        endpoint: req.originalUrl,
+        action: SupportedAction.ACTION_USER_LOGIN_WITH_GOOGLE,
+        user: req.user?._id,
+        snapshot: {
+          username: req.user?.username,
+        },
+      };
+      await crowi.activityService.createActivity(parameters);
+
       return loginSuccessHandler(req, res, user);
       return loginSuccessHandler(req, res, user);
     });
     });
   };
   };
@@ -345,8 +361,20 @@ module.exports = function(crowi, app) {
     const user = await externalAccount.getPopulatedUser();
     const user = await externalAccount.getPopulatedUser();
 
 
     // login
     // login
-    req.logIn(user, (err) => {
+    req.logIn(user, async(err) => {
       if (err) { debug(err.message); return next() }
       if (err) { debug(err.message); return next() }
+
+      const parameters = {
+        ip:  req.ip,
+        endpoint: req.originalUrl,
+        action: SupportedAction.ACTION_USER_LOGIN_WITH_GITHUB,
+        user: req.user?._id,
+        snapshot: {
+          username: req.user?.username,
+        },
+      };
+      await crowi.activityService.createActivity(parameters);
+
       return loginSuccessHandler(req, res, user);
       return loginSuccessHandler(req, res, user);
     });
     });
   };
   };
@@ -387,8 +415,20 @@ module.exports = function(crowi, app) {
     const user = await externalAccount.getPopulatedUser();
     const user = await externalAccount.getPopulatedUser();
 
 
     // login
     // login
-    req.logIn(user, (err) => {
+    req.logIn(user, async(err) => {
       if (err) { debug(err.message); return next() }
       if (err) { debug(err.message); return next() }
+
+      const parameters = {
+        ip:  req.ip,
+        endpoint: req.originalUrl,
+        action: SupportedAction.ACTION_USER_LOGIN_WITH_TWITTER,
+        user: req.user?._id,
+        snapshot: {
+          username: req.user?.username,
+        },
+      };
+      await crowi.activityService.createActivity(parameters);
+
       return loginSuccessHandler(req, res, user);
       return loginSuccessHandler(req, res, user);
     });
     });
   };
   };
@@ -435,8 +475,20 @@ module.exports = function(crowi, app) {
 
 
     // login
     // login
     const user = await externalAccount.getPopulatedUser();
     const user = await externalAccount.getPopulatedUser();
-    req.logIn(user, (err) => {
+    req.logIn(user, async(err) => {
       if (err) { debug(err.message); return next() }
       if (err) { debug(err.message); return next() }
+
+      const parameters = {
+        ip:  req.ip,
+        endpoint: req.originalUrl,
+        action: SupportedAction.ACTION_USER_LOGIN_WITH_OIDC,
+        user: req.user?._id,
+        snapshot: {
+          username: req.user?.username,
+        },
+      };
+      await crowi.activityService.createActivity(parameters);
+
       return loginSuccessHandler(req, res, user);
       return loginSuccessHandler(req, res, user);
     });
     });
   };
   };
@@ -499,6 +551,10 @@ module.exports = function(crowi, app) {
         logger.error(err);
         logger.error(err);
         return loginFailureHandler(req, res);
         return loginFailureHandler(req, res);
       }
       }
+
+      const parameters = { action: SupportedAction.ACTION_USER_LOGIN_WITH_SAML };
+      activityEvent.emit('update', res.locals.activity._id, parameters);
+
       return loginSuccessHandler(req, res, user);
       return loginSuccessHandler(req, res, user);
     });
     });
   };
   };
@@ -541,6 +597,10 @@ module.exports = function(crowi, app) {
     const user = await externalAccount.getPopulatedUser();
     const user = await externalAccount.getPopulatedUser();
     await req.logIn(user, (err) => {
     await req.logIn(user, (err) => {
       if (err) { debug(err.message); return next() }
       if (err) { debug(err.message); return next() }
+
+      const parameters = { action: SupportedAction.ACTION_USER_LOGIN_WITH_BASIC };
+      activityEvent.emit('update', res.locals.activity._id, parameters);
+
       return loginSuccessHandler(req, res, user);
       return loginSuccessHandler(req, res, user);
     });
     });
   };
   };

+ 44 - 5
packages/app/src/server/routes/page.js

@@ -309,16 +309,20 @@ module.exports = function(crowi, app) {
     const pathOrId = req.params.id || path;
     const pathOrId = req.params.id || path;
 
 
     let view;
     let view;
+    let action;
     const renderVars = { path };
     const renderVars = { path };
 
 
     if (!isCreatablePage(path)) {
     if (!isCreatablePage(path)) {
       view = 'layout-growi/not_creatable';
       view = 'layout-growi/not_creatable';
+      action = SupportedAction.ACTION_PAGE_NOT_CREATABLE;
     }
     }
     else if (req.isForbidden) {
     else if (req.isForbidden) {
       view = 'layout-growi/forbidden';
       view = 'layout-growi/forbidden';
+      action = SupportedAction.ACTION_PAGE_FORBIDDEN;
     }
     }
     else {
     else {
       view = 'layout-growi/not_found';
       view = 'layout-growi/not_found';
+      action = SupportedAction.ACTION_PAGE_NOT_FOUND;
 
 
       // retrieve templates
       // retrieve templates
       if (req.user != null) {
       if (req.user != null) {
@@ -345,6 +349,18 @@ module.exports = function(crowi, app) {
     await addRenderVarsForPageTree(renderVars, pathOrId, req.user);
     await addRenderVarsForPageTree(renderVars, pathOrId, req.user);
     await addRenderVarsWhenNotFound(renderVars, pathOrId);
     await addRenderVarsWhenNotFound(renderVars, pathOrId);
     await addRenderVarsWhenEmptyPage(renderVars, req.isEmpty, req.pageId);
     await addRenderVarsWhenEmptyPage(renderVars, req.isEmpty, req.pageId);
+
+    const parameters = {
+      ip:  req.ip,
+      endpoint: req.originalUrl,
+      action,
+      user: req.user?._id,
+      snapshot: {
+        username: req.user?.username,
+      },
+    };
+    crowi.activityService.createActivity(parameters);
+
     return res.render(view, renderVars);
     return res.render(view, renderVars);
   }
   }
 
 
@@ -421,7 +437,7 @@ module.exports = function(crowi, app) {
         username: req.user?.username,
         username: req.user?.username,
       },
       },
     };
     };
-    crowi.activityService.createActivity(SupportedAction.ACTION_PAGE_VIEW, parameters);
+    crowi.activityService.createActivity(parameters);
 
 
     return res.render(view, renderVars);
     return res.render(view, renderVars);
   }
   }
@@ -484,13 +500,13 @@ module.exports = function(crowi, app) {
     const parameters = {
     const parameters = {
       ip:  req.ip,
       ip:  req.ip,
       endpoint: req.originalUrl,
       endpoint: req.originalUrl,
-      action: SupportedAction.ACTION_PAGE_VIEW,
+      action: isUsersHomePage(path) ? SupportedAction.ACTION_PAGE_USER_HOME_VIEW : SupportedAction.ACTION_PAGE_VIEW,
       user: req.user?._id,
       user: req.user?._id,
       snapshot: {
       snapshot: {
         username: req.user?.username,
         username: req.user?.username,
       },
       },
     };
     };
-    crowi.activityService.createActivity(SupportedAction.ACTION_PAGE_VIEW, parameters);
+    crowi.activityService.createActivity(parameters);
 
 
     return res.render(view, renderVars);
     return res.render(view, renderVars);
   }
   }
@@ -525,13 +541,30 @@ module.exports = function(crowi, app) {
     const revisionId = req.query.revision;
     const revisionId = req.query.revision;
     const renderVars = {};
     const renderVars = {};
 
 
+    const parameters = {
+      ip:  req.ip,
+      endpoint: req.originalUrl,
+      user: req.user?._id,
+      snapshot: {
+        username: req.user?.username,
+      },
+    };
+
     const shareLink = await ShareLink.findOne({ _id: linkId }).populate('relatedPage');
     const shareLink = await ShareLink.findOne({ _id: linkId }).populate('relatedPage');
 
 
     if (shareLink == null || shareLink.relatedPage == null || shareLink.relatedPage.isEmpty) {
     if (shareLink == null || shareLink.relatedPage == null || shareLink.relatedPage.isEmpty) {
+
+      Object.assign(parameters, { action: SupportedAction.ACTION_SHARE_LINK_NOT_FOUND });
+      crowi.activityService.createActivity(parameters);
+
       // page or sharelink are not found (or page is empty: abnormaly)
       // page or sharelink are not found (or page is empty: abnormaly)
       return res.render('layout-growi/not_found_shared_page');
       return res.render('layout-growi/not_found_shared_page');
     }
     }
     if (crowi.configManager.getConfig('crowi', 'security:disableLinkSharing')) {
     if (crowi.configManager.getConfig('crowi', 'security:disableLinkSharing')) {
+
+      Object.assign(parameters, { action: SupportedAction.ACTION_SHARE_LINK_NOT_FOUND });
+      crowi.activityService.createActivity(parameters);
+
       return res.render('layout-growi/forbidden');
       return res.render('layout-growi/forbidden');
     }
     }
 
 
@@ -539,6 +572,9 @@ module.exports = function(crowi, app) {
 
 
     // check if share link is expired
     // check if share link is expired
     if (shareLink.isExpired()) {
     if (shareLink.isExpired()) {
+      Object.assign(parameters, { action: SupportedAction.ACTION_SHARE_LINK_EXPIRED_PAGE_VIEW });
+      crowi.activityService.createActivity(parameters);
+
       // page is not found
       // page is not found
       return res.render('layout-growi/expired_shared_page', renderVars);
       return res.render('layout-growi/expired_shared_page', renderVars);
     }
     }
@@ -561,6 +597,9 @@ module.exports = function(crowi, app) {
     addRenderVarsForPage(renderVars, page);
     addRenderVarsForPage(renderVars, page);
     addRenderVarsForScope(renderVars, page);
     addRenderVarsForScope(renderVars, page);
 
 
+    Object.assign(parameters, { action: SupportedAction.ACTION_SHARE_LINK_PAGE_VIEW });
+    crowi.activityService.createActivity(parameters);
+
     return res.render('layout-growi/shared_page', renderVars);
     return res.render('layout-growi/shared_page', renderVars);
   };
   };
 
 
@@ -681,7 +720,7 @@ module.exports = function(crowi, app) {
         username: req.user?.username,
         username: req.user?.username,
       },
       },
     };
     };
-    crowi.activityService.createActivity(SupportedAction.ACTION_PAGE_VIEW, parameters);
+    crowi.activityService.createActivity(parameters);
     return redirector(req, res, next, path);
     return redirector(req, res, next, path);
   };
   };
 
 
@@ -698,7 +737,7 @@ module.exports = function(crowi, app) {
         username: req.user?.username,
         username: req.user?.username,
       },
       },
     };
     };
-    crowi.activityService.createActivity(SupportedAction.ACTION_PAGE_VIEW, parameters);
+    crowi.activityService.createActivity(parameters);
 
 
     return redirector(req, res, next, path);
     return redirector(req, res, next, path);
   };
   };

+ 27 - 1
packages/app/src/server/routes/search.ts

@@ -1,6 +1,9 @@
+import { SupportedAction } from '~/interfaces/activity';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
+
 import { isSearchError } from '../models/vo/search-error';
 import { isSearchError } from '../models/vo/search-error';
 
 
+
 const logger = loggerFactory('growi:routes:search');
 const logger = loggerFactory('growi:routes:search');
 
 
 /**
 /**
@@ -37,9 +40,20 @@ module.exports = function(crowi, app) {
   const actions: any = {};
   const actions: any = {};
   const api: any = {};
   const api: any = {};
 
 
-  actions.searchPage = function(req, res) {
+  actions.searchPage = async function(req, res) {
     const keyword = req.query.q || null;
     const keyword = req.query.q || null;
 
 
+    const parameters = {
+      ip:  req.ip,
+      endpoint: req.originalUrl,
+      action: SupportedAction.ACTION_SEARCH_PAGE_VIEW,
+      user: req.user?._id,
+      snapshot: {
+        username: req.user?.username,
+      },
+    };
+    await crowi.activityService.createActivity(parameters);
+
     return res.render('search', {
     return res.render('search', {
       q: keyword,
       q: keyword,
     });
     });
@@ -168,6 +182,18 @@ module.exports = function(crowi, app) {
       logger.error(err);
       logger.error(err);
       return res.json(ApiResponse.error(err));
       return res.json(ApiResponse.error(err));
     }
     }
+
+    const parameters = {
+      ip:  req.ip,
+      endpoint: req.originalUrl,
+      action: SupportedAction.ACTION_SEARCH_PAGE,
+      user: req.user?._id,
+      snapshot: {
+        username: req.user?.username,
+      },
+    };
+    await crowi.activityService.createActivity(parameters);
+
     return res.json(ApiResponse.success(result));
     return res.json(ApiResponse.success(result));
   };
   };
 
 

+ 5 - 0
packages/app/src/server/routes/tag.js

@@ -1,3 +1,4 @@
+import { SupportedAction } from '~/interfaces/activity';
 import Tag from '~/server/models/tag';
 import Tag from '~/server/models/tag';
 
 
 /**
 /**
@@ -32,6 +33,7 @@ import Tag from '~/server/models/tag';
 module.exports = function(crowi, app) {
 module.exports = function(crowi, app) {
 
 
   const PageTagRelation = crowi.model('PageTagRelation');
   const PageTagRelation = crowi.model('PageTagRelation');
+  const activityEvent = crowi.event('activity');
   const ApiResponse = require('../util/apiResponse');
   const ApiResponse = require('../util/apiResponse');
   const actions = {};
   const actions = {};
   const api = {};
   const api = {};
@@ -166,6 +168,9 @@ module.exports = function(crowi, app) {
     catch (err) {
     catch (err) {
       return res.json(ApiResponse.error(err));
       return res.json(ApiResponse.error(err));
     }
     }
+
+    activityEvent.emit('update', res.locals.activity._id, { action: SupportedAction.ACTION_TAG_UPDATE });
+
     return res.json(ApiResponse.success(result));
     return res.json(ApiResponse.success(result));
   };
   };
 
 

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

@@ -103,8 +103,8 @@ class ActivityService {
   }
   }
 
 
   // for GET request
   // for GET request
-  createActivity = async function(action: SupportedActionType, parameters): Promise<void> {
-    const shoudCreateActivity = this.crowi.activityService.shoudUpdateActivity(action);
+  createActivity = async function(parameters): Promise<void> {
+    const shoudCreateActivity = this.crowi.activityService.shoudUpdateActivity(parameters.action);
     if (shoudCreateActivity) {
     if (shoudCreateActivity) {
       try {
       try {
         await Activity.createByParameters(parameters);
         await Activity.createByParameters(parameters);