fix: Login workflow depending on smtp is configured (#3307)

This commit is contained in:
sriram veeraghanta 2024-01-04 16:27:17 +05:30
parent 7272c54439
commit 46e79dde27
7 changed files with 121 additions and 98 deletions

View File

@ -39,7 +39,6 @@ OPENAI_API_BASE="https://api.openai.com/v1" # deprecated
OPENAI_API_KEY="sk-" # deprecated OPENAI_API_KEY="sk-" # deprecated
GPT_ENGINE="gpt-3.5-turbo" # deprecated GPT_ENGINE="gpt-3.5-turbo" # deprecated
# Settings related to Docker # Settings related to Docker
DOCKERIZED=1 # deprecated DOCKERIZED=1 # deprecated

View File

@ -20,7 +20,6 @@ class ConfigurationEndpoint(BaseAPIView):
] ]
def get(self, request): def get(self, request):
# Get all the configuration # Get all the configuration
( (
GOOGLE_CLIENT_ID, GOOGLE_CLIENT_ID,
@ -90,8 +89,12 @@ class ConfigurationEndpoint(BaseAPIView):
data = {} data = {}
# Authentication # Authentication
data["google_client_id"] = GOOGLE_CLIENT_ID if GOOGLE_CLIENT_ID and GOOGLE_CLIENT_ID != "\"\"" else None data["google_client_id"] = (
data["github_client_id"] = GITHUB_CLIENT_ID if GITHUB_CLIENT_ID and GITHUB_CLIENT_ID != "\"\"" else None GOOGLE_CLIENT_ID if GOOGLE_CLIENT_ID and GOOGLE_CLIENT_ID != '""' else None
)
data["github_client_id"] = (
GITHUB_CLIENT_ID if GITHUB_CLIENT_ID and GITHUB_CLIENT_ID != '""' else None
)
data["github_app_name"] = GITHUB_APP_NAME data["github_app_name"] = GITHUB_APP_NAME
data["magic_login"] = ( data["magic_login"] = (
bool(EMAIL_HOST_USER) and bool(EMAIL_HOST_PASSWORD) bool(EMAIL_HOST_USER) and bool(EMAIL_HOST_PASSWORD)
@ -114,7 +117,9 @@ class ConfigurationEndpoint(BaseAPIView):
# File size settings # File size settings
data["file_size_limit"] = float(os.environ.get("FILE_SIZE_LIMIT", 5242880)) data["file_size_limit"] = float(os.environ.get("FILE_SIZE_LIMIT", 5242880))
# is self managed # is smtp configured
data["is_self_managed"] = bool(int(os.environ.get("IS_SELF_MANAGED", "1"))) data["is_smtp_configured"] = not (
bool(EMAIL_HOST_USER) and bool(EMAIL_HOST_PASSWORD)
)
return Response(data, status=status.HTTP_200_OK) return Response(data, status=status.HTTP_200_OK)

View File

@ -1,5 +1,3 @@
export interface IAppConfig { export interface IAppConfig {
email_password_login: boolean; email_password_login: boolean;
file_size_limit: number; file_size_limit: number;
@ -12,5 +10,5 @@ export interface IAppConfig {
posthog_host: string | null; posthog_host: string | null;
has_openai_configured: boolean; has_openai_configured: boolean;
has_unsplash_configured: boolean; has_unsplash_configured: boolean;
is_self_managed: boolean; is_smtp_configured: boolean;
} }

View File

@ -1,10 +1,12 @@
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import { Controller, useForm } from "react-hook-form"; import { Controller, useForm } from "react-hook-form";
import { XCircle } from "lucide-react"; import { XCircle } from "lucide-react";
import { observer } from "mobx-react-lite";
// services // services
import { AuthService } from "services/auth.service"; import { AuthService } from "services/auth.service";
// hooks // hooks
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
import { useApplication } from "hooks/store";
// ui // ui
import { Button, Input } from "@plane/ui"; import { Button, Input } from "@plane/ui";
// helpers // helpers
@ -25,11 +27,13 @@ type TEmailFormValues = {
const authService = new AuthService(); const authService = new AuthService();
export const EmailForm: React.FC<Props> = (props) => { export const EmailForm: React.FC<Props> = observer((props) => {
const { handleStepChange, updateEmail } = props; const { handleStepChange, updateEmail } = props;
// hooks
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
const {
config: { envConfig },
} = useApplication();
const { const {
control, control,
formState: { errors, isSubmitting, isValid }, formState: { errors, isSubmitting, isValid },
@ -54,9 +58,11 @@ export const EmailForm: React.FC<Props> = (props) => {
await authService await authService
.emailCheck(payload) .emailCheck(payload)
.then((res) => { .then((res) => {
// if the password has been autoset, send the user to magic sign-in // if the password has been auto set, send the user to magic sign-in
if (res.is_password_autoset) handleStepChange(ESignInSteps.UNIQUE_CODE); if (res.is_password_autoset && envConfig?.is_smtp_configured) {
// if the password has not been autoset, send them to password sign-in handleStepChange(ESignInSteps.UNIQUE_CODE);
}
// if the password has not been auto set, send them to password sign-in
else handleStepChange(ESignInSteps.PASSWORD); else handleStepChange(ESignInSteps.PASSWORD);
}) })
.catch((err) => .catch((err) =>
@ -119,4 +125,4 @@ export const EmailForm: React.FC<Props> = (props) => {
</form> </form>
</> </>
); );
}; });

View File

@ -6,6 +6,7 @@ import { XCircle } from "lucide-react";
import { AuthService } from "services/auth.service"; import { AuthService } from "services/auth.service";
// hooks // hooks
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
import { useApplication } from "hooks/store";
// ui // ui
import { Button, Input } from "@plane/ui"; import { Button, Input } from "@plane/ui";
// helpers // helpers
@ -14,12 +15,14 @@ import { checkEmailValidity } from "helpers/string.helper";
import { IPasswordSignInData } from "@plane/types"; import { IPasswordSignInData } from "@plane/types";
// constants // constants
import { ESignInSteps } from "components/account"; import { ESignInSteps } from "components/account";
import { observer } from "mobx-react-lite";
type Props = { type Props = {
email: string; email: string;
updateEmail: (email: string) => void; updateEmail: (email: string) => void;
handleStepChange: (step: ESignInSteps) => void; handleStepChange: (step: ESignInSteps) => void;
handleSignInRedirection: () => Promise<void>; handleSignInRedirection: () => Promise<void>;
handleEmailClear: () => void;
}; };
type TPasswordFormValues = { type TPasswordFormValues = {
@ -34,13 +37,16 @@ const defaultValues: TPasswordFormValues = {
const authService = new AuthService(); const authService = new AuthService();
export const PasswordForm: React.FC<Props> = (props) => { export const PasswordForm: React.FC<Props> = observer((props) => {
const { email, updateEmail, handleStepChange, handleSignInRedirection } = props; const { email, updateEmail, handleStepChange, handleSignInRedirection, handleEmailClear } = props;
// states // states
const [isSendingUniqueCode, setIsSendingUniqueCode] = useState(false); const [isSendingUniqueCode, setIsSendingUniqueCode] = useState(false);
const [isSendingResetPasswordLink, setIsSendingResetPasswordLink] = useState(false); const [isSendingResetPasswordLink, setIsSendingResetPasswordLink] = useState(false);
// toast alert // toast alert
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
const {
config: { envConfig },
} = useApplication();
// form info // form info
const { const {
control, control,
@ -157,11 +163,12 @@ export const PasswordForm: React.FC<Props> = (props) => {
hasError={Boolean(errors.email)} hasError={Boolean(errors.email)}
placeholder="orville.wright@frstflt.com" placeholder="orville.wright@frstflt.com"
className="h-[46px] w-full border border-onboarding-border-100 pr-12 placeholder:text-onboarding-text-400" className="h-[46px] w-full border border-onboarding-border-100 pr-12 placeholder:text-onboarding-text-400"
disabled
/> />
{value.length > 0 && ( {value.length > 0 && (
<XCircle <XCircle
className="absolute right-3 h-5 w-5 stroke-custom-text-400 hover:cursor-pointer" className="absolute right-3 h-5 w-5 stroke-custom-text-400 hover:cursor-pointer"
onClick={() => onChange("")} onClick={handleEmailClear}
/> />
)} )}
</div> </div>
@ -199,26 +206,28 @@ export const PasswordForm: React.FC<Props> = (props) => {
</button> </button>
</div> </div>
</div> </div>
<div className="grid gap-2.5 sm:grid-cols-2"> <div className="flex gap-4">
<Button {envConfig && envConfig.is_smtp_configured && (
type="button" <Button
onClick={handleSendUniqueCode} type="button"
variant="primary" onClick={handleSendUniqueCode}
className="w-full" variant="outline-primary"
size="xl" className="w-full"
loading={isSendingUniqueCode} size="xl"
> loading={isSendingUniqueCode}
{isSendingUniqueCode ? "Sending code" : "Use unique code"} >
</Button> {isSendingUniqueCode ? "Sending code" : "Use unique code"}
</Button>
)}
<Button <Button
type="submit" type="submit"
variant="outline-primary" variant="primary"
className="w-full" className="w-full"
size="xl" size="xl"
disabled={!isValid} disabled={!isValid}
loading={isSubmitting} loading={isSubmitting}
> >
Go to workspace Continue
</Button> </Button>
</div> </div>
<p className="text-xs text-onboarding-text-200"> <p className="text-xs text-onboarding-text-200">
@ -230,4 +239,4 @@ export const PasswordForm: React.FC<Props> = (props) => {
</form> </form>
</> </>
); );
}; });

View File

@ -13,7 +13,6 @@ import {
OAuthOptions, OAuthOptions,
OptionalSetPasswordForm, OptionalSetPasswordForm,
CreatePasswordForm, CreatePasswordForm,
SelfHostedSignInForm,
} from "components/account"; } from "components/account";
export enum ESignInSteps { export enum ESignInSteps {
@ -45,69 +44,73 @@ export const SignInRoot = observer(() => {
return ( return (
<> <>
<div className="mx-auto flex flex-col"> <div className="mx-auto flex flex-col">
{envConfig?.is_self_managed ? ( <>
<SelfHostedSignInForm {signInStep === ESignInSteps.EMAIL && (
email={email} <EmailForm
updateEmail={(newEmail) => setEmail(newEmail)} handleStepChange={(step) => setSignInStep(step)}
handleSignInRedirection={handleRedirection} updateEmail={(newEmail) => setEmail(newEmail)}
/> />
) : ( )}
<> {signInStep === ESignInSteps.PASSWORD && (
{signInStep === ESignInSteps.EMAIL && ( <PasswordForm
<EmailForm email={email}
handleStepChange={(step) => setSignInStep(step)} updateEmail={(newEmail) => setEmail(newEmail)}
updateEmail={(newEmail) => setEmail(newEmail)} handleStepChange={(step) => setSignInStep(step)}
/> handleEmailClear={() => {
)} setEmail("");
{signInStep === ESignInSteps.PASSWORD && ( setSignInStep(ESignInSteps.EMAIL);
<PasswordForm }}
email={email} handleSignInRedirection={handleRedirection}
updateEmail={(newEmail) => setEmail(newEmail)} />
handleStepChange={(step) => setSignInStep(step)} )}
handleSignInRedirection={handleRedirection} {signInStep === ESignInSteps.SET_PASSWORD_LINK && (
/> <SetPasswordLink email={email} updateEmail={(newEmail) => setEmail(newEmail)} />
)} )}
{signInStep === ESignInSteps.SET_PASSWORD_LINK && ( {signInStep === ESignInSteps.USE_UNIQUE_CODE_FROM_PASSWORD && (
<SetPasswordLink email={email} updateEmail={(newEmail) => setEmail(newEmail)} /> <UniqueCodeForm
)} email={email}
{signInStep === ESignInSteps.USE_UNIQUE_CODE_FROM_PASSWORD && ( updateEmail={(newEmail) => setEmail(newEmail)}
<UniqueCodeForm handleStepChange={(step) => setSignInStep(step)}
email={email} handleSignInRedirection={handleRedirection}
updateEmail={(newEmail) => setEmail(newEmail)} submitButtonLabel="Go to workspace"
handleStepChange={(step) => setSignInStep(step)} showTermsAndConditions
handleSignInRedirection={handleRedirection} updateUserOnboardingStatus={(value) => setIsOnboarded(value)}
submitButtonLabel="Go to workspace" handleEmailClear={() => {
showTermsAndConditions setEmail("");
updateUserOnboardingStatus={(value) => setIsOnboarded(value)} setSignInStep(ESignInSteps.EMAIL);
/> }}
)} />
{signInStep === ESignInSteps.UNIQUE_CODE && ( )}
<UniqueCodeForm {signInStep === ESignInSteps.UNIQUE_CODE && (
email={email} <UniqueCodeForm
updateEmail={(newEmail) => setEmail(newEmail)} email={email}
handleStepChange={(step) => setSignInStep(step)} updateEmail={(newEmail) => setEmail(newEmail)}
handleSignInRedirection={handleRedirection} handleStepChange={(step) => setSignInStep(step)}
updateUserOnboardingStatus={(value) => setIsOnboarded(value)} handleSignInRedirection={handleRedirection}
/> updateUserOnboardingStatus={(value) => setIsOnboarded(value)}
)} handleEmailClear={() => {
{signInStep === ESignInSteps.OPTIONAL_SET_PASSWORD && ( setEmail("");
<OptionalSetPasswordForm setSignInStep(ESignInSteps.EMAIL);
email={email} }}
handleStepChange={(step) => setSignInStep(step)} />
handleSignInRedirection={handleRedirection} )}
isOnboarded={isOnboarded} {signInStep === ESignInSteps.OPTIONAL_SET_PASSWORD && (
/> <OptionalSetPasswordForm
)} email={email}
{signInStep === ESignInSteps.CREATE_PASSWORD && ( handleStepChange={(step) => setSignInStep(step)}
<CreatePasswordForm handleSignInRedirection={handleRedirection}
email={email} isOnboarded={isOnboarded}
handleStepChange={(step) => setSignInStep(step)} />
handleSignInRedirection={handleRedirection} )}
isOnboarded={isOnboarded} {signInStep === ESignInSteps.CREATE_PASSWORD && (
/> <CreatePasswordForm
)} email={email}
</> handleStepChange={(step) => setSignInStep(step)}
)} handleSignInRedirection={handleRedirection}
isOnboarded={isOnboarded}
/>
)}
</>
</div> </div>
{isOAuthEnabled && !OAUTH_HIDDEN_STEPS.includes(signInStep) && ( {isOAuthEnabled && !OAUTH_HIDDEN_STEPS.includes(signInStep) && (
<OAuthOptions handleSignInRedirection={handleRedirection} /> <OAuthOptions handleSignInRedirection={handleRedirection} />

View File

@ -25,6 +25,7 @@ type Props = {
submitButtonLabel?: string; submitButtonLabel?: string;
showTermsAndConditions?: boolean; showTermsAndConditions?: boolean;
updateUserOnboardingStatus: (value: boolean) => void; updateUserOnboardingStatus: (value: boolean) => void;
handleEmailClear: () => void;
}; };
type TUniqueCodeFormValues = { type TUniqueCodeFormValues = {
@ -50,6 +51,7 @@ export const UniqueCodeForm: React.FC<Props> = (props) => {
submitButtonLabel = "Continue", submitButtonLabel = "Continue",
showTermsAndConditions = false, showTermsAndConditions = false,
updateUserOnboardingStatus, updateUserOnboardingStatus,
handleEmailClear,
} = props; } = props;
// states // states
const [isRequestingNewCode, setIsRequestingNewCode] = useState(false); const [isRequestingNewCode, setIsRequestingNewCode] = useState(false);
@ -183,11 +185,12 @@ export const UniqueCodeForm: React.FC<Props> = (props) => {
hasError={Boolean(errors.email)} hasError={Boolean(errors.email)}
placeholder="orville.wright@frstflt.com" placeholder="orville.wright@frstflt.com"
className="h-[46px] w-full border border-onboarding-border-100 pr-12 placeholder:text-onboarding-text-400" className="h-[46px] w-full border border-onboarding-border-100 pr-12 placeholder:text-onboarding-text-400"
disabled
/> />
{value.length > 0 && ( {value.length > 0 && (
<XCircle <XCircle
className="absolute right-3 h-5 w-5 stroke-custom-text-400 hover:cursor-pointer" className="absolute right-3 h-5 w-5 stroke-custom-text-400 hover:cursor-pointer"
onClick={() => onChange("")} onClick={handleEmailClear}
/> />
)} )}
</div> </div>