Skip to content

Commit

Permalink
feat: add captcha (#2613)
Browse files Browse the repository at this point in the history
* feat: captcha

* add borderRadius

* code perf
  • Loading branch information
Patrickill authored Sep 5, 2024
1 parent d6233cd commit 3bcc343
Show file tree
Hide file tree
Showing 9 changed files with 215 additions and 93 deletions.
6 changes: 4 additions & 2 deletions packages/global/support/user/auth/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ export enum UserAuthTypeEnum {
register = 'register',
findPassword = 'findPassword',
wxLogin = 'wxLogin',
bindNotification = 'bindNotification'
bindNotification = 'bindNotification',
captcha = 'captcha'
}

export const userAuthTypeMap = {
[UserAuthTypeEnum.register]: 'register',
[UserAuthTypeEnum.findPassword]: 'findPassword',
[UserAuthTypeEnum.wxLogin]: 'wxLogin',
[UserAuthTypeEnum.bindNotification]: 'bindNotification'
[UserAuthTypeEnum.bindNotification]: 'bindNotification',
[UserAuthTypeEnum.captcha]: 'captcha'
};
4 changes: 2 additions & 2 deletions packages/web/i18n/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -647,8 +647,7 @@
"success": "Start syncing"
}
},
"training": {
}
"training": {}
},
"data": {
"Auxiliary Data": "Auxiliary data",
Expand Down Expand Up @@ -1257,6 +1256,7 @@
"auth": {
"Sending Code": "Sending"
},
"captcha_placeholder": "Please enter the verification code",
"inform": {
"System message": "System message"
},
Expand Down
4 changes: 2 additions & 2 deletions packages/web/i18n/zh/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -647,8 +647,7 @@
"success": "开始同步"
}
},
"training": {
}
"training": {}
},
"data": {
"Auxiliary Data": "辅助数据",
Expand Down Expand Up @@ -1248,6 +1247,7 @@
},
"user": {
"Avatar": "头像",
"captcha_placeholder": "请输入验证码",
"Go laf env": "点击前往 {{env}} 获取 PAT 凭证。",
"Laf account course": "查看绑定 laf 账号教程。",
"Laf account intro": "绑定你的 laf 账号后,你将可以在工作流中使用 laf 模块,实现在线编写代码。",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { getCaptchaPic } from '@/web/support/user/api';
import { useSendCode } from '@/web/support/user/hooks/useSendCode';
import { Box, Button, Input, Image, ModalBody, ModalFooter } from '@chakra-ui/react';
import { UserAuthTypeEnum } from '@fastgpt/global/support/user/auth/constants';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { useTranslation } from 'next-i18next';
import { useState } from 'react';

const SendCodeAuthModal = ({
username,
type,
onClose
}: {
username: string;
type: UserAuthTypeEnum;
onClose: () => void;
}) => {
const { t } = useTranslation();
const [captchaInput, setCaptchaInput] = useState('');
const { codeSending, sendCode } = useSendCode();
const {
data,
loading,
runAsync: getCaptcha
} = useRequest2(() => getCaptchaPic(username), { manual: false });
return (
<MyModal isOpen={true} isLoading={loading}>
<ModalBody pt={8}>
<Image
borderRadius={'md'}
w={'100%'}
h={'200px'}
_hover={{ cursor: 'pointer' }}
mb={8}
onClick={getCaptcha}
src={data?.captchaImage}
alt="captcha"
/>
<Input
placeholder={t('common:support.user.captcha_placeholder')}
value={captchaInput}
onChange={(e) => setCaptchaInput(e.target.value)}
/>
</ModalBody>
<ModalFooter gap={2}>
<Button isLoading={codeSending} variant={'whiteBase'} onClick={onClose}>
{t('common:common.Cancel')}
</Button>
<Button
isLoading={codeSending}
onClick={async () => {
await sendCode({ username, type, captcha: captchaInput });
onClose();
}}
>
{t('common:common.Confirm')}
</Button>
</ModalFooter>
</MyModal>
);
};

export default SendCodeAuthModal;
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import React, { useCallback } from 'react';
import { ModalBody, Box, Flex, Input, ModalFooter, Button, HStack } from '@chakra-ui/react';
import {
ModalBody,
Box,
Flex,
Input,
ModalFooter,
Button,
HStack,
useDisclosure
} from '@chakra-ui/react';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { useTranslation } from 'next-i18next';
import { useForm } from 'react-hook-form';
Expand All @@ -9,6 +18,8 @@ import Icon from '@fastgpt/web/components/common/Icon';
import { useSendCode } from '@/web/support/user/hooks/useSendCode';
import { useUserStore } from '@/web/support/user/useUserStore';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import SendCodeAuthModal from '@/components/support/user/safe/SendCodeAuthModal';
import { UserAuthTypeEnum } from '@fastgpt/global/support/user/auth/constants';

type FormType = {
account: string;
Expand All @@ -27,7 +38,11 @@ const UpdateNotificationModal = ({ onClose }: { onClose: () => void }) => {
});
const account = watch('account');
const verifyCode = watch('verifyCode');

const {
isOpen: openCodeAuthModal,
onOpen: onOpenCodeAuthModal,
onClose: onCloseCodeAuthModal
} = useDisclosure();
const { runAsync: onSubmit, loading: isLoading } = useRequest2(
(data: FormType) => {
return updateNotificationAccount(data);
Expand All @@ -42,16 +57,13 @@ const UpdateNotificationModal = ({ onClose }: { onClose: () => void }) => {
}
);

const { sendCodeText, sendCode, codeCountDown } = useSendCode();
const { sendCodeText, codeCountDown } = useSendCode();

const onclickSendCode = useCallback(async () => {
const check = await trigger('account');
if (!check) return;
sendCode({
username: getValues('account'),
type: 'bindNotification'
});
}, [getValues, sendCode, trigger]);
onOpenCodeAuthModal();
}, [onOpenCodeAuthModal, trigger]);

