diff --git a/client/packages/lowcoder/src/api/apiUtils.ts b/client/packages/lowcoder/src/api/apiUtils.ts index 8396e3501..3b8e4e434 100644 --- a/client/packages/lowcoder/src/api/apiUtils.ts +++ b/client/packages/lowcoder/src/api/apiUtils.ts @@ -122,7 +122,14 @@ export const apiFailureResponseInterceptor = (error: any) => { if (!notAuthRequiredPath(error.config?.url)) { if (error.response.status === API_STATUS_CODES.REQUEST_NOT_AUTHORISED) { // get x-org-id from failed request - const organizationId = error.response.headers['x-org-id'] || undefined; + let organizationId; + if (error.response.headers['x-org-id']) { + organizationId = error.response.headers['x-org-id']; + } + if (localStorage.getItem('lowcoder_login_orgId')) { + organizationId = localStorage.getItem('lowcoder_login_orgId'); + localStorage.removeItem('lowcoder_login_orgId'); + } // Redirect to login and set a redirect url. StoreRegistry.getStore().dispatch( logoutAction({ diff --git a/client/packages/lowcoder/src/i18n/locales/en.ts b/client/packages/lowcoder/src/i18n/locales/en.ts index 735b797d4..ce47ff709 100644 --- a/client/packages/lowcoder/src/i18n/locales/en.ts +++ b/client/packages/lowcoder/src/i18n/locales/en.ts @@ -3205,6 +3205,7 @@ export const en = { "enterPassword": "Enter your password", "selectAuthProvider": "Select Authentication Provider", "selectWorkspace": "Select your workspace", + "userNotFound": "User not found. Please make sure you entered the correct email." }, "preLoad": { "jsLibraryHelpText": "Add JavaScript Libraries to Your Current Application via URL Addresses. lodash, day.js, uuid, numbro are Built into the System for Immediate Use. JavaScript Libraries are Loaded Before the Application is Initialized, Which Can Have an Impact on Application Performance.", diff --git a/client/packages/lowcoder/src/pages/common/profileDropdown.tsx b/client/packages/lowcoder/src/pages/common/profileDropdown.tsx index 992227bd1..d3f8b2a6d 100644 --- a/client/packages/lowcoder/src/pages/common/profileDropdown.tsx +++ b/client/packages/lowcoder/src/pages/common/profileDropdown.tsx @@ -150,7 +150,13 @@ export default function ProfileDropdown(props: DropDownProps) { dispatch(profileSettingModalVisible(true)); } else if (e.key === "logout") { // logout - dispatch(logoutAction({})); + const organizationId = localStorage.getItem('lowcoder_login_orgId'); + if (organizationId) { + localStorage.removeItem('lowcoder_login_orgId'); + } + dispatch(logoutAction({ + organizationId: organizationId || undefined, + })); } else if (e.keyPath.includes("switchOrg")) { if (e.key === "newOrganization") { // create new organization diff --git a/client/packages/lowcoder/src/pages/userAuth/formLoginSteps.tsx b/client/packages/lowcoder/src/pages/userAuth/formLoginSteps.tsx index aced446bd..1e036f3a3 100644 --- a/client/packages/lowcoder/src/pages/userAuth/formLoginSteps.tsx +++ b/client/packages/lowcoder/src/pages/userAuth/formLoginSteps.tsx @@ -4,7 +4,7 @@ import { ConfirmButton, StyledRouteLink, } from "pages/userAuth/authComponents"; -import React, { useContext, useEffect, useState } from "react"; +import React, { useContext, useEffect, useMemo, useState } from "react"; import styled from "styled-components"; import UserApi from "api/userApi"; import { useRedirectUrl } from "util/hooks"; @@ -19,7 +19,7 @@ import { Divider } from "antd"; import Flex from "antd/es/flex"; import { validateResponse } from "@lowcoder-ee/api/apiUtils"; import OrgApi from "@lowcoder-ee/api/orgApi"; -import { AccountLoginWrapper } from "./formLoginAdmin"; +import FormLogin, { AccountLoginWrapper } from "./formLoginAdmin"; import { default as Button } from "antd/es/button"; import LeftOutlined from "@ant-design/icons/LeftOutlined"; import { fetchConfigAction } from "@lowcoder-ee/redux/reduxActions/configActions"; @@ -28,6 +28,9 @@ import history from "util/history"; import { getServerSettings } from "@lowcoder-ee/redux/selectors/applicationSelector"; import {fetchOrgPaginationByEmail} from "@lowcoder-ee/util/pagination/axios"; import PaginationComp from "@lowcoder-ee/util/pagination/Pagination"; +import { getSystemConfigFetching } from "@lowcoder-ee/redux/selectors/configSelectors"; +import Spin from "antd/es/spin"; +import LoadingOutlined from "@ant-design/icons/LoadingOutlined"; const StyledCard = styled.div<{$selected: boolean}>` display: flex; @@ -107,18 +110,28 @@ export default function FormLoginSteps(props: FormLoginProps) { const { systemConfig, inviteInfo, fetchUserAfterAuthSuccess } = useContext(AuthContext); const invitationId = inviteInfo?.invitationId; const authId = systemConfig?.form.id; - const isFormLoginEnabled = systemConfig?.form.enableLogin; + const isFormLoginEnabled = systemConfig?.form.enableLogin; // check from configs const [orgLoading, setOrgLoading] = useState(false); const [orgList, setOrgList] = useState([]); const [currentStep, setCurrentStep] = useState(CurrentStepEnum.EMAIL); const [organizationId, setOrganizationId] = useState(props.organizationId); const [skipWorkspaceStep, setSkipWorkspaceStep] = useState(false); const [signupEnabled, setSignupEnabled] = useState(true); + const [signinEnabled, setSigninEnabled] = useState(true); // check from server settings const serverSettings = useSelector(getServerSettings); + const isFetchingConfig = useSelector(getSystemConfigFetching); const [elements, setElements] = useState({ elements: [], total: 0 }); const [currentPage, setCurrentPage] = useState(1); const [pageSize, setPageSize] = useState(10); + const isEmailLoginEnabled = useMemo(() => { + return isFormLoginEnabled && signinEnabled; + }, [isFormLoginEnabled, signinEnabled]); + + const isEnterpriseMode = useMemo(() => { + return serverSettings?.LOWCODER_WORKSPACE_MODE === "ENTERPRISE" || serverSettings?.LOWCODER_WORKSPACE_MODE === "SINGLEWORKSPACE"; + }, [serverSettings]); + useEffect(() => { if (account) fetchOrgPaginationByEmail({ @@ -133,13 +146,22 @@ export default function FormLoginSteps(props: FormLoginProps) { }, [pageSize, currentPage]) useEffect(() => { - const { LOWCODER_EMAIL_SIGNUP_ENABLED } = serverSettings; - if (!LOWCODER_EMAIL_SIGNUP_ENABLED) { - return setSignupEnabled(true); - } + const { + LOWCODER_EMAIL_SIGNUP_ENABLED, + LOWCODER_EMAIL_AUTH_ENABLED, + } = serverSettings; + setSignupEnabled(LOWCODER_EMAIL_SIGNUP_ENABLED === 'true'); + setSigninEnabled(LOWCODER_EMAIL_AUTH_ENABLED === 'true'); }, [serverSettings]); + const afterLoginSuccess = () => { + if (props.organizationId) { + localStorage.setItem("lowcoder_login_orgId", props.organizationId); + } + fetchUserAfterAuthSuccess?.(); + } + const { onSubmit, loading } = useAuthSubmit( () => UserApi.formLogin({ @@ -153,7 +175,7 @@ export default function FormLoginSteps(props: FormLoginProps) { }), false, redirectUrl, - fetchUserAfterAuthSuccess, + afterLoginSuccess, ); const fetchOrgsByEmail = () => { @@ -167,8 +189,9 @@ export default function FormLoginSteps(props: FormLoginProps) { } setOrgLoading(true); + // for enterprise mode, we will not ask for email in first step fetchOrgPaginationByEmail({ - email: account, + email: isEnterpriseMode ? ' ' : account, pageNum: currentPage, pageSize: pageSize }) @@ -177,15 +200,13 @@ export default function FormLoginSteps(props: FormLoginProps) { setElements({elements: resp.data || [], total: resp.total || 1}) setOrgList(resp.data); if (!resp.data.length) { - history.push( - AUTH_REGISTER_URL, - {...location.state || {}, email: account}, - ) - return; + throw new Error(trans("userAuth.userNotFound")); } if (resp.data.length === 1) { - setOrganizationId(resp.data[0].orgId); - dispatch(fetchConfigAction(resp.data[0].orgId)); + // in Enterprise mode, we will get org data in different format + const selectedOrgId = isEnterpriseMode ? resp.data[0].id : resp.data[0].orgId; + setOrganizationId(selectedOrgId); + dispatch(fetchConfigAction(selectedOrgId)); setCurrentStep(CurrentStepEnum.AUTH_PROVIDERS); return; } @@ -202,6 +223,39 @@ export default function FormLoginSteps(props: FormLoginProps) { }); } + useEffect(() => { + if (isEnterpriseMode) { + // dispatch(fetchConfigAction()); + fetchOrgsByEmail(); + } + }, [isEnterpriseMode]); + + if (isEnterpriseMode) { + return ( + } spinning={isFetchingConfig}> + { isEmailLoginEnabled && } + + {signupEnabled && ( + <> + + + + {trans("userAuth.register")} + + + + )} + + ); + } + if(currentStep === CurrentStepEnum.EMAIL) { return ( <> @@ -227,8 +281,8 @@ export default function FormLoginSteps(props: FormLoginProps) { {trans("userAuth.register")} @@ -280,10 +334,10 @@ export default function FormLoginSteps(props: FormLoginProps) { }} /> - {isFormLoginEnabled && ( + {isEmailLoginEnabled && ( <> )} - {isFormLoginEnabled && signupEnabled && ( + {isEmailLoginEnabled && signupEnabled && ( <> diff --git a/client/packages/lowcoder/src/pages/userAuth/register.tsx b/client/packages/lowcoder/src/pages/userAuth/register.tsx index 62bd7f7c2..41ba0115e 100644 --- a/client/packages/lowcoder/src/pages/userAuth/register.tsx +++ b/client/packages/lowcoder/src/pages/userAuth/register.tsx @@ -63,12 +63,16 @@ function UserRegister() { return inviteInfo?.invitedOrganizationId; } return orgId; - }, [ inviteInfo, orgId ]) + }, [ inviteInfo, orgId ]); const authId = systemConfig?.form.id; const serverSettings = useSelector(getServerSettings); + const isEnterpriseMode = useMemo(() => { + return serverSettings?.LOWCODER_WORKSPACE_MODE === "ENTERPRISE" || serverSettings?.LOWCODER_WORKSPACE_MODE === "SINGLEWORKSPACE"; + }, [serverSettings]); + useEffect(() => { const { LOWCODER_EMAIL_SIGNUP_ENABLED } = serverSettings; if( @@ -82,6 +86,13 @@ function UserRegister() { }; }, [serverSettings]); + const afterLoginSuccess = () => { + if (organizationId) { + localStorage.setItem("lowcoder_login_orgId", organizationId); + } + fetchUserAfterAuthSuccess?.(); + } + const { loading, onSubmit } = useAuthSubmit( () => UserApi.formLogin({ @@ -95,7 +106,7 @@ function UserRegister() { }), false, redirectUrl, - fetchUserAfterAuthSuccess, + afterLoginSuccess, ); const checkEmailExist = () => { @@ -160,7 +171,7 @@ function UserRegister() { {trans("userAuth.register")} setSubmitBtnDisable(!e.target.checked)} /> - {organizationId && ( + {(organizationId || isEnterpriseMode) && ( { + return isFormLoginEnabled && serverSettings.LOWCODER_EMAIL_AUTH_ENABLED === 'true'; + }, [isFormLoginEnabled, serverSettings]); + + const isEmailSignupEnabled = useMemo(() => { + return serverSettings.LOWCODER_EMAIL_SIGNUP_ENABLED === 'true'; + }, [serverSettings]); if (systemConfigFetching) { return } />; @@ -140,7 +150,10 @@ export function ThirdPartyAuth(props: { }); return ( - { isFormLoginEnabled && Boolean(socialLoginButtons.length) && ( + { ( + (isEmailLoginEnabled && props.authGoal === 'login') + || (isEmailSignupEnabled && props.authGoal === 'register') + ) && Boolean(socialLoginButtons.length) && ( or