|
|
@@ -1,24 +1,64 @@
|
|
|
+import type { IPage, IUser } from '@growi/core/dist/interfaces';
|
|
|
+import { isPermalink as _isPermalink, isCreatablePage, isTopPage } from '@growi/core/dist/utils/page-path-utils';
|
|
|
+import { removeHeadingSlash } from '@growi/core/dist/utils/path-utils';
|
|
|
+import type { model } from 'mongoose';
|
|
|
import type { GetServerSidePropsContext, GetServerSidePropsResult } from 'next';
|
|
|
|
|
|
import type { CrowiRequest } from '~/interfaces/crowi-request';
|
|
|
import type { PageModel } from '~/server/models/page';
|
|
|
-import type { PageRedirectModel } from '~/server/models/page-redirect';
|
|
|
+import type { IPageRedirect, PageRedirectModel } from '~/server/models/page-redirect';
|
|
|
|
|
|
import type { InitialProps, SameRouteEachProps } from './types';
|
|
|
|
|
|
+// Utility to resolve path, redirect, and identical path page check
|
|
|
+type PathResolutionResult = {
|
|
|
+ resolvedPathname: string;
|
|
|
+ isIdenticalPathPage: boolean;
|
|
|
+ redirectFrom?: string;
|
|
|
+};
|
|
|
+
|
|
|
+let mongooseModel: typeof model;
|
|
|
+let Page: PageModel;
|
|
|
+let PageRedirect: PageRedirectModel;
|
|
|
+
|
|
|
+async function resolvePathAndCheckIdentical(
|
|
|
+ path: string,
|
|
|
+ user: IUser | undefined,
|
|
|
+): Promise<PathResolutionResult> {
|
|
|
+ if (mongooseModel == null) {
|
|
|
+ mongooseModel = (await import('mongoose')).model;
|
|
|
+ }
|
|
|
+ if (Page == null) {
|
|
|
+ Page = mongooseModel<IPage, PageModel>('Page');
|
|
|
+ }
|
|
|
+ if (PageRedirect == null) {
|
|
|
+ PageRedirect = mongooseModel<IPageRedirect, PageRedirectModel>('PageRedirect');
|
|
|
+ }
|
|
|
+
|
|
|
+ const isPermalink = _isPermalink(path);
|
|
|
+ let resolvedPathname = path;
|
|
|
+ let redirectFrom: string | undefined;
|
|
|
+ let isIdenticalPathPage = false;
|
|
|
+
|
|
|
+ if (!isPermalink) {
|
|
|
+ const chains = await PageRedirect.retrievePageRedirectEndpoints(path);
|
|
|
+ if (chains != null) {
|
|
|
+ resolvedPathname = chains.end.toPath;
|
|
|
+ redirectFrom = chains.start.fromPath;
|
|
|
+ }
|
|
|
+ const multiplePagesCount = await Page.countByPathAndViewer(resolvedPathname, user, null, true);
|
|
|
+ isIdenticalPathPage = multiplePagesCount > 1;
|
|
|
+ }
|
|
|
+ return { resolvedPathname, isIdenticalPathPage, redirectFrom };
|
|
|
+}
|
|
|
+
|
|
|
// Page data retrieval for initial load - returns GetServerSidePropsResult
|
|
|
export async function getPageDataForInitial(
|
|
|
context: GetServerSidePropsContext,
|
|
|
): Promise<GetServerSidePropsResult<
|
|
|
- Pick<InitialProps, 'pageWithMeta' | 'isNotFound' | 'isNotCreatable' | 'isForbidden' | 'skipSSR'> &
|
|
|
- Pick<SameRouteEachProps, 'currentPathname' | 'isIdenticalPathPage'>
|
|
|
- >> {
|
|
|
- const { pagePathUtils, pathUtils } = await import('@growi/core/dist/utils');
|
|
|
- const { model: mongooseModel } = await import('mongoose');
|
|
|
-
|
|
|
- const { isPermalink: _isPermalink, isCreatablePage } = pagePathUtils;
|
|
|
- const { removeHeadingSlash } = pathUtils;
|
|
|
-
|
|
|
+ Pick<InitialProps, 'pageWithMeta' | 'isNotFound' | 'isNotCreatable' | 'isForbidden' | 'skipSSR'> &
|
|
|
+ Pick<SameRouteEachProps, 'currentPathname' | 'isIdenticalPathPage'>
|
|
|
+>> {
|
|
|
const req: CrowiRequest = context.req as CrowiRequest;
|
|
|
const { crowi, user } = req;
|
|
|
const { revisionId } = req.query;
|
|
|
@@ -29,44 +69,29 @@ export async function getPageDataForInitial(
|
|
|
let pathFromUrl = `/${pathFromQuery.join('/')}`;
|
|
|
pathFromUrl = pathFromUrl === '//' ? '/' : pathFromUrl;
|
|
|
|
|
|
- const Page = crowi.model('Page') as PageModel;
|
|
|
- const PageRedirect = mongooseModel('PageRedirect') as PageRedirectModel;
|
|
|
const { pageService, configManager } = crowi;
|
|
|
|
|
|
const pageId = _isPermalink(pathFromUrl) ? removeHeadingSlash(pathFromUrl) : null;
|
|
|
const isPermalink = _isPermalink(pathFromUrl);
|
|
|
|
|
|
- let currentPathname = pathFromUrl;
|
|
|
+ const { resolvedPathname, isIdenticalPathPage } = await resolvePathAndCheckIdentical(pathFromUrl, user);
|
|
|
|
|
|
- // Check for redirects and whether the path is identical
|
|
|
- if (!isPermalink) {
|
|
|
- const chains = await PageRedirect.retrievePageRedirectEndpoints(pathFromUrl);
|
|
|
- if (chains != null) {
|
|
|
- currentPathname = chains.end.toPath;
|
|
|
- }
|
|
|
-
|
|
|
- // Check multiple pages hits
|
|
|
- const multiplePagesCount = await Page.countByPathAndViewer(currentPathname, user, null, true);
|
|
|
- const isIdenticalPathPage = multiplePagesCount > 1;
|
|
|
-
|
|
|
- // Early return for identical path pages
|
|
|
- if (isIdenticalPathPage) {
|
|
|
- return {
|
|
|
- props: {
|
|
|
- currentPathname,
|
|
|
- isIdenticalPathPage: true,
|
|
|
- pageWithMeta: null,
|
|
|
- isNotFound: false,
|
|
|
- isNotCreatable: true,
|
|
|
- isForbidden: false,
|
|
|
- skipSSR: false,
|
|
|
- },
|
|
|
- };
|
|
|
- }
|
|
|
+ if (isIdenticalPathPage) {
|
|
|
+ return {
|
|
|
+ props: {
|
|
|
+ currentPathname: resolvedPathname,
|
|
|
+ isIdenticalPathPage: true,
|
|
|
+ pageWithMeta: null,
|
|
|
+ isNotFound: false,
|
|
|
+ isNotCreatable: true,
|
|
|
+ isForbidden: false,
|
|
|
+ skipSSR: false,
|
|
|
+ },
|
|
|
+ };
|
|
|
}
|
|
|
|
|
|
// Get full page data
|
|
|
- const pageWithMeta = await pageService.findPageAndMetaDataByViewer(pageId, currentPathname, user, true);
|
|
|
+ const pageWithMeta = await pageService.findPageAndMetaDataByViewer(pageId, resolvedPathname, user, true);
|
|
|
const { data: page, meta } = pageWithMeta ?? {};
|
|
|
|
|
|
// Add user to seen users
|
|
|
@@ -86,13 +111,13 @@ export async function getPageDataForInitial(
|
|
|
const populatedPage = await page.populateDataToShowRevision(skipSSR);
|
|
|
|
|
|
// Handle URL conversion
|
|
|
- let finalPathname = currentPathname;
|
|
|
+ let finalPathname = resolvedPathname;
|
|
|
if (page != null && !page.isEmpty) {
|
|
|
if (isPermalink) {
|
|
|
finalPathname = page.path;
|
|
|
}
|
|
|
else {
|
|
|
- const isToppage = pagePathUtils.isTopPage(currentPathname);
|
|
|
+ const isToppage = isTopPage(resolvedPathname);
|
|
|
if (!isToppage) {
|
|
|
finalPathname = `/${page._id}`;
|
|
|
}
|
|
|
@@ -115,15 +140,15 @@ export async function getPageDataForInitial(
|
|
|
// Handle non-existent page
|
|
|
const count = isPermalink
|
|
|
? await Page.count({ _id: pageId })
|
|
|
- : await Page.count({ path: currentPathname });
|
|
|
+ : await Page.count({ path: resolvedPathname });
|
|
|
|
|
|
return {
|
|
|
props: {
|
|
|
- currentPathname,
|
|
|
+ currentPathname: resolvedPathname,
|
|
|
isIdenticalPathPage: false,
|
|
|
pageWithMeta: null,
|
|
|
isNotFound: true,
|
|
|
- isNotCreatable: !isCreatablePage(currentPathname),
|
|
|
+ isNotCreatable: !isCreatablePage(resolvedPathname),
|
|
|
isForbidden: count > 0,
|
|
|
skipSSR: false,
|
|
|
},
|
|
|
@@ -134,48 +159,23 @@ export async function getPageDataForInitial(
|
|
|
export async function getPageDataForSameRoute(
|
|
|
context: GetServerSidePropsContext,
|
|
|
): Promise<GetServerSidePropsResult<Pick<SameRouteEachProps, 'currentPathname' | 'isIdenticalPathPage' | 'redirectFrom'>>> {
|
|
|
- const { pagePathUtils, pathUtils } = await import('@growi/core/dist/utils');
|
|
|
- const { model: mongooseModel } = await import('mongoose');
|
|
|
-
|
|
|
- const { isPermalink: _isPermalink } = pagePathUtils;
|
|
|
- const { removeHeadingSlash } = pathUtils;
|
|
|
-
|
|
|
const req: CrowiRequest = context.req as CrowiRequest;
|
|
|
- const { crowi, user } = req;
|
|
|
-
|
|
|
- const Page = crowi.model('Page') as PageModel;
|
|
|
- const PageRedirect = mongooseModel('PageRedirect') as PageRedirectModel;
|
|
|
+ const { user } = req;
|
|
|
|
|
|
const currentPathname = decodeURIComponent(context.resolvedUrl?.split('?')[0] ?? '/');
|
|
|
const pageId = _isPermalink(currentPathname) ? removeHeadingSlash(currentPathname) : null;
|
|
|
const isPermalink = _isPermalink(currentPathname);
|
|
|
|
|
|
- // Handle redirects
|
|
|
- let resolvedPathname = currentPathname;
|
|
|
- let redirectFrom: string | undefined;
|
|
|
-
|
|
|
- // Check for redirects and whether the path is identical
|
|
|
- if (!isPermalink) {
|
|
|
- const chains = await PageRedirect.retrievePageRedirectEndpoints(currentPathname);
|
|
|
- if (chains != null) {
|
|
|
- resolvedPathname = chains.end.toPath;
|
|
|
- redirectFrom = chains.start.fromPath;
|
|
|
- }
|
|
|
+ const { resolvedPathname, isIdenticalPathPage, redirectFrom } = await resolvePathAndCheckIdentical(currentPathname, user);
|
|
|
|
|
|
- // Check multiple pages hits
|
|
|
- const multiplePagesCount = await Page.countByPathAndViewer(resolvedPathname, user, null, true);
|
|
|
- const isIdenticalPathPage = multiplePagesCount > 1;
|
|
|
-
|
|
|
- // Early return for identical path pages
|
|
|
- if (isIdenticalPathPage) {
|
|
|
- return {
|
|
|
- props: {
|
|
|
- currentPathname: resolvedPathname,
|
|
|
- isIdenticalPathPage: true,
|
|
|
- redirectFrom,
|
|
|
- },
|
|
|
- };
|
|
|
- }
|
|
|
+ if (isIdenticalPathPage) {
|
|
|
+ return {
|
|
|
+ props: {
|
|
|
+ currentPathname: resolvedPathname,
|
|
|
+ isIdenticalPathPage: true,
|
|
|
+ redirectFrom,
|
|
|
+ },
|
|
|
+ };
|
|
|
}
|
|
|
|
|
|
// For same route access, do minimal page lookup
|
|
|
@@ -189,7 +189,7 @@ export async function getPageDataForSameRoute(
|
|
|
finalPathname = basicPageInfo.path;
|
|
|
}
|
|
|
else {
|
|
|
- const isToppage = pagePathUtils.isTopPage(resolvedPathname);
|
|
|
+ const isToppage = isTopPage(resolvedPathname);
|
|
|
if (!isToppage) {
|
|
|
finalPathname = `/${basicPageInfo._id}`;
|
|
|
}
|