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 (
+
+
+
+ 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(() => {