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

feat: implement page view tracking by emitting seen event for users

Shun Miyazawa 3 месяцев назад
Родитель
Сommit
1d1144ba97

+ 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',

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

@@ -249,7 +249,7 @@ export async function getPageDataForSameRoute(
   props: Pick<CommonEachProps, 'currentPathname'> &
   props: Pick<CommonEachProps, 'currentPathname'> &
     Pick<EachProps, 'currentPathname' | 'isIdenticalPathPage' | 'redirectFrom'>;
     Pick<EachProps, 'currentPathname' | 'isIdenticalPathPage' | 'redirectFrom'>;
   internalProps?: {
   internalProps?: {
-    basicPageInfo: PageDocument | null;
+    pageId?: string;
   };
   };
 }> {
 }> {
   const req: CrowiRequest = context.req as CrowiRequest;
   const req: CrowiRequest = context.req as CrowiRequest;
@@ -292,7 +292,7 @@ export async function getPageDataForSameRoute(
       redirectFrom,
       redirectFrom,
     },
     },
     internalProps: {
     internalProps: {
-      basicPageInfo,
+      pageId: basicPageInfo?._id?.toString(),
     },
     },
   };
   };
 }
 }

+ 12 - 19
apps/app/src/pages/[[...path]]/server-side-props.ts

@@ -31,19 +31,6 @@ const nextjsRoutingProps = {
   },
   },
 };
 };
 
 
-/**
- * Add user to page's seen users list
- */
-function addSeenUser(
-  page: PageDocument | null | undefined,
-  user: HydratedDocument<IUser> | undefined,
-): void {
-  if (page != null && user != null) {
-    // addSeenUser is executed asynchronously and increments the view count in real-time via Socket.io
-    page.seen(user);
-  }
-}
-
 export async function getServerSidePropsForInitial(
 export async function getServerSidePropsForInitial(
   context: GetServerSidePropsContext,
   context: GetServerSidePropsContext,
 ): Promise<GetServerSidePropsResult<Stage2InitialProps>> {
 ): Promise<GetServerSidePropsResult<Stage2InitialProps>> {
@@ -95,9 +82,12 @@ export async function getServerSidePropsForInitial(
 
 
   // Add user to seen users
   // Add user to seen users
   const req = context.req as CrowiRequest;
   const req = context.req as CrowiRequest;
-  const { user } = req;
-  const pageData = mergedProps.pageWithMeta?.data;
-  addSeenUser(pageData, user);
+  const { user, crowi } = req;
+  const pageEvent = crowi.event('page');
+  const pageId = mergedProps.pageWithMeta?.data?._id;
+  if (pageId != null && user != null) {
+    pageEvent.emit('seen', pageId, user);
+  }
 
 
   // -- TODO: persist activity
   // -- TODO: persist activity
   // await addActivity(context, getActivityAction(mergedProps));
   // await addActivity(context, getActivityAction(mergedProps));
@@ -115,12 +105,15 @@ export async function getServerSidePropsForSameRoute(
   ]);
   ]);
 
 
   const { props: pageDataProps, internalProps } = pageDataForSameRouteResult;
   const { props: pageDataProps, internalProps } = pageDataForSameRouteResult;
-  const pageData = internalProps?.basicPageInfo;
 
 
   // Add user to seen users
   // Add user to seen users
   const req = context.req as CrowiRequest;
   const req = context.req as CrowiRequest;
-  const { user } = req;
-  addSeenUser(pageData, user);
+  const { user, crowi } = req;
+  const pageEvent = crowi.event('page');
+  const pageId = internalProps?.pageId;
+  if (pageId != null && user != null) {
+    pageEvent.emit('seen', pageId, user);
+  }
 
 
   // -- TODO: persist activity
   // -- TODO: persist activity
   // const mergedProps = await mergedResult.props;
   // const mergedProps = await mergedResult.props;

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

@@ -202,6 +202,30 @@ 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 {