ryoji-s 2 лет назад
Родитель
Сommit
c6430dd1fa
2 измененных файлов с 54 добавлено и 84 удалено
  1. 54 30
      apps/app/src/server/routes/apiv3/pages.js
  2. 0 54
      apps/app/src/server/service/page.ts

+ 54 - 30
apps/app/src/server/routes/apiv3/pages.js

@@ -2,12 +2,13 @@
 import { PageGrant } from '@growi/core';
 import { PageGrant } from '@growi/core';
 import { ErrorV3 } from '@growi/core/dist/models';
 import { ErrorV3 } from '@growi/core/dist/models';
 import {
 import {
-  isCreatablePage, isTrashPage, isUserPage, isUsersHomepage,
+  isCreatablePage, isTrashPage, isUserPage, isUsersHomepage, getUsernameByPath,
 } from '@growi/core/dist/utils/page-path-utils';
 } from '@growi/core/dist/utils/page-path-utils';
 import { normalizePath, addHeadingSlash, attachTitleHeader } from '@growi/core/dist/utils/path-utils';
 import { normalizePath, addHeadingSlash, attachTitleHeader } from '@growi/core/dist/utils/path-utils';
 
 
 import { SupportedTargetModel, SupportedAction } from '~/interfaces/activity';
 import { SupportedTargetModel, SupportedAction } from '~/interfaces/activity';
 import { subscribeRuleNames } from '~/interfaces/in-app-notification';
 import { subscribeRuleNames } from '~/interfaces/in-app-notification';
+import { configManager } from '~/server/service/config-manager';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 
 import { generateAddActivityMiddleware } from '../../middlewares/add-activity';
 import { generateAddActivityMiddleware } from '../../middlewares/add-activity';
