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

Merge pull request #6186 from weseek/support/apply-nextjs-loginPage

support: Login pages rendering with next.js
Shun Murai 3 лет назад
Родитель
Сommit
fcc8f6a0de

+ 10 - 9
packages/app/src/components/LoginForm.jsx

@@ -4,11 +4,8 @@ import { useTranslation } from 'next-i18next';
 import PropTypes from 'prop-types';
 import ReactCardFlip from 'react-card-flip';
 
-import AppContainer from '~/client/services/AppContainer';
 import { useCsrfToken } from '~/stores/context';
 
-import { withUnstatedContainers } from './UnstatedUtils';
-
 class LoginForm extends React.Component {
 
   constructor(props) {
@@ -148,7 +145,7 @@ class LoginForm extends React.Component {
   renderRegisterForm() {
     const {
       t,
-      appContainer,
+      // appContainer,
       csrfToken,
       isEmailAuthenticationEnabled,
       username,
@@ -156,9 +153,9 @@ class LoginForm extends React.Component {
       email,
       registrationMode,
       registrationWhiteList,
+      isMailerSetup,
     } = this.props;
 
-    const { isMailerSetup } = appContainer.config;
     let registerAction = '/register';
 
     let submitText = t('Sign up');
@@ -288,8 +285,10 @@ class LoginForm extends React.Component {
       objOfIsExternalAuthEnableds,
     } = this.props;
 
+
     const isLocalOrLdapStrategiesEnabled = isLocalStrategySetup || isLdapStrategySetup;
-    const isSomeExternalAuthEnabled = Object.values(objOfIsExternalAuthEnableds).some(elem => elem);
+    // const isSomeExternalAuthEnabled = Object.values(objOfIsExternalAuthEnableds).some(elem => elem);
+    const isSomeExternalAuthEnabled = true;
 
     return (
       <div className="login-dialog mx-auto" id="login-dialog">
@@ -332,7 +331,7 @@ class LoginForm extends React.Component {
 LoginForm.propTypes = {
   // i18next
   t: PropTypes.func.isRequired,
-  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
+  // appContainer: PropTypes.instanceOf(AppContainer).isRequired,
 
   csrfToken: PropTypes.string,
   isRegistering: PropTypes.bool,
@@ -347,6 +346,7 @@ LoginForm.propTypes = {
   isLocalStrategySetup: PropTypes.bool,
   isLdapStrategySetup: PropTypes.bool,
   objOfIsExternalAuthEnableds: PropTypes.object,
+  isMailerSetup: PropTypes.bool,
 };
 
 const LoginFormWrapperFC = (props) => {
@@ -359,6 +359,7 @@ const LoginFormWrapperFC = (props) => {
 /**
  * Wrapper component for using unstated
  */
-const LoginFormWrapper = withUnstatedContainers(LoginFormWrapperFC, [AppContainer]);
+// const LoginFormWrapper = withUnstatedContainers(LoginFormWrapperFC, [AppContainer]);
 
-export default LoginFormWrapper;
+// export default LoginForm;
+export default LoginFormWrapperFC;

+ 2 - 1
packages/app/src/components/UnstatedUtils.tsx

@@ -2,7 +2,7 @@
 
 import React from 'react';
 
-import { Subscribe } from 'unstated';
+import { Provider, Subscribe } from 'unstated';
 
 /**
  * generate K/V object by specified instances
@@ -15,6 +15,7 @@ import { Subscribe } from 'unstated';
  *     exampleContainer: <ExampleContainer />,
  *   }
  */
+
 function generateAutoNamedProps(instances) {
   const props = {};
 

+ 0 - 2
packages/app/src/pages/[[...path]].page.tsx

@@ -411,9 +411,7 @@ async function injectRoutingInformation(context: GetServerSidePropsContext, prop
   }
   else if (page == null) {
     props.isNotFound = true;
-
     props.IsNotCreatable = !isCreatablePage(currentPathname);
-
     // check the page is forbidden or just does not exist.
     const count = isPermalink ? await Page.count({ _id: pageId }) : await Page.count({ path: currentPathname });
     props.isForbidden = count > 0;

+ 112 - 0
packages/app/src/pages/login.page.tsx

@@ -0,0 +1,112 @@
+import React from 'react';
+
+
+import { pagePathUtils } from '@growi/core';
+import {
+  NextPage, GetServerSideProps, GetServerSidePropsContext,
+} from 'next';
+import dynamic from 'next/dynamic';
+
+import { RawLayout } from '~/components/Layout/RawLayout';
+import { CrowiRequest } from '~/interfaces/crowi-request';
+
+import {
+  useCsrfToken,
+  useCurrentPathname,
+} from '../stores/context';
+
+
+import {
+  CommonProps, getServerSideCommonProps, useCustomTitle,
+} from './utils/commons';
+
+type Props = CommonProps & {
+
+  pageWithMetaStr: string,
+  isMailerSetup: boolean,
+  enabledStrategies: unknown,
+  registrationWhiteList: string[],
+};
+
+const LoginPage: NextPage<Props> = (props: Props) => {
+
+  // commons
+  useCsrfToken(props.csrfToken);
+
+  // page
+  useCurrentPathname(props.currentPathname);
+
+  const classNames: string[] = [];
+
+  const LoginForm = dynamic(() => import('~/components/LoginForm'), {
+    ssr: false,
+  });
+
+  return (
+    <>
+      <RawLayout title={useCustomTitle(props, 'GROWI')} className={classNames.join(' ')}>
+        <div className='nologin'>
+          <div id='wrapper'>
+            <div id="page-wrapper">
+              <LoginForm objOfIsExternalAuthEnableds={props.enabledStrategies} isLocalStrategySetup={true} isLdapStrategySetup={true}
+                isRegistrationEnabled={true} registrationWhiteList={props.registrationWhiteList} isPasswordResetEnabled={true} />
+            </div>
+          </div>
+        </div>
+      </RawLayout>
+    </>
+  );
+};
+
+function injectEnabledStrategies(context: GetServerSidePropsContext, props: Props): void {
+  const req: CrowiRequest = context.req as CrowiRequest;
+  const { crowi } = req;
+  const {
+    configManager,
+  } = crowi;
+
+  const enabledStrategies = {
+    google: configManager.getConfig('crowi', 'security:passport-google:isEnabled'),
+    github: configManager.getConfig('crowi', 'security:passport-github:isEnabled'),
+    facebook: false,
+    twitter: configManager.getConfig('crowi', 'security:passport-twitter:isEnabled'),
+    smal: configManager.getConfig('crowi', 'security:passport-saml:isEnabled'),
+    oidc: configManager.getConfig('crowi', 'security:passport-oidc:isEnabled'),
+    basic: configManager.getConfig('crowi', 'security:passport-basic:isEnabled'),
+  };
+
+  props.enabledStrategies = enabledStrategies;
+}
+
+async function injectServerConfigurations(context: GetServerSidePropsContext, props: Props): Promise<void> {
+  const req: CrowiRequest = context.req as CrowiRequest;
+  const { crowi } = req;
+  const {
+    mailService,
+    configManager,
+  } = crowi;
+
+  props.isMailerSetup = mailService.isMailerSetup;
+  props.registrationWhiteList = configManager.getConfig('crowi', 'security:registrationWhiteList');
+}
+
+export const getServerSideProps: GetServerSideProps = async(context: GetServerSidePropsContext) => {
+  const result = await getServerSideCommonProps(context);
+
+  // check for presence
+  // see: https://github.com/vercel/next.js/issues/19271#issuecomment-730006862
+  if (!('props' in result)) {
+    throw new Error('invalid getSSP result');
+  }
+
+  const props: Props = result.props as Props;
+
+  injectServerConfigurations(context, props);
+  injectEnabledStrategies(context, props);
+
+  return {
+    props,
+  };
+};
+
+export default LoginPage;

+ 1 - 1
packages/app/src/server/routes/index.js

@@ -79,7 +79,7 @@ module.exports = function(crowi, app) {
   app.get('/'                         , applicationInstalled, unavailableWhenMaintenanceMode, loginRequired, autoReconnectToSearch, next.delegateToNext);
 
   app.get('/login/error/:reason'      , applicationInstalled, login.error);
-  app.get('/login'                    , applicationInstalled, login.preLogin, login.login);
+  app.get('/login'                    , applicationInstalled, login.preLogin, next.delegateToNext);
   app.get('/login/invited'            , applicationInstalled, login.invited);
   app.post('/login/activateInvited'   , applicationInstalled, loginFormValidator.inviteRules(), loginFormValidator.inviteValidation, csrfProtection, login.invited);
   app.post('/login'                   , applicationInstalled, loginFormValidator.loginRules(), loginFormValidator.loginValidation, csrfProtection,  addActivity, loginPassport.loginWithLocal, loginPassport.loginWithLdap, loginPassport.loginFailure);

+ 12 - 12
packages/app/src/server/routes/login.js

@@ -67,18 +67,18 @@ module.exports = function(crowi, app) {
 
   actions.preLogin = function(req, res, next) {
     // user has already logged in
-    const { user } = req;
-    if (user != null && user.status === User.STATUS_ACTIVE) {
-      const { redirectTo } = req.session;
-      // remove session.redirectTo
-      delete req.session.redirectTo;
-      return res.safeRedirect(redirectTo);
-    }
-
-    // set referer to 'redirectTo'
-    if (req.session.redirectTo == null && req.headers.referer != null) {
-      req.session.redirectTo = req.headers.referer;
-    }
+    // const { user } = req;
+    // if (user != null && user.status === User.STATUS_ACTIVE) {
+    //   const { redirectTo } = req.session;
+    //   // remove session.redirectTo
+    //   delete req.session.redirectTo;
+    //   return res.safeRedirect(redirectTo);
+    // }
+
+    // // set referer to 'redirectTo'
+    // if (req.session.redirectTo == null && req.headers.referer != null) {
+    //   req.session.redirectTo = req.headers.referer;
+    // }
 
     next();
   };

+ 13 - 10
packages/app/src/styles/_login.scss

@@ -1,3 +1,6 @@
+@use '~/styles/bootstrap/init' as bs;
+
+
 .nologin {
   #page-wrapper {
     background: none;
@@ -92,40 +95,40 @@
 
   $btn-fill-colors: (
     'login': (
-      rgba($danger, 0.4),
+      rgba(bs.$danger, 0.4),
       rgba(#7e4153, 0.7),
     ),
     'register': (
-      rgba($success, 0.4),
+      rgba(bs.$success, 0.4),
       rgba(#3f7263, 0.7),
     ),
     'google': (
       rgba(#24292e, 0.4),
-      $gray-700,
+      bs.$gray-700,
     ),
     'github': (
       rgba(lighten(black, 20%), 0.4),
-      $gray-700,
+      bs.$gray-700,
     ),
     'facebook': (
       rgba(#29487d, 0.4),
-      $gray-700,
+      bs.$gray-700,
     ),
     'twitter': (
       rgba(#1da1f2, 0.4),
-      $gray-700,
+      bs.$gray-700,
     ),
     'oidc': (
       rgba(#24292e, 0.4),
-      $gray-700,
+      bs.$gray-700,
     ),
     'saml': (
       rgba(#55a79a, 0.4),
-      $gray-700,
+      bs.$gray-700,
     ),
     'basic': (
       rgba(#24292e, 0.4),
-      $gray-700,
+      bs.$gray-700,
     ),
   );
 
@@ -153,7 +156,7 @@
   }
 
   .link-switch {
-    color: $gray-200;
+    color: bs.$gray-200;
 
     &:hover {
       color: white;

+ 1 - 1
packages/app/src/styles/style-next.scss

@@ -46,7 +46,7 @@
 // @import 'page-content-footer';
 // @import 'handsontable';
 @import 'layout';
-// @import 'login';
+@import 'login';
 // @import 'me';
 // @import 'mirror_mode';
 // @import 'modal';