From 3bcc3430fb8d59e3c857c8ec58f75b88f37571cd Mon Sep 17 00:00:00 2001 From: papapatrick <109422393+Patrickill@users.noreply.github.com> Date: Thu, 5 Sep 2024 11:46:51 +0800 Subject: [PATCH] feat: add captcha (#2613) * feat: captcha * add borderRadius * code perf --- .../global/support/user/auth/constants.ts | 6 +- packages/web/i18n/en/common.json | 4 +- packages/web/i18n/zh/common.json | 4 +- .../support/user/safe/SendCodeAuthModal.tsx | 64 +++++++ .../Info/UpdateNotificationModal.tsx | 159 ++++++++++-------- .../login/components/ForgetPasswordForm.tsx | 26 ++- .../pages/login/components/RegisterForm.tsx | 27 ++- projects/app/src/web/support/user/api.ts | 6 + .../src/web/support/user/hooks/useSendCode.ts | 12 +- 9 files changed, 215 insertions(+), 93 deletions(-) create mode 100644 projects/app/src/components/support/user/safe/SendCodeAuthModal.tsx diff --git a/packages/global/support/user/auth/constants.ts b/packages/global/support/user/auth/constants.ts index 860ade109..74812b519 100644 --- a/packages/global/support/user/auth/constants.ts +++ b/packages/global/support/user/auth/constants.ts @@ -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' }; diff --git a/packages/web/i18n/en/common.json b/packages/web/i18n/en/common.json index 5c0ce8252..6e67b09e3 100644 --- a/packages/web/i18n/en/common.json +++ b/packages/web/i18n/en/common.json @@ -647,8 +647,7 @@ "success": "Start syncing" } }, - "training": { - } + "training": {} }, "data": { "Auxiliary Data": "Auxiliary data", @@ -1257,6 +1256,7 @@ "auth": { "Sending Code": "Sending" }, + "captcha_placeholder": "Please enter the verification code", "inform": { "System message": "System message" }, diff --git a/packages/web/i18n/zh/common.json b/packages/web/i18n/zh/common.json index 16c679215..b3b1d28e6 100644 --- a/packages/web/i18n/zh/common.json +++ b/packages/web/i18n/zh/common.json @@ -647,8 +647,7 @@ "success": "开始同步" } }, - "training": { - } + "training": {} }, "data": { "Auxiliary Data": "辅助数据", @@ -1248,6 +1247,7 @@ }, "user": { "Avatar": "头像", + "captcha_placeholder": "请输入验证码", "Go laf env": "点击前往 {{env}} 获取 PAT 凭证。", "Laf account course": "查看绑定 laf 账号教程。", "Laf account intro": "绑定你的 laf 账号后,你将可以在工作流中使用 laf 模块,实现在线编写代码。", diff --git a/projects/app/src/components/support/user/safe/SendCodeAuthModal.tsx b/projects/app/src/components/support/user/safe/SendCodeAuthModal.tsx new file mode 100644 index 000000000..be27aae3f --- /dev/null +++ b/projects/app/src/components/support/user/safe/SendCodeAuthModal.tsx @@ -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 ( + + + captcha + setCaptchaInput(e.target.value)} + /> + + + + + + + ); +}; + +export default SendCodeAuthModal; diff --git a/projects/app/src/pages/account/components/Info/UpdateNotificationModal.tsx b/projects/app/src/pages/account/components/Info/UpdateNotificationModal.tsx index 317ddb79c..28588dcbc 100644 --- a/projects/app/src/pages/account/components/Info/UpdateNotificationModal.tsx +++ b/projects/app/src/pages/account/components/Info/UpdateNotificationModal.tsx @@ -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'; @@ -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; @@ -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); @@ -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) => { @@ -65,68 +77,77 @@ const UpdateNotificationModal = ({ onClose }: { onClose: () => void }) => { .join('/'); return ( - - - - - - {t('user:notification.Bind Notification Pipe Hint')} - - - {t('common:user.Account')} - + <> + + + + + + {t('user:notification.Bind Notification Pipe Hint')} + + + {t('common:user.Account')} + + + + {t('user:password.verification_code')} + + 0 + ? { + color: 'myGray.500' + } + : { + color: 'primary.700', + cursor: 'pointer', + onClick: onclickSendCode + })} + > + {sendCodeText} + + - - {t('user:password.verification_code')} - - 0 - ? { - color: 'myGray.500' - } - : { - color: 'primary.700', - cursor: 'pointer', - onClick: onclickSendCode - })} - > - {sendCodeText} - - - - - - - - - + + + + + + + {openCodeAuthModal && ( + + )} + ); }; diff --git a/projects/app/src/pages/login/components/ForgetPasswordForm.tsx b/projects/app/src/pages/login/components/ForgetPasswordForm.tsx index ecf22923c..2185d09c1 100644 --- a/projects/app/src/pages/login/components/ForgetPasswordForm.tsx +++ b/projects/app/src/pages/login/components/ForgetPasswordForm.tsx @@ -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'; @@ -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; @@ -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 @@ -200,6 +203,13 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => { {t('user:password.to_login')} + {openCodeAuthModal && ( + + )} ); }; diff --git a/projects/app/src/pages/login/components/RegisterForm.tsx b/projects/app/src/pages/login/components/RegisterForm.tsx index 0125218c2..f4af40d92 100644 --- a/projects/app/src/pages/login/components/RegisterForm.tsx +++ b/projects/app/src/pages/login/components/RegisterForm.tsx @@ -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 { postRegister } from '@/web/support/user/api'; @@ -11,6 +11,9 @@ import { emptyTemplates } from '@/web/core/app/templates'; import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useTranslation } from 'next-i18next'; import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; +import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; +import SendCodeAuthModal from '@/components/support/user/safe/SendCodeAuthModal'; +import { UserAuthTypeEnum } from '@fastgpt/global/support/user/auth/constants'; interface Props { loginSuccess: (e: ResLogin) => void; setPageType: Dispatch<`${LoginPageTypeEnum}`>; @@ -37,17 +40,18 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => { } = useForm({ mode: 'onBlur' }); - - const { sendCodeText, sendCode, codeCountDown } = useSendCode(); + const { + isOpen: openCodeAuthModal, + onOpen: onOpenCodeAuthModal, + onClose: onCloseCodeAuthModal + } = useDisclosure(); + const { sendCodeText, codeCountDown } = useSendCode(); const onclickSendCode = useCallback(async () => { const check = await trigger('username'); if (!check) return; - sendCode({ - username: getValues('username'), - type: 'register' - }); - }, [getValues, sendCode, trigger]); + onOpenCodeAuthModal(); + }, [onOpenCodeAuthModal, trigger]); const [requesting, setRequesting] = useState(false); @@ -215,6 +219,13 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => { {t('user:register.to_login')} + {openCodeAuthModal && ( + + )} ); }; diff --git a/projects/app/src/web/support/user/api.ts b/projects/app/src/web/support/user/api.ts index 15bb53545..0180c12a9 100644 --- a/projects/app/src/web/support/user/api.ts +++ b/projects/app/src/web/support/user/api.ts @@ -15,6 +15,7 @@ export const sendAuthCode = (data: { username: string; type: `${UserAuthTypeEnum}`; googleToken: string; + captcha: string; }) => POST(`/proApi/support/user/inform/sendAuthCode`, data); export const getTokenLogin = () => @@ -82,3 +83,8 @@ export const getWXLoginQR = () => export const getWXLoginResult = (code: string) => GET(`/proApi/support/user/account/login/wx/getResult`, { code }); + +export const getCaptchaPic = (username: string) => + GET<{ + captchaImage: string; + }>('/proApi/support/user/account/captcha', { username }); diff --git a/projects/app/src/web/support/user/hooks/useSendCode.ts b/projects/app/src/web/support/user/hooks/useSendCode.ts index 33e9b458a..6314d8a2f 100644 --- a/projects/app/src/web/support/user/hooks/useSendCode.ts +++ b/projects/app/src/web/support/user/hooks/useSendCode.ts @@ -12,10 +12,18 @@ export const useSendCode = () => { const [codeCountDown, setCodeCountDown] = useState(0); const { runAsync: sendCode, loading: codeSending } = useRequest2( - async ({ username, type }: { username: string; type: `${UserAuthTypeEnum}` }) => { + async ({ + username, + type, + captcha + }: { + username: string; + type: `${UserAuthTypeEnum}`; + captcha: string; + }) => { if (codeCountDown > 0) return; const googleToken = await getClientToken(feConfigs.googleClientVerKey); - await sendAuthCode({ username, type, googleToken }); + await sendAuthCode({ username, type, googleToken, captcha }); setCodeCountDown(60); timer = setInterval(() => {