commons.ts 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. import type { ColorScheme, IUserHasId } from '@growi/core';
  2. import type { GetServerSideProps, GetServerSidePropsContext } from 'next';
  3. import type { CrowiRequest } from '~/interfaces/crowi-request';
  4. import { getGrowiVersion } from '~/utils/growi-version';
  5. import loggerFactory from '~/utils/logger';
  6. const logger = loggerFactory('growi:pages:common-props:commons');
  7. export type CommonInitialProps = {
  8. isNextjsRoutingTypeInitial: true,
  9. appTitle: string,
  10. siteUrl: string | undefined,
  11. confidential: string,
  12. growiVersion: string,
  13. isDefaultLogo: boolean,
  14. customTitleTemplate: string,
  15. growiCloudUri: string | undefined,
  16. forcedColorScheme?: ColorScheme,
  17. };
  18. export const getServerSideCommonInitialProps: GetServerSideProps<CommonInitialProps> = async(context: GetServerSidePropsContext) => {
  19. const req = context.req as CrowiRequest;
  20. const { crowi } = req;
  21. const {
  22. appService, configManager, attachmentService, customizeService,
  23. } = crowi;
  24. const isCustomizedLogoUploaded = await attachmentService.isBrandLogoExist();
  25. const isDefaultLogo = crowi.configManager.getConfig('customize:isDefaultLogo') || !isCustomizedLogoUploaded;
  26. const forcedColorScheme = crowi.customizeService.forcedColorScheme;
  27. return {
  28. props: {
  29. isNextjsRoutingTypeInitial: true,
  30. appTitle: appService.getAppTitle(),
  31. siteUrl: configManager.getConfig('app:siteUrl'), // DON'T USE growiInfoService.getSiteUrl()
  32. confidential: appService.getAppConfidential() || '',
  33. growiVersion: getGrowiVersion(),
  34. isDefaultLogo,
  35. customTitleTemplate: customizeService.customTitleTemplate,
  36. growiCloudUri: configManager.getConfig('app:growiCloudUri'),
  37. forcedColorScheme,
  38. },
  39. };
  40. };
  41. export type CommonEachProps = {
  42. currentPathname: string,
  43. nextjsRoutingPage: string | null, // must be set by each page
  44. currentUser?: IUserHasId,
  45. csrfToken: string,
  46. isMaintenanceMode: boolean,
  47. redirectDestination?: string | null,
  48. };
  49. /**
  50. * Type guard for SameRouteEachProps validation
  51. * Lightweight validation for same-route navigation
  52. */
  53. export function isValidCommonEachRouteProps(props: unknown): props is CommonEachProps {
  54. if (typeof props !== 'object' || props === null) {
  55. logger.warn('isValidCommonEachRouteProps: props is not an object or is null');
  56. return false;
  57. }
  58. const p = props as Record<string, unknown>;
  59. // Essential properties validation
  60. if (typeof p.nextjsRoutingPage !== 'string' && p.nextjsRoutingPage !== null) {
  61. logger.warn('isValidCommonEachRouteProps: nextjsRoutingPage is not a string or null', { nextjsRoutingPage: p.nextjsRoutingPage });
  62. return false;
  63. }
  64. if (typeof p.currentPathname !== 'string') {
  65. logger.warn('isValidCommonEachRouteProps: currentPathname is not a string', { currentPathname: p.currentPathname });
  66. return false;
  67. }
  68. if (typeof p.csrfToken !== 'string') {
  69. logger.warn('isValidCommonEachRouteProps: csrfToken is not a string', { csrfToken: p.csrfToken });
  70. return false;
  71. }
  72. if (typeof p.isMaintenanceMode !== 'boolean') {
  73. logger.warn('isValidCommonEachRouteProps: isMaintenanceMode is not a boolean', { isMaintenanceMode: p.isMaintenanceMode });
  74. return false;
  75. }
  76. if (typeof p.isIdenticalPathPage !== 'boolean') {
  77. logger.warn('isValidCommonEachRouteProps: isIdenticalPathPage is not a boolean', { isIdenticalPathPage: p.isIdenticalPathPage });
  78. return false;
  79. }
  80. return true;
  81. }
  82. export const getServerSideCommonEachProps: GetServerSideProps<Omit<CommonEachProps, 'nextjsRoutingPage'>> = async(context: GetServerSidePropsContext) => {
  83. const req = context.req as CrowiRequest;
  84. const { crowi, user } = req;
  85. const { appService } = crowi;
  86. const url = new URL(context.resolvedUrl, 'http://example.com');
  87. const currentPathname = decodeURIComponent(url.pathname);
  88. const isMaintenanceMode = appService.isMaintenanceMode();
  89. let currentUser;
  90. if (user != null) {
  91. currentUser = user.toObject();
  92. }
  93. // Redirect destination for page transition by next/link
  94. let redirectDestination: string | null = null;
  95. if (!crowi.aclService.isGuestAllowedToRead() && currentUser == null) {
  96. redirectDestination = '/login';
  97. }
  98. else if (!isMaintenanceMode && currentPathname === '/maintenance') {
  99. redirectDestination = '/';
  100. }
  101. else if (isMaintenanceMode && !currentPathname.match('/admin/*') && !(currentPathname === '/maintenance')) {
  102. redirectDestination = '/maintenance';
  103. }
  104. else {
  105. redirectDestination = null;
  106. }
  107. return {
  108. props: {
  109. currentPathname,
  110. currentUser,
  111. csrfToken: req.csrfToken(),
  112. isMaintenanceMode,
  113. redirectDestination,
  114. },
  115. };
  116. };