Browse Source

WIP: impl custom serializer for superjson

Yuki Takei 3 years ago
parent
commit
c897c64755

+ 3 - 3
packages/app/src/interfaces/crowi-request.ts

@@ -1,10 +1,10 @@
 import { Request } from 'express';
 
-import { IUserHasId } from './user';
+import { IUser, IUserHasId } from './user';
 
-export interface CrowiRequest extends Request {
+export interface CrowiRequest<U extends IUser = IUserHasId> extends Request {
 
-  user?: IUserHasId,
+  user?: U,
 
   // eslint-disable-next-line @typescript-eslint/no-explicit-any
   crowi: any,

+ 58 - 28
packages/app/src/pages/[[...path]].page.tsx

@@ -1,9 +1,10 @@
 import React, { useEffect } from 'react';
 
+
 import EventEmitter from 'events';
 
 import {
-  IDataWithMeta, IPageInfoForEntity, IPagePopulatedToShowRevision, isClient, pagePathUtils, pathUtils,
+  IDataWithMeta, IPageInfoForEntity, IPagePopulatedToShowRevision, isClient, isIPageInfoForEntity, IUserHasId, pagePathUtils, pathUtils,
 } from '@growi/core';
 import ExtensibleCustomError from 'extensible-custom-error';
 import {
@@ -13,6 +14,7 @@ import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
 import dynamic from 'next/dynamic';
 import Head from 'next/head';
 import { useRouter } from 'next/router';
+import superjson from 'superjson';
 
 import { PageAlerts } from '~/components/PageAlert/PageAlerts';
 // import { PageComments } from '~/components/PageComment/PageComments';
@@ -25,8 +27,9 @@ import { CrowiRequest } from '~/interfaces/crowi-request';
 import { CustomWindow } from '~/interfaces/global';
 import { RendererConfig } from '~/interfaces/services/renderer';
 import { ISidebarConfig } from '~/interfaces/sidebar-config';
+import { IUserUISettings } from '~/interfaces/user-ui-settings';
 import { PageModel, PageDocument } from '~/server/models/page';
-import UserUISettings, { UserUISettingsDocument } from '~/server/models/user-ui-settings';
+import UserUISettings from '~/server/models/user-ui-settings';
 import Xss from '~/services/xss';
 import { useSWRxCurrentPage, useSWRxPageInfo, useSWRxPage } from '~/stores/page';
 import {
@@ -66,12 +69,42 @@ import {
 
 
 const logger = loggerFactory('growi:pages:all');
+
 const {
   isPermalink: _isPermalink, isUsersHomePage, isTrashPage: _isTrashPage, isUserPage, isCreatablePage,
 } = pagePathUtils;
 const { removeHeadingSlash } = pathUtils;
 
 
+type IPageToShowRevisionWithMeta = IDataWithMeta<IPagePopulatedToShowRevision & PageDocument, IPageInfoForEntity>;
+type IPageToShowRevisionWithMetaSerialized = IDataWithMeta<string, string>;
+// register custom serializer
+superjson.registerCustom<IPageToShowRevisionWithMeta, IPageToShowRevisionWithMetaSerialized>(
+  {
+    isApplicable: (v): v is IPageToShowRevisionWithMeta => {
+      return v?.data != null
+        && v?.data.toObject != null
+        && v?.meta != null
+        && isIPageInfoForEntity(v.meta);
+    },
+    serialize: (v) => {
+      return {
+        data: superjson.stringify(v.data.toObject()),
+        // data: superjson.stringify(v.data),
+        meta: superjson.stringify(v.meta),
+      };
+    },
+    deserialize: (v) => {
+      return {
+        data: superjson.parse(v.data),
+        meta: v.meta != null ? superjson.parse(v.meta) : undefined,
+      };
+    },
+  },
+  'IPageToShowRevisionWithMetaTransformer',
+);
+
+
 const IdenticalPathPage = (): JSX.Element => {
   const IdenticalPathPage = dynamic(() => import('../components/IdenticalPathPage').then(mod => mod.IdenticalPathPage), { ssr: false });
   return <IdenticalPathPage />;
@@ -82,12 +115,10 @@ const PutbackPageModal = (): JSX.Element => {
   return <PutbackPageModal />;
 };
 
-type IPageToShowRevisionWithMeta = IDataWithMeta<IPagePopulatedToShowRevision, IPageInfoForEntity>;
-
 type Props = CommonProps & {
-  currentUser: string,
+  currentUser: IUserHasId,
 
-  pageWithMetaStr: string,
+  pageWithMeta: IPageToShowRevisionWithMeta,
   // pageUser?: any,
   // redirectTo?: string;
   // redirectFrom?: string;
@@ -127,7 +158,7 @@ type Props = CommonProps & {
   rendererConfig: RendererConfig,
 
   // UI
-  userUISettings: UserUISettingsDocument | null
+  userUISettings: IUserUISettings | null
   // Sidebar
   sidebarConfig: ISidebarConfig,
 };
@@ -139,7 +170,7 @@ const GrowiPage: NextPage<Props> = (props: Props) => {
   const UnsavedAlertDialog = dynamic(() => import('./UnsavedAlertDialog'), { ssr: false });
   const GrowiSubNavigationSwitcher = dynamic(() => import('../components/Navbar/GrowiSubNavigationSwitcher'), { ssr: false });
 
-  const { data: currentUser } = useCurrentUser(props.currentUser != null ? JSON.parse(props.currentUser) : null);
+  const { data: currentUser } = useCurrentUser(props.currentUser ?? null);
 
   // register global EventEmitter
   if (isClient()) {
@@ -196,10 +227,9 @@ const GrowiPage: NextPage<Props> = (props: Props) => {
 
   // const { data: editorMode } = useEditorMode();
 
-  let pageWithMeta: IPageToShowRevisionWithMeta | undefined;
-  if (props.pageWithMetaStr != null) {
-    pageWithMeta = JSON.parse(props.pageWithMetaStr) as IPageToShowRevisionWithMeta;
-  }
+  const { pageWithMeta, userUISettings } = props;
+
+  console.log({ pageWithMeta, currentUser, userUISettings });
 
   let shouldRenderPutbackPageModal = false;
   if (pageWithMeta != null) {
@@ -319,7 +349,7 @@ class MultiplePagesHitsError extends ExtensibleCustomError {
 
 }
 
-async function getPageData(context: GetServerSidePropsContext, props: Props): Promise<IPageToShowRevisionWithMeta|null> {
+async function injectPageData(context: GetServerSidePropsContext, props: Props): Promise<void> {
   const req: CrowiRequest = context.req as CrowiRequest;
   const { crowi } = req;
   const { revisionId } = req.query;
@@ -342,8 +372,8 @@ async function getPageData(context: GetServerSidePropsContext, props: Props): Pr
     }
   }
 
-  const result: IPageToShowRevisionWithMeta = await pageService.findPageAndMetaDataByViewer(pageId, currentPathname, user, true); // includeEmpty = true, isSharedPage = false
-  const page = result?.data as unknown as PageDocument;
+  const pageWithMeta: IPageToShowRevisionWithMeta = await pageService.findPageAndMetaDataByViewer(pageId, currentPathname, user, true); // includeEmpty = true, isSharedPage = false
+  const page = pageWithMeta?.data as unknown as PageDocument;
 
   // populate & check if the revision is latest
   if (page != null) {
@@ -352,10 +382,10 @@ async function getPageData(context: GetServerSidePropsContext, props: Props): Pr
     props.isLatestRevision = page.isLatestRevision();
   }
 
-  return result;
+  props.pageWithMeta = pageWithMeta;
 }
 
-async function injectRoutingInformation(context: GetServerSidePropsContext, props: Props, pageWithMeta: IPageToShowRevisionWithMeta|null): Promise<void> {
+async function injectRoutingInformation(context: GetServerSidePropsContext, props: Props): Promise<void> {
   const req: CrowiRequest = context.req as CrowiRequest;
   const { crowi } = req;
   const Page = crowi.model('Page') as PageModel;
@@ -364,7 +394,7 @@ async function injectRoutingInformation(context: GetServerSidePropsContext, prop
   const pageId = getPageIdFromPathname(currentPathname);
   const isPermalink = _isPermalink(currentPathname);
 
-  const page = pageWithMeta?.data;
+  const page = props.pageWithMeta?.data;
 
   if (props.isIdenticalPathPage) {
     // TBD
@@ -477,7 +507,7 @@ async function injectNextI18NextConfigurations(context: GetServerSidePropsContex
 }
 
 export const getServerSideProps: GetServerSideProps = async(context: GetServerSidePropsContext) => {
-  const req: CrowiRequest = context.req as CrowiRequest;
+  const req = context.req as CrowiRequest<IUserHasId & any>;
   const { user } = req;
 
   const result = await getServerSideCommonProps(context);
@@ -490,10 +520,8 @@ export const getServerSideProps: GetServerSideProps = async(context: GetServerSi
   }
 
   const props: Props = result.props as Props;
-  let pageWithMeta;
   try {
-    pageWithMeta = await getPageData(context, props);
-    props.pageWithMetaStr = JSON.stringify(pageWithMeta);
+    await injectPageData(context, props);
   }
   catch (err) {
     if (err instanceof MultiplePagesHitsError) {
@@ -504,17 +532,19 @@ export const getServerSideProps: GetServerSideProps = async(context: GetServerSi
     }
   }
 
-  injectRoutingInformation(context, props, pageWithMeta);
+  injectRoutingInformation(context, props);
   injectServerConfigurations(context, props);
   injectNextI18NextConfigurations(context, props, ['translation']);
 
-  if (user != null) {
-    props.currentUser = JSON.stringify(user);
-  }
+  // if (user != null) {
+  //   props.currentUser = superjson.stringify(user.toObject());
+  // }
 
   // UI
-  const userUISettings = user == null ? null : await UserUISettings.findOne({ user: user._id }).exec();
-  props.userUISettings = JSON.parse(JSON.stringify(userUISettings));
+  // const userUISettings = user == null ? null : await UserUISettings.findOne({ user: user._id }).exec();
+  // if (userUISettings != null) {
+  //   props.userUISettings = userUISettings.toJSON();
+  // }
 
   return {
     props,