diff --git a/packages/web/i18n/en/common.json b/packages/web/i18n/en/common.json index 6e67b09e3b2e..23842179b0f7 100644 --- a/packages/web/i18n/en/common.json +++ b/packages/web/i18n/en/common.json @@ -647,7 +647,8 @@ "success": "Start syncing" } }, - "training": {} + "training": { + } }, "data": { "Auxiliary Data": "Auxiliary data", @@ -1084,13 +1085,15 @@ "default_reply": "Default reply", "error": { "Create failed": "Create failed", + "code_error": "Code error", "fileNotFound": "File not found~", "inheritPermissionError": "Inherit permission Error", "missingParams": "Insufficient parameters", "team": { "overSize": "Team members exceed the limit" }, - "upload_file_error_filename": "{{name}} upload failed" + "upload_file_error_filename": "{{name}} upload failed", + "username_empty": "Account cannot be empty" }, "extraction_results": "Extract results", "field_name": "Name", diff --git a/packages/web/i18n/zh/common.json b/packages/web/i18n/zh/common.json index b3b1d28e6348..0d9b681c3b60 100644 --- a/packages/web/i18n/zh/common.json +++ b/packages/web/i18n/zh/common.json @@ -647,7 +647,8 @@ "success": "开始同步" } }, - "training": {} + "training": { + } }, "data": { "Auxiliary Data": "辅助数据", @@ -1084,13 +1085,15 @@ "default_reply": "默认回复", "error": { "Create failed": "创建失败", + "code_error": "验证码错误", "fileNotFound": "文件找不到了~", "inheritPermissionError": "权限继承错误", "missingParams": "参数缺失", "team": { "overSize": "团队成员超出上限" }, - "upload_file_error_filename": "{{name}} 上传失败" + "upload_file_error_filename": "{{name}} 上传失败", + "username_empty": "账号不能为空" }, "extraction_results": "提取结果", "field_name": "字段名", @@ -1247,7 +1250,6 @@ }, "user": { "Avatar": "头像", - "captcha_placeholder": "请输入验证码", "Go laf env": "点击前往 {{env}} 获取 PAT 凭证。", "Laf account course": "查看绑定 laf 账号教程。", "Laf account intro": "绑定你的 laf 账号后,你将可以在工作流中使用 laf 模块,实现在线编写代码。", @@ -1257,6 +1259,7 @@ "auth": { "Sending Code": "正在发送" }, + "captcha_placeholder": "请输入验证码", "inform": { "System message": "系统消息" }, diff --git a/projects/app/Dockerfile b/projects/app/Dockerfile index 6f213e0ff133..3cad897ef54a 100644 --- a/projects/app/Dockerfile +++ b/projects/app/Dockerfile @@ -75,8 +75,8 @@ COPY ./projects/app/data /app/data RUN chown -R nextjs:nodejs /app/data -ENV NODE_ENV production -ENV NEXT_TELEMETRY_DISABLED 1 +ENV NODE_ENV=production +ENV NEXT_TELEMETRY_DISABLED=1 ENV PORT=3000 EXPOSE 3000 diff --git a/projects/app/src/components/support/user/safe/SendCodeAuthModal.tsx b/projects/app/src/components/support/user/safe/SendCodeAuthModal.tsx index be27aae3f9b5..2a6762f5a709 100644 --- a/projects/app/src/components/support/user/safe/SendCodeAuthModal.tsx +++ b/projects/app/src/components/support/user/safe/SendCodeAuthModal.tsx @@ -1,58 +1,72 @@ 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 { Button, Input, Image, ModalBody, ModalFooter, Skeleton } from '@chakra-ui/react'; import MyModal from '@fastgpt/web/components/common/MyModal'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useTranslation } from 'next-i18next'; -import { useState } from 'react'; +import { useForm } from 'react-hook-form'; const SendCodeAuthModal = ({ username, - type, - onClose + onClose, + onSending, + onSendCode }: { username: string; - type: UserAuthTypeEnum; onClose: () => void; + + onSending: boolean; + onSendCode: (params_0: { username: string; captcha: string }) => Promise; }) => { const { t } = useTranslation(); - const [captchaInput, setCaptchaInput] = useState(''); - const { codeSending, sendCode } = useSendCode(); + + const { register, handleSubmit } = useForm({ + defaultValues: { + code: '' + } + }); + const { data, loading, runAsync: getCaptcha } = useRequest2(() => getCaptchaPic(username), { manual: false }); + return ( - + - captcha - setCaptchaInput(e.target.value)} - /> + + + + + - diff --git a/projects/app/src/pages/account/components/Info/UpdateNotificationModal.tsx b/projects/app/src/pages/account/components/Info/UpdateNotificationModal.tsx index 28588dcbc8a4..49ae75a57112 100644 --- a/projects/app/src/pages/account/components/Info/UpdateNotificationModal.tsx +++ b/projects/app/src/pages/account/components/Info/UpdateNotificationModal.tsx @@ -18,8 +18,6 @@ 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; @@ -30,7 +28,8 @@ const UpdateNotificationModal = ({ onClose }: { onClose: () => void }) => { const { t } = useTranslation(); const { initUserInfo } = useUserStore(); const { feConfigs } = useSystemStore(); - const { register, handleSubmit, trigger, getValues, watch } = useForm({ + + const { register, handleSubmit, watch } = useForm({ defaultValues: { account: '', verifyCode: '' @@ -38,11 +37,7 @@ 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); @@ -57,13 +52,7 @@ const UpdateNotificationModal = ({ onClose }: { onClose: () => void }) => { } ); - const { sendCodeText, codeCountDown } = useSendCode(); - - const onclickSendCode = useCallback(async () => { - const check = await trigger('account'); - if (!check) return; - onOpenCodeAuthModal(); - }, [onOpenCodeAuthModal, trigger]); + const { SendCodeBox } = useSendCode({ type: 'bindNotification' }); const placeholder = feConfigs?.bind_notification_method ?.map((item) => { @@ -107,23 +96,7 @@ const UpdateNotificationModal = ({ onClose }: { onClose: () => void }) => { {...register('verifyCode', { required: true })} placeholder={t('user:password.code_required')} > - 0 - ? { - color: 'myGray.500' - } - : { - color: 'primary.700', - cursor: 'pointer', - onClick: onclickSendCode - })} - > - {sendCodeText} - + @@ -140,13 +113,6 @@ const UpdateNotificationModal = ({ onClose }: { onClose: () => void }) => { - {openCodeAuthModal && ( - - )} ); }; diff --git a/projects/app/src/pages/login/components/ForgetPasswordForm.tsx b/projects/app/src/pages/login/components/ForgetPasswordForm.tsx index 2185d09c183e..8cd9fa0c261f 100644 --- a/projects/app/src/pages/login/components/ForgetPasswordForm.tsx +++ b/projects/app/src/pages/login/components/ForgetPasswordForm.tsx @@ -8,8 +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'; +import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; + interface Props { setPageType: Dispatch<`${LoginPageTypeEnum}`>; loginSuccess: (e: ResLogin) => void; @@ -30,25 +30,15 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => { register, handleSubmit, getValues, - trigger, + watch, formState: { errors } } = useForm({ mode: 'onBlur' }); + const username = watch('username'); - const { sendCodeText, codeCountDown } = useSendCode(); - const { - isOpen: openCodeAuthModal, - onOpen: onOpenCodeAuthModal, - onClose: onCloseCodeAuthModal - } = useDisclosure(); - const onclickSendCode = useCallback(async () => { - const check = await trigger('username'); - if (!check) return; - onOpenCodeAuthModal(); - }, [onOpenCodeAuthModal, trigger]); + const { SendCodeBox } = useSendCode({ type: 'findPassword' }); - const [requesting, setRequesting] = useState(false); const placeholder = feConfigs?.find_password_method ?.map((item) => { switch (item) { @@ -62,30 +52,23 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => { }) .join('/'); - const onclickFindPassword = useCallback( + const { runAsync: onclickFindPassword, loading: requesting } = useRequest2( async ({ username, code, password }: RegisterType) => { - setRequesting(true); - try { - loginSuccess( - await postFindPassword({ - username, - code, - password - }) - ); - toast({ - title: t('user:password.retrieved'), - status: 'success' - }); - } catch (error: any) { - toast({ - title: error.message || t('user:password.change_error'), - status: 'error' - }); - } - setRequesting(false); + loginSuccess( + await postFindPassword({ + username, + code, + password + }) + ); + toast({ + status: 'success', + title: t('user:password.retrieved') + }); }, - [loginSuccess, toast] + { + refreshDeps: [loginSuccess, t, toast] + } ); return ( @@ -131,23 +114,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => { required: t('user:password.code_required') })} > - 0 - ? { - color: 'myGray.500' - } - : { - color: 'primary.700', - cursor: 'pointer', - onClick: onclickSendCode - })} - > - {sendCodeText} - + { {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 f4af40d9261d..856e0656711c 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, useDisclosure } from '@chakra-ui/react'; +import React, { Dispatch } from 'react'; +import { FormControl, Box, Input, Button } 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'; @@ -12,8 +12,7 @@ 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}`>; @@ -35,63 +34,47 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => { register, handleSubmit, getValues, - trigger, + watch, formState: { errors } } = useForm({ mode: 'onBlur' }); - const { - isOpen: openCodeAuthModal, - onOpen: onOpenCodeAuthModal, - onClose: onCloseCodeAuthModal - } = useDisclosure(); - const { sendCodeText, codeCountDown } = useSendCode(); + const username = watch('username'); - const onclickSendCode = useCallback(async () => { - const check = await trigger('username'); - if (!check) return; - onOpenCodeAuthModal(); - }, [onOpenCodeAuthModal, trigger]); + const { SendCodeBox } = useSendCode({ type: 'register' }); - const [requesting, setRequesting] = useState(false); - - const onclickRegister = useCallback( + const { runAsync: onclickRegister, loading: requesting } = useRequest2( async ({ username, password, code }: RegisterType) => { - setRequesting(true); - try { - loginSuccess( - await postRegister({ - username, - code, - password, - inviterId: localStorage.getItem('inviterId') || undefined - }) - ); - toast({ - title: t('user:register.success'), - status: 'success' - }); - // auto register template app - setTimeout(() => { - Object.entries(emptyTemplates).map(([type, emptyTemplate]) => { - postCreateApp({ - avatar: emptyTemplate.avatar, - name: t(emptyTemplate.name as any), - modules: emptyTemplate.nodes, - edges: emptyTemplate.edges, - type: type as AppTypeEnum - }); + loginSuccess( + await postRegister({ + username, + code, + password, + inviterId: localStorage.getItem('inviterId') || undefined + }) + ); + + toast({ + status: 'success', + title: t('user:register.success') + }); + + // auto register template app + setTimeout(() => { + Object.entries(emptyTemplates).map(([type, emptyTemplate]) => { + postCreateApp({ + avatar: emptyTemplate.avatar, + name: t(emptyTemplate.name as any), + modules: emptyTemplate.nodes, + edges: emptyTemplate.edges, + type: type as AppTypeEnum }); - }, 100); - } catch (error: any) { - toast({ - title: error.message || t('user:register.error'), - status: 'error' }); - } - setRequesting(false); + }, 100); }, - [loginSuccess, t, toast] + { + refreshDeps: [loginSuccess, t, toast] + } ); const placeholder = feConfigs?.register_method @@ -148,23 +131,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => { required: t('user:password.code_required') })} > - 0 - ? { - color: 'myGray.500' - } - : { - color: 'primary.700', - cursor: 'pointer', - onClick: onclickSendCode - })} - > - {sendCodeText} - + { {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 0180c12a9938..6b7147883f8d 100644 --- a/projects/app/src/web/support/user/api.ts +++ b/projects/app/src/web/support/user/api.ts @@ -87,4 +87,4 @@ export const getWXLoginResult = (code: string) => export const getCaptchaPic = (username: string) => GET<{ captchaImage: string; - }>('/proApi/support/user/account/captcha', { username }); + }>('/proApi/support/user/account/captcha/getImgCaptcha', { username }); diff --git a/projects/app/src/web/support/user/hooks/useSendCode.ts b/projects/app/src/web/support/user/hooks/useSendCode.tsx similarity index 54% rename from projects/app/src/web/support/user/hooks/useSendCode.ts rename to projects/app/src/web/support/user/hooks/useSendCode.tsx index 6314d8a2f77d..0251d51c9d1c 100644 --- a/projects/app/src/web/support/user/hooks/useSendCode.ts +++ b/projects/app/src/web/support/user/hooks/useSendCode.tsx @@ -4,26 +4,24 @@ import { UserAuthTypeEnum } from '@fastgpt/global/support/user/auth/constants'; import { useTranslation } from 'next-i18next'; import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; +import { Box, BoxProps, useDisclosure } from '@chakra-ui/react'; +import SendCodeAuthModal from '@/components/support/user/safe/SendCodeAuthModal'; +import { useMemoizedFn } from 'ahooks'; +import { useToast } from '@fastgpt/web/hooks/useToast'; let timer: NodeJS.Timeout; -export const useSendCode = () => { +export const useSendCode = ({ type }: { type: `${UserAuthTypeEnum}` }) => { const { t } = useTranslation(); const { feConfigs } = useSystemStore(); + const { toast } = useToast(); const [codeCountDown, setCodeCountDown] = useState(0); const { runAsync: sendCode, loading: codeSending } = useRequest2( - async ({ - username, - type, - captcha - }: { - username: string; - type: `${UserAuthTypeEnum}`; - captcha: string; - }) => { + async ({ username, captcha }: { username: string; captcha: string }) => { if (codeCountDown > 0) return; const googleToken = await getClientToken(feConfigs.googleClientVerKey); await sendAuthCode({ username, type, googleToken, captcha }); + setCodeCountDown(60); timer = setInterval(() => { @@ -38,7 +36,7 @@ export const useSendCode = () => { { successToast: t('user:password.code_sended'), errorToast: t('user:password.code_send_error'), - refreshDeps: [codeCountDown, feConfigs?.googleClientVerKey] + refreshDeps: [codeCountDown, type, feConfigs?.googleClientVerKey] } ); @@ -53,11 +51,60 @@ export const useSendCode = () => { return t('user:password.get_code'); }, [codeCountDown, codeSending, t]); + const { + isOpen: openCodeAuthModal, + onOpen: onOpenCodeAuthModal, + onClose: onCloseCodeAuthModal + } = useDisclosure(); + + const SendCodeBox = useMemoizedFn(({ username, ...styles }: BoxProps & { username: string }) => { + return ( + <> + 0 + ? { + color: 'myGray.500' + } + : { + color: 'primary.700', + cursor: 'pointer', + onClick: () => { + if (!username) { + toast({ + status: 'warning', + title: t('common:error.username_empty') + }); + } else { + onOpenCodeAuthModal(); + } + } + })} + > + {sendCodeText} + + {openCodeAuthModal && ( + + )} + + ); + }); + return { codeSending, sendCode, sendCodeText, - codeCountDown + codeCountDown, + SendCodeBox }; };