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

Merge pull request #10596 from growilabs/fix/175597-page-view-count-not-incremented-on-same-routing

fix: Page view count not incremented on same routing
Yuki Takei 3 месяцев назад
Родитель
Сommit
087e9e4392

+ 1 - 0
apps/app/config/logger/config.dev.js

@@ -15,6 +15,7 @@ module.exports = {
   'growi:routes:login': 'debug',
   'growi:routes:login': 'debug',
   'growi:routes:login-passport': 'debug',
   'growi:routes:login-passport': 'debug',
   'growi:middleware:safe-redirect': 'debug',
   'growi:middleware:safe-redirect': 'debug',
+  'growi:services:page': 'debug',
   'growi:service:PassportService': 'debug',
   'growi:service:PassportService': 'debug',
   'growi:service:s2s-messaging:*': 'debug',
   'growi:service:s2s-messaging:*': 'debug',
   'growi:service:yjs': 'debug',
   'growi:service:yjs': 'debug',

+ 11 - 15
apps/app/src/pages/[[...path]]/page-data-props.ts

@@ -15,7 +15,7 @@ import assert from 'assert';
 import type { HydratedDocument, model } from 'mongoose';
 import type { HydratedDocument, model } from 'mongoose';
 
 
 import type { CrowiRequest } from '~/interfaces/crowi-request';
 import type { CrowiRequest } from '~/interfaces/crowi-request';
-import type { PageModel } from '~/server/models/page';
+import type { PageDocument, PageModel } from '~/server/models/page';
 import type {
 import type {
   IPageRedirect,
   IPageRedirect,
   PageRedirectModel,
   PageRedirectModel,
@@ -193,11 +193,6 @@ export async function getPageDataForInitial(
       };
       };
     }
     }
 
 
-    // Add user to seen users
-    if (user != null) {
-      await page.seen(user);
-    }
-
     // Handle existing page with valid meta that is not IPageNotFoundInfo
     // Handle existing page with valid meta that is not IPageNotFoundInfo
     page.initLatestRevisionField(revisionId);
     page.initLatestRevisionField(revisionId);
     const ssrMaxRevisionBodyLength = configManager.getConfig(
     const ssrMaxRevisionBodyLength = configManager.getConfig(
@@ -250,15 +245,13 @@ export async function getPageDataForInitial(
 // Page data retrieval for same-route navigation
 // Page data retrieval for same-route navigation
 export async function getPageDataForSameRoute(
 export async function getPageDataForSameRoute(
   context: GetServerSidePropsContext,
   context: GetServerSidePropsContext,
-): Promise<
-  GetServerSidePropsResult<
-    Pick<CommonEachProps, 'currentPathname'> &
-      Pick<
-        EachProps,
-        'currentPathname' | 'isIdenticalPathPage' | 'redirectFrom'
-      >
-  >
-> {
+): Promise<{
+  props: Pick<CommonEachProps, 'currentPathname'> &
+    Pick<EachProps, 'currentPathname' | 'isIdenticalPathPage' | 'redirectFrom'>;
+  internalProps?: {
+    pageId?: string;
+  };
+}> {
   const req: CrowiRequest = context.req as CrowiRequest;
   const req: CrowiRequest = context.req as CrowiRequest;
   const { user } = req;
   const { user } = req;
 
 
@@ -298,5 +291,8 @@ export async function getPageDataForSameRoute(
       isIdenticalPathPage: false,
       isIdenticalPathPage: false,
       redirectFrom,
       redirectFrom,
     },
     },
+    internalProps: {
+      pageId: basicPageInfo?._id?.toString(),
+    },
   };
   };
 }
 }

+ 36 - 2
apps/app/src/pages/[[...path]]/server-side-props.ts

@@ -1,5 +1,7 @@
 import type { GetServerSidePropsContext, GetServerSidePropsResult } from 'next';
 import type { GetServerSidePropsContext, GetServerSidePropsResult } from 'next';
 
 
+import type { CrowiRequest } from '~/interfaces/crowi-request';
+
 import { getServerSideBasicLayoutProps } from '../basic-layout-page';
 import { getServerSideBasicLayoutProps } from '../basic-layout-page';
 import {
 import {
   getServerSideCommonInitialProps,
   getServerSideCommonInitialProps,
@@ -26,6 +28,30 @@ const nextjsRoutingProps = {
   },
   },
 };
 };
 
 