const placeholder = feConfigs?.bind_notification_method
?.map((item) => {
Expand All @@ -65,68 +77,77 @@ const UpdateNotificationModal = ({ onClose }: { onClose: () => void }) => {
.join('/');

return (
<MyModal
isOpen
iconSrc="common/settingLight"
w={'32rem'}
title={t('common:user.Notification Receive')}
>
<ModalBody px={10}>
<Flex flexDirection="column">
<HStack px="6" py="3" color="primary.600" bgColor="primary.50" borderRadius="md">
<Icon name="common/info" w="1rem" />
<Box fontSize={'sm'}>{t('user:notification.Bind Notification Pipe Hint')}</Box>
</HStack>
<Flex mt="4" alignItems="center">
<Box flex={'0 0 70px'}>{t('common:user.Account')}</Box>
<Input
flex={1}
bg={'myGray.50'}
{...register('account', { required: true })}
placeholder={placeholder}
></Input>
</Flex>
<Flex mt="6" alignItems="center" position={'relative'}>
<Box flex={'0 0 70px'}>{t('user:password.verification_code')}</Box>
<Input
flex={1}
bg={'myGray.50'}
{...register('verifyCode', { required: true })}
placeholder={t('user:password.code_required')}
></Input>
<Box
position={'absolute'}
right={2}
zIndex={1}
fontSize={'sm'}
{...(codeCountDown > 0
? {
color: 'myGray.500'
}
: {
color: 'primary.700',
cursor: 'pointer',
onClick: onclickSendCode
})}
>
{sendCodeText}
</Box>
<>
<MyModal
isOpen
iconSrc="common/settingLight"
w={'32rem'}
title={t('common:user.Notification Receive')}
>
<ModalBody px={10}>
<Flex flexDirection="column">
<HStack px="6" py="3" color="primary.600" bgColor="primary.50" borderRadius="md">
<Icon name="common/info" w="1rem" />
<Box fontSize={'sm'}>{t('user:notification.Bind Notification Pipe Hint')}</Box>
</HStack>
<Flex mt="4" alignItems="center">
<Box flex={'0 0 70px'}>{t('common:user.Account')}</Box>
<Input
flex={1}
bg={'myGray.50'}
{...register('account', { required: true })}
placeholder={placeholder}
></Input>
</Flex>
<Flex mt="6" alignItems="center" position={'relative'}>
<Box flex={'0 0 70px'}>{t('user:password.verification_code')}</Box>
<Input
flex={1}
bg={'myGray.50'}
{...register('verifyCode', { required: true })}
placeholder={t('user:password.code_required')}
></Input>
<Box
position={'absolute'}
right={2}
zIndex={1}
fontSize={'sm'}
{...(codeCountDown > 0
? {
color: 'myGray.500'
}
: {
color: 'primary.700',
cursor: 'pointer',
onClick: onclickSendCode
})}
>
{sendCodeText}
</Box>
</Flex>
</Flex>
</Flex>
</ModalBody>
<ModalFooter>
<Button mr={3} variant={'whiteBase'} onClick={onClose}>
{t('common:common.Cancel')}
</Button>
<Button
isLoading={isLoading}
isDisabled={!account || !verifyCode}
onClick={handleSubmit((data) => onSubmit(data))}
>
{t('common:common.Confirm')}
</Button>
</ModalFooter>
</MyModal>
</ModalBody>
<ModalFooter>
<Button mr={3} variant={'whiteBase'} onClick={onClose}>
{t('common:common.Cancel')}
</Button>
<Button
isLoading={isLoading}
isDisabled={!account || !verifyCode}
onClick={handleSubmit((data) => onSubmit(data))}
>
{t('common:common.Confirm')}
</Button>
</ModalFooter>
</MyModal>
{openCodeAuthModal && (
<SendCodeAuthModal
onClose={onCloseCodeAuthModal}
username={getValues('account')}
type={UserAuthTypeEnum.bindNotification}
/>
)}
</>
);
};

Expand Down
26 changes: 18 additions & 8 deletions projects/app/src/pages/login/components/ForgetPasswordForm.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useState, Dispatch, useCallback } from 'react';
import { FormControl, Box, Input, Button } from '@chakra-ui/react';
import { FormControl, Box, Input, Button, useDisclosure } from '@chakra-ui/react';
import { useForm } from 'react-hook-form';
import { LoginPageTypeEnum } from '@/web/support/user/login/constants';
import { postFindPassword } from '@/web/support/user/api';
Expand All @@ -8,6 +8,8 @@ import type { ResLogin } from '@/global/support/api/userRes.d';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useTranslation } from 'next-i18next';
import { UserAuthTypeEnum } from '@fastgpt/global/support/user/auth/constants';
import SendCodeAuthModal from '@/components/support/user/safe/SendCodeAuthModal';
interface Props {
setPageType: Dispatch<`${LoginPageTypeEnum}`>;
loginSuccess: (e: ResLogin) => void;
Expand All @@ -34,16 +36,17 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
mode: 'onBlur'
});

const { codeSending, sendCodeText, sendCode, codeCountDown } = useSendCode();

const { sendCodeText, codeCountDown } = useSendCode();
const {
isOpen: openCodeAuthModal,
onOpen: onOpenCodeAuthModal,
onClose: onCloseCodeAuthModal
} = useDisclosure();
const onclickSendCode = useCallback(async () => {
const check = await trigger('username');
if (!check) return;
sendCode({
username: getValues('username'),
type: 'findPassword'
});
}, [getValues, sendCode, trigger]);
onOpenCodeAuthModal();
}, [onOpenCodeAuthModal, trigger]);

const [requesting, setRequesting] = useState(false);
const placeholder = feConfigs?.find_password_method
Expand Down Expand Up @@ -200,6 +203,13 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
{t('user:password.to_login')}
</Box>
</Box>
{openCodeAuthModal && (
<SendCodeAuthModal
onClose={onCloseCodeAuthModal}
username={getValues('username')}
type={UserAuthTypeEnum.findPassword}
/>
)}
</>
);
};
Expand Down
Loading

0 comments on commit 3bcc343

Please sign in to comment.