mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
fix: Login workflow depending on smtp is configured (#3307)
This commit is contained in:
parent
7272c54439
commit
46e79dde27
@ -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
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
4
packages/types/src/app.d.ts
vendored
4
packages/types/src/app.d.ts
vendored
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
@ -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>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
@ -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} />
|
||||||
|
@ -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>
|
||||||
|
Loading…
Reference in New Issue
Block a user