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

fix: externalize findPageAndMetaDataByViewer to a dedicated module

Shun Miyazawa 3 месяцев назад
Родитель
Сommit
6652dd9eae
1 измененных файлов с 163 добавлено и 0 удалено
  1. 163 0
      apps/app/src/server/service/page/find-page-and-meta-data-by-viewer.ts

+ 163 - 0
apps/app/src/server/service/page/find-page-and-meta-data-by-viewer.ts

@@ -0,0 +1,163 @@
+import type {
+  IDataWithRequiredMeta,
+  IPageInfo,
+  IPageInfoExt,
+  IPageInfoForEmpty,
+  IPageInfoForOperation,
+  IPageNotFoundInfo,
+  IUser,
+} from '@growi/core/dist/interfaces';
+import { isIPageInfoForEntity } from '@growi/core/dist/interfaces';
+import { pagePathUtils } from '@growi/core/dist/utils';
+import assert from 'assert';
+import type { HydratedDocument } from 'mongoose';
+import mongoose from 'mongoose';
+
+import type { BookmarkedPage } from '~/interfaces/bookmark-info';
+import type { PageDocument, PageModel } from '~/server/models/page';
+
+import Subscription from '../../models/subscription';
+
+export async function findPageAndMetaDataByViewer(
+  pageId: string | null, // either pageId or path must be specified
+  path: string | null, // either pageId or path must be specified
+  user?: HydratedDocument<IUser>,
+  isSharedPage = false,
+): Promise<
+  | IDataWithRequiredMeta<HydratedDocument<PageDocument>, IPageInfoExt>
+  | IDataWithRequiredMeta<null, IPageNotFoundInfo>
+> {
+  assert(pageId != null || path != null);
+
+  const Page = mongoose.model<HydratedDocument<PageDocument>, PageModel>(
+    'Page',
+  );
+
+  let page: HydratedDocument<PageDocument> | null;
+  if (pageId != null) {
+    // prioritized
+    page = await Page.findByIdAndViewer(pageId, user, null, true);
+  } else {
+    page = await Page.findByPathAndViewer(path, user, null, true, true);
+  }
+
+  // not found or forbidden
+  if (page == null) {
+    const count =
+      pageId != null
+        ? await Page.count({ _id: pageId })
+        : await Page.count({ path });
+    const isForbidden = count > 0;
+    return {
+      data: null,
+      meta: {
+        isNotFound: true,
+        isForbidden,
+      } satisfies IPageNotFoundInfo,
+    };
+  }
+
+  const isGuestUser = user == null;
+  const basicPageInfo = this.constructBasicPageInfo(page, isGuestUser);
+
+  if (isSharedPage) {
+    return {
+      data: page,
+      meta: {
+        ...basicPageInfo,
+        isMovable: false,
+        isDeletable: false,
+        isAbleToDeleteCompletely: false,
+        isRevertible: false,
+        bookmarkCount: 0,
+      } satisfies IPageInfo,
+    };
+  }
+
+  const Bookmark = mongoose.model<
+    BookmarkedPage,
+    { countDocuments; findByPageIdAndUserId }
+  >('Bookmark');
+  const bookmarkCount: number = await Bookmark.countDocuments({
+    page: pageId,
+  });
+
+  const pageInfo = {
+    ...basicPageInfo,
+    bookmarkCount,
+  };
+
+  if (isGuestUser) {
+    return {
+      data: page,
+      meta: {
+        ...pageInfo,
+        isDeletable: false,
+        isAbleToDeleteCompletely: false,
+      } satisfies IPageInfo,
+    };
+  }
+
+  const creatorId = await this.getCreatorIdForCanDelete(page);
+
+  const userRelatedGroups =
+    await this.pageGrantService.getUserRelatedGroups(user);
+
+  const canDeleteUserHomepage = await (async () => {
+    // Not a user homepage
+    if (!pagePathUtils.isUsersHomepage(page.path)) {
+      return true;
+    }
+
+    if (!this.canDeleteUserHomepageByConfig()) {
+      return false;
+    }
+
+    return await this.isUsersHomepageOwnerAbsent(page.path);
+  })();
+
+  const isDeletable =
+    canDeleteUserHomepage && this.canDelete(page, creatorId, user, false);
+
+  const isAbleToDeleteCompletely =
+    canDeleteUserHomepage &&
+    this.canDeleteCompletely(page, creatorId, user, false, userRelatedGroups); // use normal delete config
+
+  const isBookmarked: boolean = isGuestUser
+    ? false
+    : (await Bookmark.findByPageIdAndUserId(pageId, user._id)) != null;
+
+  if (pageInfo.isEmpty) {
+    return {
+      data: page,
+      meta: {
+        ...pageInfo,
+        isDeletable,
+        isAbleToDeleteCompletely,
+        isBookmarked,
+      } satisfies IPageInfoForEmpty,
+    };
+  }
+
+  // IPageInfoForEmpty and IPageInfoForEntity are mutually exclusive
+  // so hereafter we can safely
+  assert(isIPageInfoForEntity(pageInfo));
+
+  const isLiked: boolean = page.isLiked(user);
+  const subscription = await Subscription.findByUserIdAndTargetId(
+    user._id,
+    page._id,
+  );
+
+  return {
+    data: page,
+    meta: {
+      ...pageInfo,
+      isDeletable,
+      isAbleToDeleteCompletely,
+      isBookmarked,
+      isLiked,
+      subscriptionStatus: subscription?.status,
+    } satisfies IPageInfoForOperation,
+  };
+}