+/**
+ * Emit page seen event
+ * @param context - Next.js server-side context
+ * @param pageId - Page ID to mark as seen
+ */
+function emitPageSeenEvent(
+  context: GetServerSidePropsContext,
+  pageId?: string,
+): void {
+  if (pageId == null) {
+    return;
+  }
+
+  const req = context.req as CrowiRequest;
+  const { user, crowi } = req;
+
+  if (user == null) {
+    return;
+  }
+
+  const pageEvent = crowi.event('page');
+  pageEvent.emit('seen', pageId, user);
+}
+
 export async function getServerSidePropsForInitial(
 export async function getServerSidePropsForInitial(
   context: GetServerSidePropsContext,
   context: GetServerSidePropsContext,
 ): Promise<GetServerSidePropsResult<Stage2InitialProps>> {
 ): Promise<GetServerSidePropsResult<Stage2InitialProps>> {
@@ -75,6 +101,9 @@ export async function getServerSidePropsForInitial(
     throw new Error('Invalid merged props structure');
     throw new Error('Invalid merged props structure');
   }
   }
 
 
+  // Add user to seen users
+  emitPageSeenEvent(context, mergedProps.pageWithMeta?.data?._id);
+
   // -- TODO: persist activity
   // -- TODO: persist activity
   // await addActivity(context, getActivityAction(mergedProps));
   // await addActivity(context, getActivityAction(mergedProps));
   return mergedResult;
   return mergedResult;
@@ -85,16 +114,21 @@ export async function getServerSidePropsForSameRoute(
 ): Promise<GetServerSidePropsResult<Stage2EachProps>> {
 ): Promise<GetServerSidePropsResult<Stage2EachProps>> {
   // -- TODO: :https://redmine.weseek.co.jp/issues/174725
   // -- TODO: :https://redmine.weseek.co.jp/issues/174725
   // Remove getServerSideI18nProps from getServerSidePropsForSameRoute for performance improvement
   // Remove getServerSideI18nProps from getServerSidePropsForSameRoute for performance improvement
-  const [i18nPropsResult, pageDataResult] = await Promise.all([
+  const [i18nPropsResult, pageDataForSameRouteResult] = await Promise.all([
     getServerSideI18nProps(context, ['translation']),
     getServerSideI18nProps(context, ['translation']),
     getPageDataForSameRoute(context),
     getPageDataForSameRoute(context),
   ]);
   ]);
 
 
+  const { props: pageDataProps, internalProps } = pageDataForSameRouteResult;
+
+  // Add user to seen users
+  emitPageSeenEvent(context, internalProps?.pageId);
+
   // -- TODO: persist activity
   // -- TODO: persist activity
   // const mergedProps = await mergedResult.props;
   // const mergedProps = await mergedResult.props;
   // await addActivity(context, getActivityAction(mergedProps));
   // await addActivity(context, getActivityAction(mergedProps));
   const mergedResult = mergeGetServerSidePropsResults(
   const mergedResult = mergeGetServerSidePropsResults(
-    pageDataResult,
+    { props: pageDataProps },
     i18nPropsResult,
     i18nPropsResult,
   );
   );
 
 

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

@@ -230,6 +230,36 @@ class PageService implements IPageService {
     // createMany
     // createMany
     this.pageEvent.on('createMany', this.pageEvent.onCreateMany);
     this.pageEvent.on('createMany', this.pageEvent.onCreateMany);
     this.pageEvent.on('addSeenUsers', this.pageEvent.onAddSeenUsers);
     this.pageEvent.on('addSeenUsers', this.pageEvent.onAddSeenUsers);
+
+    // seen - mark page as seen by user
+    this.pageEvent.on(
+      'seen',
+      async (pageId: string, user: IUserHasId): Promise<void> => {
+        if (pageId == null || user == null) {
+          logger.warn('onSeen: pageId or user is null');
+          return;
+        }
+
+        try {
+          const Page = mongoose.model<
+            HydratedDocument<PageDocument>,
+            PageModel
+          >('Page');
+
+          const page = await Page.findById(pageId);
+
+          if (page == null) {
+            logger.warn('onSeen: page not found', { pageId });
+            return;
+          }
+
+          await page.seen(user);
+          logger.debug('onSeen: successfully marked page as seen', { pageId });
+        } catch (err) {
+          logger.error('onSeen: failed to mark page as seen', err);
+        }
+      },
+    );
   }
   }
 
 
   getEventEmitter(): EventEmitter {
   getEventEmitter(): EventEmitter {