@@ -643,19 +644,7 @@ module.exports = (crowi) => {
 
 
     const pagesInTrash = await crowi.pageService.findAllTrashPages(req.user);
     const pagesInTrash = await crowi.pageService.findAllTrashPages(req.user);
 
 
-    const deletablePages = [];
-    const filteredPages = pagesInTrash.filter(page => !isUsersHomepage(page.path));
-    deletablePages.push(...crowi.pageService.filterPagesByCanDeleteCompletely(filteredPages, req.user, true));
-
-    const usersHomepages = pagesInTrash.filter(page => isUsersHomepage(page.path));
-    const usersHomepagesDeletable = await Promise.all(
-      usersHomepages
-        .map(async(page) => {
-          const canDeleteCompletely = page.isEmpty || await crowi.pageService.canDeleteCompletelyPromise(page.path, page.creator, req.user, true);
-          return canDeleteCompletely ? page : null;
-        }),
-    );
-    deletablePages.push(...usersHomepagesDeletable.filter(page => page !== null));
+    const deletablePages = crowi.pageService.filterPagesByCanDeleteCompletely(pagesInTrash, req.user, true);
 
 
     if (deletablePages.length === 0) {
     if (deletablePages.length === 0) {
       const msg = 'No pages can be deleted.';
       const msg = 'No pages can be deleted.';
@@ -919,37 +908,72 @@ module.exports = (crowi) => {
       return res.apiv3Err(new ErrorV3('The grant of the retrieved page is not restricted'), 500);
       return res.apiv3Err(new ErrorV3('The grant of the retrieved page is not restricted'), 500);
     }
     }
 
 
-    const filteredPages = pagesToDelete.filter(page => !isUsersHomepage(page.path));
-    const usersHomepages = pagesToDelete.filter(page => isUsersHomepage(page.path));
     let pagesCanBeDeleted = [];
     let pagesCanBeDeleted = [];
+
+    // Since the decision to delete or not a user's homepage is an asynchronous process,
+    // filtering is done here on the user homepages and other pages for performance optimization.
+    const { filteredPages, userHomepages } = pagesToDelete.reduce((result, page) => {
+      if (isUsersHomepage(page.path)) {
+        result.userHomepages.push(page);
+      }
+      else {
+        result.filteredPages.push(page);
+      }
+      return result;
+    },
+    { filteredPages: [], userHomepages: [] });
+
     /*
     /*
      * Delete Completely
      * Delete Completely
      */
      */
     if (isCompletely) {
     if (isCompletely) {
       pagesCanBeDeleted.push(...crowi.pageService.filterPagesByCanDeleteCompletely(filteredPages, req.user, isRecursively));
       pagesCanBeDeleted.push(...crowi.pageService.filterPagesByCanDeleteCompletely(filteredPages, req.user, isRecursively));
 
 
-      const usersHomepagesDeletable = await Promise.all(
-        usersHomepages.map(async(page) => {
-          const canDeleteCompletely = page.isEmpty || (await crowi.pageService.canDeleteCompletelyPromise(page.path, page.creator, req.user, isRecursively));
-          return canDeleteCompletely ? page : null;
-        }),
-      );
-      pagesCanBeDeleted.push(...usersHomepagesDeletable.filter(page => page !== null));
+      const isUsersHomepageDeletionEnabled = configManager.getConfig('crowi', 'security:user-homepage-deletion:isEnabled');
+      if (isUsersHomepageDeletionEnabled) {
+        const usernameList = userHomepages.map(page => getUsernameByPath(page.path));
+
+        const User = mongoose.model('User');
+        const existingUsernames = await User.distinct('username', { username: { $in: usernameList } });
+        const nonExistingUsernames = usernameList.filter(username => !existingUsernames.includes(username));
+
+        for (const page of userHomepages) {
+          const pageUsername = getUsernameByPath(page.path);
+          if (nonExistingUsernames.includes(pageUsername)) {
+            const canDelete = page.isEmpty || crowi.pageService.canDeleteCompletely(page.path, page.creator, req.user, isRecursively);
+            if (canDelete) {
+              pagesCanBeDeleted.push(page);
+            }
+          }
+        }
+      }
     }
     }
     /*
     /*
      * Trash
      * Trash
      */
      */
     else {
     else {
+      // TODO: check this code
       pagesCanBeDeleted = pagesToDelete.filter(p => p.isEmpty || p.isUpdatable(pageIdToRevisionIdMap[p._id].toString()));
       pagesCanBeDeleted = pagesToDelete.filter(p => p.isEmpty || p.isUpdatable(pageIdToRevisionIdMap[p._id].toString()));
       pagesCanBeDeleted.push(...crowi.pageService.filterPagesByCanDelete(filteredPages, req.user, isRecursively));
       pagesCanBeDeleted.push(...crowi.pageService.filterPagesByCanDelete(filteredPages, req.user, isRecursively));
 
 
-      const usersHomepagesDeletable = await Promise.all(
-        usersHomepages.map(async(page) => {
-          const canDeleteCompletely = page.isEmpty || (await crowi.pageService.canDeletePromise(page.path, page.creator, req.user, isRecursively));
-          return canDeleteCompletely ? page : null;
-        }),
-      );
-      pagesCanBeDeleted.push(...usersHomepagesDeletable.filter(page => page !== null));
+      const isUsersHomepageDeletionEnabled = configManager.getConfig('crowi', 'security:user-homepage-deletion:isEnabled');
+      if (isUsersHomepageDeletionEnabled) {
+        const usernameList = userHomepages.map(page => getUsernameByPath(page.path));
+
+        const User = mongoose.model('User');
+        const existingUsernames = await User.distinct('username', { username: { $in: usernameList } });
+        const nonExistingUsernames = usernameList.filter(username => !existingUsernames.includes(username));
+
+        for (const page of userHomepages) {
+          const pageUsername = getUsernameByPath(page.path);
+          if (nonExistingUsernames.includes(pageUsername)) {
+            const canDelete = page.isEmpty || crowi.pageService.canDelete(page.path, page.creator, req.user, isRecursively);
+            if (canDelete) {
+              pagesCanBeDeleted.push(page);
+            }
+          }
+        }
+      }
     }
     }
 
 
     if (pagesCanBeDeleted.length === 0) {
     if (pagesCanBeDeleted.length === 0) {

+ 0 - 54
apps/app/src/server/service/page.ts

@@ -191,60 +191,6 @@ class PageService {
     return this.canDeleteLogic(creatorId, operator, isRecursively, singleAuthority, recursiveAuthority);
     return this.canDeleteLogic(creatorId, operator, isRecursively, singleAuthority, recursiveAuthority);
   }
   }
 
 
-  async canDeleteCompletelyPromise(path: string, creatorId: ObjectIdLike, operator: any | null, isRecursively: boolean): Promise<boolean> {
-    if (operator == null || isTopPage(path) || isUsersTopPage(path)) {
-      return false;
-    }
-
-    if (isUsersHomepage(path)) {
-      const isUsersHomepageDeletionEnabled = configManager.getConfig('crowi', 'security:user-homepage-deletion:isEnabled');
-      if (!isUsersHomepageDeletionEnabled) {
-        return false;
-      }
-
-      const User = mongoose.model('User');
-      const username = getUsernameByPath(path);
-      const userHomepageOwner = await User.findOne<Promise<IUserHasId | null>>({ username });
-      if (userHomepageOwner != null) {
-        return false;
-      }
-    }
-
-    const pageCompleteDeletionAuthority = this.crowi.configManager.getConfig('crowi', 'security:pageCompleteDeletionAuthority');
-    const pageRecursiveCompleteDeletionAuthority = this.crowi.configManager.getConfig('crowi', 'security:pageRecursiveCompleteDeletionAuthority');
-
-    const [singleAuthority, recursiveAuthority] = prepareDeleteConfigValuesForCalc(pageCompleteDeletionAuthority, pageRecursiveCompleteDeletionAuthority);
-
-    return this.canDeleteLogic(creatorId, operator, isRecursively, singleAuthority, recursiveAuthority);
-  }
-
-  async canDeletePromise(path: string, creatorId: ObjectIdLike, operator: any | null, isRecursively: boolean): Promise<boolean> {
-    if (operator == null || isTopPage(path) || isUsersTopPage(path)) {
-      return false;
-    }
-
-    if (isUsersHomepage(path)) {
-      const isUsersHomepageDeletionEnabled = configManager.getConfig('crowi', 'security:user-homepage-deletion:isEnabled');
-      if (!isUsersHomepageDeletionEnabled) {
-        return false;
-      }
-
-      const User = mongoose.model('User');
-      const username = getUsernameByPath(path);
-      const userHomepageOwner = await User.findOne<Promise<IUserHasId | null>>({ username });
-      if (userHomepageOwner != null) {
-        return false;
-      }
-    }
-
-    const pageDeletionAuthority = this.crowi.configManager.getConfig('crowi', 'security:pageDeletionAuthority');
-    const pageRecursiveDeletionAuthority = this.crowi.configManager.getConfig('crowi', 'security:pageRecursiveDeletionAuthority');
-
-    const [singleAuthority, recursiveAuthority] = prepareDeleteConfigValuesForCalc(pageDeletionAuthority, pageRecursiveDeletionAuthority);
-
-    return this.canDeleteLogic(creatorId, operator, isRecursively, singleAuthority, recursiveAuthority);
-  }
-
   private canDeleteLogic(
   private canDeleteLogic(
       creatorId: ObjectIdLike,
       creatorId: ObjectIdLike,
       operator,
       operator,