From 2194fc41dcd5519622af1a1c005d18bc87771c51 Mon Sep 17 00:00:00 2001 From: zzyangh <799463087@qq.com> Date: Fri, 21 Feb 2025 17:15:23 +0800 Subject: [PATCH 1/5] [chore]: Update Api --- .../api/base/service/Configuration/index.d.ts | 16 +++ .../api/base/service/Configuration/index.ts | 37 ++++++ .../lib/api/base/service/SMS/index.d.ts | 14 ++ .../shared/lib/api/base/service/SMS/index.ts | 39 ++++++ .../lib/api/base/service/User/index.d.ts | 6 + .../shared/lib/api/base/service/User/index.ts | 14 ++ .../shared/lib/api/base/service/common.d.ts | 120 ++++++++++++++++++ 7 files changed, 246 insertions(+) create mode 100644 packages/shared/lib/api/base/service/SMS/index.d.ts create mode 100644 packages/shared/lib/api/base/service/SMS/index.ts diff --git a/packages/shared/lib/api/base/service/Configuration/index.d.ts b/packages/shared/lib/api/base/service/Configuration/index.d.ts index 5473dfead..097824801 100644 --- a/packages/shared/lib/api/base/service/Configuration/index.d.ts +++ b/packages/shared/lib/api/base/service/Configuration/index.d.ts @@ -11,6 +11,10 @@ import { IGetLicenseUsageReply, IGetOauth2ConfigurationResDataReply, IOauth2ConfigurationReq, + IGetSmsConfigurationReply, + IUpdateSmsConfigurationReq, + ITestSmsConfigurationReq, + ITestSmsConfigurationReply, IGetSMTPConfigurationReply, IUpdateSMTPConfigurationReq, ITestSMTPConfigurationReq, @@ -70,6 +74,18 @@ export interface IUpdateOauth2ConfigurationParams export interface IUpdateOauth2ConfigurationReturn extends IGenericResp {} +export interface IGetSmsConfigurationReturn extends IGetSmsConfigurationReply {} + +export interface IUpdateSmsConfigurationParams + extends IUpdateSmsConfigurationReq {} + +export interface IUpdateSmsConfigurationReturn extends IGenericResp {} + +export interface ITestSmsConfigurationParams extends ITestSmsConfigurationReq {} + +export interface ITestSmsConfigurationReturn + extends ITestSmsConfigurationReply {} + export interface IGetSMTPConfigurationReturn extends IGetSMTPConfigurationReply {} diff --git a/packages/shared/lib/api/base/service/Configuration/index.ts b/packages/shared/lib/api/base/service/Configuration/index.ts index 327f0e493..968406f22 100644 --- a/packages/shared/lib/api/base/service/Configuration/index.ts +++ b/packages/shared/lib/api/base/service/Configuration/index.ts @@ -24,6 +24,11 @@ import { IGetOauth2ConfigurationReturn, IUpdateOauth2ConfigurationParams, IUpdateOauth2ConfigurationReturn, + IGetSmsConfigurationReturn, + IUpdateSmsConfigurationParams, + IUpdateSmsConfigurationReturn, + ITestSmsConfigurationParams, + ITestSmsConfigurationReturn, IGetSMTPConfigurationReturn, IUpdateSMTPConfigurationParams, IUpdateSMTPConfigurationReturn, @@ -180,6 +185,38 @@ class ConfigurationService extends ServiceBase { ); } + public GetSmsConfiguration(options?: AxiosRequestConfig) { + return this.get( + '/v1/dms/configurations/sms', + undefined, + options + ); + } + + public UpdateSmsConfiguration( + params: IUpdateSmsConfigurationParams, + options?: AxiosRequestConfig + ) { + const paramsData = this.cloneDeep(params); + return this.patch( + '/v1/dms/configurations/sms', + paramsData, + options + ); + } + + public TestSmsConfiguration( + params: ITestSmsConfigurationParams, + options?: AxiosRequestConfig + ) { + const paramsData = this.cloneDeep(params); + return this.post( + '/v1/dms/configurations/sms/test', + paramsData, + options + ); + } + public GetSMTPConfiguration(options?: AxiosRequestConfig) { return this.get( '/v1/dms/configurations/smtp', diff --git a/packages/shared/lib/api/base/service/SMS/index.d.ts b/packages/shared/lib/api/base/service/SMS/index.d.ts new file mode 100644 index 000000000..a2f7e9255 --- /dev/null +++ b/packages/shared/lib/api/base/service/SMS/index.d.ts @@ -0,0 +1,14 @@ +import { + ISendSmsCodeReq, + ISendSmsCodeReply, + IVerifySmsCodeReq, + IVerifySmsCodeReply +} from '../common.d'; + +export interface ISendSmsCodeParams extends ISendSmsCodeReq {} + +export interface ISendSmsCodeReturn extends ISendSmsCodeReply {} + +export interface IVerifySmsCodeParams extends IVerifySmsCodeReq {} + +export interface IVerifySmsCodeReturn extends IVerifySmsCodeReply {} diff --git a/packages/shared/lib/api/base/service/SMS/index.ts b/packages/shared/lib/api/base/service/SMS/index.ts new file mode 100644 index 000000000..168a378a6 --- /dev/null +++ b/packages/shared/lib/api/base/service/SMS/index.ts @@ -0,0 +1,39 @@ +/* tslint:disable no-identical-functions */ +/* tslint:disable no-useless-cast */ +/* tslint:disable no-unnecessary-type-assertion */ +/* tslint:disable no-big-function */ +/* tslint:disable no-duplicate-string */ +import ServiceBase from '../Service.base'; +import { AxiosRequestConfig } from 'axios'; + +import { + ISendSmsCodeParams, + ISendSmsCodeReturn, + IVerifySmsCodeParams, + IVerifySmsCodeReturn +} from './index.d'; + +class SMSService extends ServiceBase { + public SendSmsCode(params: ISendSmsCodeParams, options?: AxiosRequestConfig) { + const paramsData = this.cloneDeep(params); + return this.post( + '/v1/dms/configurations/sms/send_code', + paramsData, + options + ); + } + + public VerifySmsCode( + params: IVerifySmsCodeParams, + options?: AxiosRequestConfig + ) { + const paramsData = this.cloneDeep(params); + return this.post( + '/v1/dms/configurations/sms/verify_code', + paramsData, + options + ); + } +} + +export default new SMSService(); diff --git a/packages/shared/lib/api/base/service/User/index.d.ts b/packages/shared/lib/api/base/service/User/index.d.ts index 64c293e17..7cc10f9e2 100644 --- a/packages/shared/lib/api/base/service/User/index.d.ts +++ b/packages/shared/lib/api/base/service/User/index.d.ts @@ -8,6 +8,8 @@ import { IAddUserReply, IGenAccessToken, IGenAccessTokenReply, + IAddSessionReq, + IVerifyUserLoginReply, IGetUserReply, IUpdateUserReq, IUserOpPermission, @@ -42,6 +44,10 @@ export interface IGenAccessTokenParams extends IGenAccessToken {} export interface IGenAccessTokenReturn extends IGenAccessTokenReply {} +export interface IVerifyUserLoginParams extends IAddSessionReq {} + +export interface IVerifyUserLoginReturn extends IVerifyUserLoginReply {} + export interface IGetUserParams { user_uid: string; } diff --git a/packages/shared/lib/api/base/service/User/index.ts b/packages/shared/lib/api/base/service/User/index.ts index c14464e9b..d2923baf4 100644 --- a/packages/shared/lib/api/base/service/User/index.ts +++ b/packages/shared/lib/api/base/service/User/index.ts @@ -15,6 +15,8 @@ import { IAddUserReturn, IGenAccessTokenParams, IGenAccessTokenReturn, + IVerifyUserLoginParams, + IVerifyUserLoginReturn, IGetUserParams, IGetUserReturn, IUpdateUserParams, @@ -60,6 +62,18 @@ class UserService extends ServiceBase { ); } + public VerifyUserLogin( + params: IVerifyUserLoginParams, + options?: AxiosRequestConfig + ) { + const paramsData = this.cloneDeep(params); + return this.post( + '/v1/dms/users/verify_user_login', + paramsData, + options + ); + } + public GetUser(params: IGetUserParams, options?: AxiosRequestConfig) { const paramsData = this.cloneDeep(params); const user_uid = paramsData.user_uid; diff --git a/packages/shared/lib/api/base/service/common.d.ts b/packages/shared/lib/api/base/service/common.d.ts index 38b8b662a..5f257f728 100644 --- a/packages/shared/lib/api/base/service/common.d.ts +++ b/packages/shared/lib/api/base/service/common.d.ts @@ -150,12 +150,16 @@ export interface IAddSession { password: string; username: string; + + verify_code?: string; } export interface IAddSessionReply { code?: number; data?: { + message?: string; + token?: string; }; @@ -832,6 +836,26 @@ export interface IGetSQLQueryConfigurationReply { message?: string; } +export interface IGetSmsConfigurationReply { + code?: number; + + data?: IGetSmsConfigurationReplyItem; + + message?: string; +} + +export interface IGetSmsConfigurationReplyItem { + configuration?: { + [key: string]: string; + }; + + enable?: boolean; + + sms_type?: string; + + url?: string; +} + export interface IGetUser { access_token_info?: IAccessTokenInfo; @@ -853,6 +877,8 @@ export interface IGetUser { third_party_user_info?: string; + two_factor_enabled?: boolean; + uid?: string; user_bind_projects?: IUserBindProject[]; @@ -1772,6 +1798,24 @@ export interface ISQLQueryConfig { query_timeout_second?: number; } +export interface ISendSmsCodeReply { + code?: number; + + data?: ISendSmsCodeReplyData; + + message?: string; +} + +export interface ISendSmsCodeReplyData { + is_sms_code_sent_normally?: boolean; + + send_error_message?: string; +} + +export interface ISendSmsCodeReq { + username?: string; +} + export interface ITask { task_uid?: string; } @@ -1832,6 +1876,28 @@ export interface ITestSMTPConfigurationResData { send_error_message?: string; } +export interface ITestSmsConfiguration { + recipient_phone?: string; +} + +export interface ITestSmsConfigurationReply { + code?: number; + + data?: ITestSmsConfigurationResData; + + message?: string; +} + +export interface ITestSmsConfigurationReq { + test_sms_configuration?: ITestSmsConfiguration; +} + +export interface ITestSmsConfigurationResData { + is_smtp_send_normal?: boolean; + + send_error_message?: string; +} + export interface ITestWeChatConfiguration { recipient_id?: string; } @@ -1905,6 +1971,8 @@ export interface IUpdateCurrentUser { phone?: string; + two_factor_enabled?: boolean; + wxid?: string; } @@ -2040,6 +2108,22 @@ export interface IUpdateSMTPConfigurationReq { smtp_configuration?: IUpdateSMTPConfiguration; } +export interface IUpdateSmsConfiguration { + configuration?: { + [key: string]: string; + }; + + enable_sms?: boolean; + + sms_type?: string; + + url?: string; +} + +export interface IUpdateSmsConfigurationReq { + update_sms_configuration?: IUpdateSmsConfiguration; +} + export interface IUpdateUser { email?: string; @@ -2134,6 +2218,42 @@ export interface IUserOpPermission { project_uid?: string; } +export interface IVerifySmsCodeReply { + code?: number; + + data?: IVerifySmsCodeReplyData; + + message?: string; +} + +export interface IVerifySmsCodeReplyData { + is_verify_sent_normally?: boolean; + + verify_error_message?: string; +} + +export interface IVerifySmsCodeReq { + code?: string; + + username?: string; +} + +export interface IVerifyUserLoginReply { + code?: number; + + data?: { + phone?: string; + + two_factor_enabled?: boolean; + + user_uid?: string; + + verify_failed_msg?: string; + }; + + message?: string; +} + export interface IWeChatConfigurationResData { agent_id?: number; From 4edc8c2eccbe8c7215ce8fac35a478dbd47d5935 Mon Sep 17 00:00:00 2001 From: zzyangh <799463087@qq.com> Date: Fri, 21 Feb 2025 17:19:34 +0800 Subject: [PATCH 2/5] [feature]: Two factor authentication --- packages/base/src/locale/zh-CN/dmsAccount.ts | 8 +- packages/base/src/locale/zh-CN/dmsLogin.ts | 6 +- packages/base/src/locale/zh-CN/dmsSystem.ts | 9 +- .../PersonalSMS/components/ConfigField.tsx | 28 +++ .../page/Account/PersonalSMS/index.data.ts | 1 + .../src/page/Account/PersonalSMS/index.tsx | 166 ++++++++++++++ .../page/Account/PersonalSMS/index.type.ts | 17 ++ packages/base/src/page/Account/index.tsx | 8 + .../src/page/Login/components/LoginForm.tsx | 122 ++++++++++ .../Login/components/VerificationCodeForm.tsx | 68 ++++++ packages/base/src/page/Login/index.tsx | 213 ++++++++---------- packages/base/src/page/Login/style.ts | 5 + packages/base/src/page/Login/types.ts | 22 ++ .../components/ConfigExtraButtons.tsx | 124 ++++++++++ .../SMSSetting/components/ConfigField.tsx | 39 ++++ .../GlobalSetting/SMSSetting/index.data.ts | 1 + .../System/GlobalSetting/SMSSetting/index.tsx | 212 +++++++++++++++++ .../GlobalSetting/SMSSetting/index.type.ts | 5 + .../src/page/System/GlobalSetting/index.tsx | 4 + .../VerificationCodeInput.tsx | 62 +++++ .../VerificationCodeInput/index.tsx | 1 + .../components/VerificationCodeInput/style.ts | 10 + packages/shared/lib/components/index.ts | 1 + .../usePermission/permissionManifest.ts | 5 + .../lib/features/usePermission/permissions.ts | 3 +- packages/shared/lib/locale/zh-CN/common.ts | 7 + packages/shared/lib/utils/Common.ts | 7 + 27 files changed, 1032 insertions(+), 122 deletions(-) create mode 100644 packages/base/src/page/Account/PersonalSMS/components/ConfigField.tsx create mode 100644 packages/base/src/page/Account/PersonalSMS/index.data.ts create mode 100644 packages/base/src/page/Account/PersonalSMS/index.tsx create mode 100644 packages/base/src/page/Account/PersonalSMS/index.type.ts create mode 100644 packages/base/src/page/Login/components/LoginForm.tsx create mode 100644 packages/base/src/page/Login/components/VerificationCodeForm.tsx create mode 100644 packages/base/src/page/System/GlobalSetting/SMSSetting/components/ConfigExtraButtons.tsx create mode 100644 packages/base/src/page/System/GlobalSetting/SMSSetting/components/ConfigField.tsx create mode 100644 packages/base/src/page/System/GlobalSetting/SMSSetting/index.data.ts create mode 100644 packages/base/src/page/System/GlobalSetting/SMSSetting/index.tsx create mode 100644 packages/base/src/page/System/GlobalSetting/SMSSetting/index.type.ts create mode 100644 packages/shared/lib/components/VerificationCodeInput/VerificationCodeInput.tsx create mode 100644 packages/shared/lib/components/VerificationCodeInput/index.tsx create mode 100644 packages/shared/lib/components/VerificationCodeInput/style.ts diff --git a/packages/base/src/locale/zh-CN/dmsAccount.ts b/packages/base/src/locale/zh-CN/dmsAccount.ts index 3a1833f3f..285c4ee45 100644 --- a/packages/base/src/locale/zh-CN/dmsAccount.ts +++ b/packages/base/src/locale/zh-CN/dmsAccount.ts @@ -38,5 +38,11 @@ export default { updateWechatSuccess: '微信号更新成功', updatePhoneSuccess: '手机号更新成功', wechat: '企业微信UserID', - phone: '手机号' + phone: '手机号', + sms: { + title: '双因素认证', + verificationCode: '验证码', + noPhoneNumbersTips: '请先完成手机号绑定后开启双因素认证', + updateSuccessTips: '双因素认证更新成功' + } }; diff --git a/packages/base/src/locale/zh-CN/dmsLogin.ts b/packages/base/src/locale/zh-CN/dmsLogin.ts index 36d08fd20..8f36e2045 100644 --- a/packages/base/src/locale/zh-CN/dmsLogin.ts +++ b/packages/base/src/locale/zh-CN/dmsLogin.ts @@ -23,5 +23,9 @@ export default { }, browserVersionTile: '浏览器版本不兼容', browserVersionDesc: - '为了正常使用平台功能,请使用 Chrome 80 或更高版本进行访问' + '为了正常使用平台功能,请使用 Chrome 80 或更高版本进行访问', + verifyCode: '验证码', + verifyCodePlaceholder: '请输入验证码', + verify: '验证', + returnLogin: '返回登录' }; diff --git a/packages/base/src/locale/zh-CN/dmsSystem.ts b/packages/base/src/locale/zh-CN/dmsSystem.ts index 734ad2c01..eb64fc34a 100644 --- a/packages/base/src/locale/zh-CN/dmsSystem.ts +++ b/packages/base/src/locale/zh-CN/dmsSystem.ts @@ -213,7 +213,14 @@ export default { cbOperationLogsExpiredHours: 'CB工作台操作审计过期时间', urlAddressPrefix: 'URL地址前缀', urlAddressPrefixTips: '配置能访问SQLE的url地址信息', - urlAddressFormatTips: '格式为 http(s)://ip:port/sqle' + urlAddressFormatTips: '格式为 http(s)://ip:port/sqle', + + smsSetting: { + title: '短信服务', + testSuccess: '当前短信服务验证通过', + testing: '正在测试短信服务...', + smsType: '短信服务类型' + } }, personalize: { diff --git a/packages/base/src/page/Account/PersonalSMS/components/ConfigField.tsx b/packages/base/src/page/Account/PersonalSMS/components/ConfigField.tsx new file mode 100644 index 000000000..cc83298ad --- /dev/null +++ b/packages/base/src/page/Account/PersonalSMS/components/ConfigField.tsx @@ -0,0 +1,28 @@ +import { useTranslation } from 'react-i18next'; +import { FormItemLabel } from '@actiontech/shared/lib/components/CustomForm'; +import { VerificationCodeInput } from '@actiontech/shared'; +import { ConfigFieldProps } from '../index.type'; +import SMS from '@actiontech/shared/lib/api/base/service/SMS'; + +const ConfigField: React.FC = ({ userPhone, username }) => { + const { t } = useTranslation(); + + const onSendCode = () => { + return SMS.SendSmsCode({ username }); + }; + + return ( + <> + {userPhone} + + + + + ); +}; + +export default ConfigField; diff --git a/packages/base/src/page/Account/PersonalSMS/index.data.ts b/packages/base/src/page/Account/PersonalSMS/index.data.ts new file mode 100644 index 000000000..054126ab0 --- /dev/null +++ b/packages/base/src/page/Account/PersonalSMS/index.data.ts @@ -0,0 +1 @@ +export const switchFieldName = 'enabled'; diff --git a/packages/base/src/page/Account/PersonalSMS/index.tsx b/packages/base/src/page/Account/PersonalSMS/index.tsx new file mode 100644 index 000000000..18c533cbf --- /dev/null +++ b/packages/base/src/page/Account/PersonalSMS/index.tsx @@ -0,0 +1,166 @@ +import { useTranslation } from 'react-i18next'; +import { useBoolean } from 'ahooks'; +import { useMemo, useEffect } from 'react'; +import { Spin, message } from 'antd'; +import { CustomLabelContent } from '@actiontech/shared/lib/components/CustomForm'; +import ConfigField from './components/ConfigField'; +import { ResponseCode } from '@actiontech/shared/lib/enum'; +import { FormFields } from './index.type'; +import { switchFieldName } from './index.data'; +import { + useConfigRender, + ConfigSwitch, + ConfigSubmitButtonField, + useConfigSwitchControls +} from '@actiontech/shared'; +import { PersonalSMSProps } from './index.type'; +import User from '@actiontech/shared/lib/api/base/service/User'; +import SMS from '@actiontech/shared/lib/api/base/service/SMS'; + +const PersonalSMS: React.FC = ({ + userBaseInfo, + getUserInfo, + loading +}) => { + const { t } = useTranslation(); + + const [messageApi, contextHolder] = message.useMessage(); + + const [submitLoading, { setTrue: startSubmit, setFalse: submitFinish }] = + useBoolean(); + + const { + form, + renderConfigForm, + startModify, + modifyFinish, + modifyFlag, + enabled + } = useConfigRender({ + switchFieldName, + switchFieldLabel: ( + + ) + }); + + useEffect(() => { + if (!!userBaseInfo) { + form.setFieldsValue({ + [switchFieldName]: !!userBaseInfo.two_factor_enabled + }); + } + }, [userBaseInfo, form]); + + const isConfigClosed = useMemo(() => { + return !userBaseInfo?.two_factor_enabled; + }, [userBaseInfo]); + + const handleClickModify = () => { + startModify(); + }; + const handleClickCancel = () => { + if (isConfigClosed) form.setFieldValue(switchFieldName, false); + modifyFinish(); + }; + + const onSubmit = async (isCodingEnabled: boolean, values?: FormFields) => { + startSubmit(); + if (isCodingEnabled) { + const verificationRes = await SMS.VerifySmsCode({ + code: values?.code, + username: userBaseInfo?.name + }); + if (verificationRes.data.data?.verify_error_message) { + messageApi.error(verificationRes.data.data?.verify_error_message); + } + if ( + verificationRes.data.code !== ResponseCode.SUCCESS || + !verificationRes.data.data?.is_verify_sent_normally + ) { + submitFinish(); + return; + } + } + User.UpdateCurrentUser({ + current_user: { + two_factor_enabled: isCodingEnabled + } + }) + .then((res) => { + if (res.data.code === ResponseCode.SUCCESS) { + messageApi.success(t('dmsAccount.sms.updateSuccessTips')); + modifyFinish(); + form.resetFields(); + getUserInfo(); + } + }) + .finally(() => { + submitFinish(); + }); + }; + + const onConfigSwitchPopoverConfirm = () => { + if (!userBaseInfo?.two_factor_enabled && modifyFlag) { + handleClickCancel(); + hiddenConfigSwitchPopover(); + } else { + onSubmit(false); + } + }; + + const { + configSwitchPopoverOpenState, + generateConfigSwitchPopoverTitle, + onConfigSwitchPopoverOpen, + handleConfigSwitchChange, + hiddenConfigSwitchPopover + } = useConfigSwitchControls(form, switchFieldName); + + return ( +
+ {contextHolder} + + {renderConfigForm({ + data: userBaseInfo ?? {}, + columns: [], + configExtraButtons: null, + configSwitchNode: ( + { + if (open && !userBaseInfo?.phone) { + messageApi.error(t('dmsAccount.sms.noPhoneNumbersTips')); + form.setFieldValue(switchFieldName, false); + return; + } + handleConfigSwitchChange(open, handleClickModify); + }} + onSwitchPopoverOpen={onConfigSwitchPopoverOpen} + /> + ), + configField: ( + + ), + submitButtonField: ( + + ), + onSubmit: (values) => { + onSubmit(enabled, values); + } + })} + +
+ ); +}; + +export default PersonalSMS; diff --git a/packages/base/src/page/Account/PersonalSMS/index.type.ts b/packages/base/src/page/Account/PersonalSMS/index.type.ts new file mode 100644 index 000000000..bb4812323 --- /dev/null +++ b/packages/base/src/page/Account/PersonalSMS/index.type.ts @@ -0,0 +1,17 @@ +import { IGetUser } from '@actiontech/shared/lib/api/base/service/common'; + +export type FormFields = { + enabled: boolean; + code: string; +}; + +export type PersonalSMSProps = { + userBaseInfo?: IGetUser; + getUserInfo: () => void; + loading: boolean; +}; + +export type ConfigFieldProps = { + userPhone?: string; + username?: string; +}; diff --git a/packages/base/src/page/Account/index.tsx b/packages/base/src/page/Account/index.tsx index 46c59eca7..4c5ee5bf8 100644 --- a/packages/base/src/page/Account/index.tsx +++ b/packages/base/src/page/Account/index.tsx @@ -14,6 +14,7 @@ import UserWechat from './components/UserWechat'; import UserPhone from './components/UserPhone'; import UserEmail from './components/UserEmail'; import AccessToken from './components/AccessToken'; +import PersonalSMS from './PersonalSMS'; const Account: React.FC = () => { const { t } = useTranslation(); @@ -80,6 +81,13 @@ const Account: React.FC = () => { expiration={userInfo?.access_token_info?.token_expired_timestamp} updateUserInfo={updateUserInfo} /> + {/* #if [ee] */} + + {/* #endif */} diff --git a/packages/base/src/page/Login/components/LoginForm.tsx b/packages/base/src/page/Login/components/LoginForm.tsx new file mode 100644 index 000000000..d7512ebab --- /dev/null +++ b/packages/base/src/page/Login/components/LoginForm.tsx @@ -0,0 +1,122 @@ +import { Form, Typography, Space, Checkbox } from 'antd'; +import React, { useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useRequest } from 'ahooks'; +import OAuth2 from '@actiontech/shared/lib/api/base/service/OAuth2'; +import { BasicInput, BasicButton } from '@actiontech/shared'; +import { LockFilled, UserFilled } from '@actiontech/icons'; +import useThemeStyleData from '../../../hooks/useThemeStyleData'; +import { LoginFormProps } from '../types'; + +const LoginForm: React.FC = ({ + onSubmit, + loading, + form, + hidden +}) => { + const { t } = useTranslation(); + + const { baseTheme } = useThemeStyleData(); + + const { run: getOauth2Tips, data: oauthConfig } = useRequest( + () => { + return OAuth2.GetOauth2Tips().then((res) => res.data?.data ?? {}); + }, + { + manual: true + } + ); + + // #if [ee] + useEffect(() => { + getOauth2Tips(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + // #endif + + return ( + + ); +}; + +export default LoginForm; diff --git a/packages/base/src/page/Login/components/VerificationCodeForm.tsx b/packages/base/src/page/Login/components/VerificationCodeForm.tsx new file mode 100644 index 000000000..cbfb4b111 --- /dev/null +++ b/packages/base/src/page/Login/components/VerificationCodeForm.tsx @@ -0,0 +1,68 @@ +import { + VerificationCodeInput, + BasicButton, + maskPhoneNumber +} from '@actiontech/shared'; +import { Form, Typography, Space } from 'antd'; +import { useTranslation } from 'react-i18next'; +import { VerificationCodeReturnButtonStyleWrapper } from '../style'; +import SMS from '@actiontech/shared/lib/api/base/service/SMS'; +import { VerificationCodeFormProps } from '../types'; + +const VerificationCodeForm: React.FC = ({ + loading, + onVerify, + form, + hideVerificationForm, + username, + phone +}) => { + const { t } = useTranslation(); + + const onSendCode = () => { + return SMS.SendSmsCode({ username }); + }; + + return ( +
+ + + + {`${t('common.phone')}:`} + {phone ? maskPhoneNumber(phone) : '-'} + + + + + + + + + {t('dmsLogin.verify')} + + + + + {t('dmsLogin.returnLogin')} + + +
+ ); +}; + +export default VerificationCodeForm; diff --git a/packages/base/src/page/Login/index.tsx b/packages/base/src/page/Login/index.tsx index bff7e3d51..1840a203b 100644 --- a/packages/base/src/page/Login/index.tsx +++ b/packages/base/src/page/Login/index.tsx @@ -1,66 +1,79 @@ -import { Form, message, Typography, Space, Checkbox } from 'antd'; -import { useEffect } from 'react'; +import { message, Form } from 'antd'; import { useTranslation } from 'react-i18next'; import { useDispatch } from 'react-redux'; -import { useRequest } from 'ahooks'; import { updateToken } from '../../store/user'; import Session from '@actiontech/shared/lib/api/base/service/Session'; -import OAuth2 from '@actiontech/shared/lib/api/base/service/OAuth2'; import LoginLayout from './components/LoginLayout'; -import { - BasicInput, - BasicButton, - useTypedNavigate, - useTypedQuery -} from '@actiontech/shared'; -import { LoginFormFieldValue } from './types'; +import { useTypedNavigate, useTypedQuery, EmptyBox } from '@actiontech/shared'; +import { LoginFormFieldValue, VerificationCodeFormFieldValue } from './types'; import { useBoolean } from 'ahooks'; import useBrowserVersionTips from '../../hooks/useBrowserVersionTips'; -import { LockFilled, UserFilled } from '@actiontech/icons'; -import useThemeStyleData from '../../hooks/useThemeStyleData'; import { LocalStorageWrapper } from '@actiontech/shared'; import { StorageKey, - CompanyNoticeDisplayStatusEnum + CompanyNoticeDisplayStatusEnum, + ResponseCode } from '@actiontech/shared/lib/enum'; import { OPEN_CLOUD_BEAVER_URL_PARAM_NAME, ROUTE_PATHS } from '@actiontech/shared/lib/data/routePaths'; +import LoginForm from './components/LoginForm'; +import VerificationCodeForm from './components/VerificationCodeForm'; +import User from '@actiontech/shared/lib/api/base/service/User'; +import { useState } from 'react'; const Login = () => { const { t } = useTranslation(); - const { baseTheme } = useThemeStyleData(); - useBrowserVersionTips(); + const [loginForm] = Form.useForm(); + + const username = Form.useWatch('username', loginForm); + + const [verificationCodeForm] = Form.useForm(); + const dispatch = useDispatch(); const [messageApi, contextHolder] = message.useMessage(); const [loading, { setTrue, setFalse }] = useBoolean(); + const [phone, setPhone] = useState(); + + const [ + verifyCodeLoading, + { setTrue: verifyCodePending, setFalse: verifyCodeDone } + ] = useBoolean(); + + const [ + allowVerificationCode, + { setTrue: showVerificationForm, setFalse: hideVerificationForm } + ] = useBoolean(); + const navigate = useTypedNavigate(); const extractQueries = useTypedQuery(); - const login = (formData: LoginFormFieldValue) => { - // #if [ee] - if (!formData.userAgreement) { - messageApi.error(t('dmsLogin.errorMessage.userAgreement')); - return; - } - // #endif - setTrue(); + + const addSession = () => { + const loginFormValues = loginForm.getFieldsValue(); + const verificationCodeFormValues = verificationCodeForm.getFieldsValue(); Session.AddSession({ session: { - username: formData.username, - password: formData.password + username: loginFormValues.username, + password: loginFormValues.password, + // #if [ee] + verify_code: verificationCodeFormValues.verificationCode + // #endif } }) .then((res) => { const token = res.data.data?.token ? `Bearer ${res.data.data.token}` : ''; + if (res.data.data?.message) { + messageApi.error(res.data.data.message); + } if (token) { dispatch( updateToken({ @@ -70,11 +83,9 @@ const Login = () => { const encodedTarget = extractQueries( ROUTE_PATHS.BASE.LOGIN.index )?.target; - if (encodedTarget) { const decoded = decodeURIComponent(encodedTarget); const [path, targetParams] = decoded.split('?'); - if (targetParams) { navigate(`${path}?${targetParams}`); } else if (path.endsWith('cloud-beaver')) { @@ -93,107 +104,73 @@ const Login = () => { }) .finally(() => { setFalse(); + verifyCodeDone(); }); }; - const { run: getOauth2Tips, data: oauthConfig } = useRequest( - () => { - return OAuth2.GetOauth2Tips().then((res) => res.data?.data ?? {}); - }, - { - manual: true + const login = (formData: LoginFormFieldValue) => { + // #if [ee] + if (!formData.userAgreement) { + messageApi.error(t('dmsLogin.errorMessage.userAgreement')); + return; } - ); + // #endif + setTrue(); + // #if [ee] + User.VerifyUserLogin({ + session: { + username: formData.username, + password: formData.password + } + }) + .then((res) => { + const { code, data } = res.data; + if (code === ResponseCode.SUCCESS) { + if (data?.two_factor_enabled) { + showVerificationForm(); + setPhone(data.phone); + setFalse(); + } else { + addSession(); + } + } + }) + .catch(() => { + setFalse(); + }); + // #else + addSession(); + // #endif + }; // #if [ee] - useEffect(() => { - getOauth2Tips(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + const onVerify = () => { + verifyCodePending(); + addSession(); + }; // #endif return ( {contextHolder} -
- - - } - /> - - - - } - /> - - {/* #if [ee] */} - - - - {t('dmsLogin.userAgreementTips')} - - {t('dmsLogin.userAgreement')} - - - - - {/* #endif */} - - {t('dmsLogin.login')} - - {oauthConfig?.enable_oauth2 ? ( - - {oauthConfig?.login_tip} - - ) : null} -
+
); }; diff --git a/packages/base/src/page/Login/style.ts b/packages/base/src/page/Login/style.ts index c3614c402..8a5fb98ba 100644 --- a/packages/base/src/page/Login/style.ts +++ b/packages/base/src/page/Login/style.ts @@ -131,3 +131,8 @@ export const LoginPageRightStyleWrapper = styled('div')` } } `; + +export const VerificationCodeReturnButtonStyleWrapper = styled('div')` + display: flex; + justify-content: center; +`; diff --git a/packages/base/src/page/Login/types.ts b/packages/base/src/page/Login/types.ts index d0fc79e09..aac3be838 100644 --- a/packages/base/src/page/Login/types.ts +++ b/packages/base/src/page/Login/types.ts @@ -1,5 +1,27 @@ +import { FormInstance } from 'antd'; + export type LoginFormFieldValue = { username: string; password: string; userAgreement: boolean; }; + +export type LoginFormProps = { + onSubmit: (values: LoginFormFieldValue) => void; + loading: boolean; + form: FormInstance; + hidden: boolean; +}; + +export type VerificationCodeFormFieldValue = { + verificationCode: string; +}; + +export type VerificationCodeFormProps = { + form: FormInstance; + loading: boolean; + onVerify: (values: VerificationCodeFormFieldValue) => void; + hideVerificationForm: () => void; + username?: string; + phone?: string; +}; diff --git a/packages/base/src/page/System/GlobalSetting/SMSSetting/components/ConfigExtraButtons.tsx b/packages/base/src/page/System/GlobalSetting/SMSSetting/components/ConfigExtraButtons.tsx new file mode 100644 index 000000000..c7c30fd3e --- /dev/null +++ b/packages/base/src/page/System/GlobalSetting/SMSSetting/components/ConfigExtraButtons.tsx @@ -0,0 +1,124 @@ +import { useTranslation } from 'react-i18next'; +import { Space, Form, message } from 'antd'; +import { + ConfigTestBtn, + ConfigTestPopoverForm, + BasicInput, + ConfigModifyBtn +} from '@actiontech/shared'; +import { FormItemLabel } from '@actiontech/shared/lib/components/CustomForm'; +import { useRef, useState } from 'react'; +import baseConfiguration from '@actiontech/shared/lib/api/base/service/Configuration'; +import { ResponseCode } from '@actiontech/shared/lib/enum'; +import { formItemLayout } from '@actiontech/shared/lib/components/CustomForm/style'; + +export interface ConfigExtraButtonsProps { + isConfigClosed: boolean; + extraButtonsVisible: boolean; + handleClickModify: () => void; + enabled: boolean; +} + +const ConfigExtraButtons = ({ + isConfigClosed, + extraButtonsVisible, + handleClickModify, + enabled +}: ConfigExtraButtonsProps) => { + const { t } = useTranslation(); + + const [messageApi, messageContextHolder] = message.useMessage(); + + const [testForm] = Form.useForm<{ phone: string }>(); + + const testing = useRef(false); + const [testPopoverVisible, toggleTestPopoverVisible] = useState(false); + + const testCodingConfiguration = async () => { + if (testing.current) { + return; + } + const values = await testForm.validateFields(); + + testing.current = true; + toggleTestPopoverVisible(false); + const hide = messageApi.loading( + t('dmsSystem.global.smsSetting.testing'), + 0 + ); + baseConfiguration + .TestSmsConfiguration({ + test_sms_configuration: { + recipient_phone: values.phone + } + }) + .then((res) => { + if (res.data.code === ResponseCode.SUCCESS) { + if (res.data.data?.is_smtp_send_normal) { + messageApi.success(t('dmsSystem.global.smsSetting.testSuccess')); + } else { + messageApi.error( + res.data.data?.send_error_message ?? t('common.unknownError') + ); + } + } + }) + .finally(() => { + hide(); + testing.current = false; + testForm.resetFields(); + }); + }; + + const onTestPopoverOpen = (open: boolean) => { + if (!enabled) { + return; + } + if (!open) { + testForm.resetFields(); + } + toggleTestPopoverVisible(open); + }; + + return ( + <> + {messageContextHolder} + + + ); +}; + +export default ConfigExtraButtons; diff --git a/packages/base/src/page/System/GlobalSetting/SMSSetting/components/ConfigField.tsx b/packages/base/src/page/System/GlobalSetting/SMSSetting/components/ConfigField.tsx new file mode 100644 index 000000000..1e3be27bc --- /dev/null +++ b/packages/base/src/page/System/GlobalSetting/SMSSetting/components/ConfigField.tsx @@ -0,0 +1,39 @@ +import { useTranslation } from 'react-i18next'; + +import { FormItemLabel } from '@actiontech/shared/lib/components/CustomForm'; +import { BasicInput } from '@actiontech/shared'; + +const ConfigField = () => { + const { t } = useTranslation(); + + return ( + <> + + + + + + + + ); +}; + +export default ConfigField; diff --git a/packages/base/src/page/System/GlobalSetting/SMSSetting/index.data.ts b/packages/base/src/page/System/GlobalSetting/SMSSetting/index.data.ts new file mode 100644 index 000000000..054126ab0 --- /dev/null +++ b/packages/base/src/page/System/GlobalSetting/SMSSetting/index.data.ts @@ -0,0 +1 @@ +export const switchFieldName = 'enabled'; diff --git a/packages/base/src/page/System/GlobalSetting/SMSSetting/index.tsx b/packages/base/src/page/System/GlobalSetting/SMSSetting/index.tsx new file mode 100644 index 000000000..c48d6b3cd --- /dev/null +++ b/packages/base/src/page/System/GlobalSetting/SMSSetting/index.tsx @@ -0,0 +1,212 @@ +import { useTranslation } from 'react-i18next'; +import { useBoolean, useRequest } from 'ahooks'; +import { useCallback, useMemo } from 'react'; +import { Spin, Typography } from 'antd'; +import { CustomLabelContent } from '@actiontech/shared/lib/components/CustomForm'; +import ConfigExtraButtons from './components/ConfigExtraButtons'; +import ConfigField from './components/ConfigField'; +import baseConfiguration from '@actiontech/shared/lib/api/base/service/Configuration'; +import { IGetSmsConfigurationReplyItem } from '@actiontech/shared/lib/api/base/service/common'; +import { ResponseCode } from '@actiontech/shared/lib/enum'; +import { FormFields } from './index.type'; +import { switchFieldName } from './index.data'; +import { + useConfigRender, + ReadOnlyConfigColumnsType, + ConfigSwitch, + ConfigSubmitButtonField, + useConfigSwitchControls +} from '@actiontech/shared'; +import { + PERMISSIONS, + PermissionControl +} from '@actiontech/shared/lib/features'; + +const CodingSetting: React.FC = () => { + const { t } = useTranslation(); + + const [submitLoading, { setTrue: startSubmit, setFalse: submitFinish }] = + useBoolean(); + + const { + form, + renderConfigForm, + startModify, + modifyFinish, + modifyFlag, + extraButtonsVisible, + enabled + } = useConfigRender({ + switchFieldName, + switchFieldLabel: ( + + ) + }); + + const { + data: smsInfo, + loading, + refresh: refreshSmsInfo + } = useRequest( + () => + baseConfiguration + .GetSmsConfiguration() + .then((res) => res.data.data ?? {}), + { + onSuccess(res) { + if (res) { + form.setFieldsValue({ + enabled: !!res.enable + }); + } + } + } + ); + + const isConfigClosed = useMemo(() => { + return !smsInfo?.enable; + }, [smsInfo]); + + const setFormDefaultValue = useCallback(() => { + form.setFieldsValue({ + url: smsInfo?.url, + token: smsInfo?.configuration?.token + }); + }, [form, smsInfo]); + + const handleClickModify = () => { + setFormDefaultValue(); + startModify(); + }; + const handleClickCancel = () => { + if (isConfigClosed) form.setFieldValue(switchFieldName, false); + setFormDefaultValue(); + modifyFinish(); + }; + + const onSubmit = (isCodingEnabled: boolean, values?: FormFields) => { + startSubmit(); + baseConfiguration + .UpdateSmsConfiguration({ + update_sms_configuration: { + enable_sms: isCodingEnabled, + url: values?.url, + configuration: values?.token + ? { + token: values?.token ?? '' + } + : undefined + } + }) + .then((res) => { + if (res.data.code === ResponseCode.SUCCESS) { + modifyFinish(); + form.resetFields(); + refreshSmsInfo(); + } + }) + .finally(() => { + submitFinish(); + }); + }; + + const onConfigSwitchPopoverConfirm = () => { + if (!smsInfo?.enable && modifyFlag) { + handleClickCancel(); + hiddenConfigSwitchPopover(); + } else { + onSubmit(false); + } + }; + + const { + configSwitchPopoverOpenState, + generateConfigSwitchPopoverTitle, + onConfigSwitchPopoverOpen, + handleConfigSwitchChange, + hiddenConfigSwitchPopover + } = useConfigSwitchControls(form, switchFieldName); + + const readonlyColumnsConfig: ReadOnlyConfigColumnsType = + useMemo(() => { + return [ + { + label: t('dmsSystem.global.smsSetting.smsType'), + span: 3, + dataIndex: 'sms_type', + hidden: !smsInfo?.enable, + render: (val) => ( + {val || '--'} + ) + }, + { + label: 'url', + span: 3, + dataIndex: 'url', + hidden: !smsInfo?.enable, + render: (val) => ( + {val || '--'} + ) + } + ]; + }, [smsInfo, t]); + + return ( +
+ + {renderConfigForm({ + data: smsInfo ?? {}, + columns: readonlyColumnsConfig, + configExtraButtons: ( + + + + ), + configSwitchNode: ( + + { + handleConfigSwitchChange(open, handleClickModify); + }} + onSwitchPopoverOpen={onConfigSwitchPopoverOpen} + /> + + ), + configField: , + submitButtonField: ( + + ), + onSubmit: (values) => { + onSubmit(enabled, values); + } + })} + +
+ ); +}; + +export default CodingSetting; diff --git a/packages/base/src/page/System/GlobalSetting/SMSSetting/index.type.ts b/packages/base/src/page/System/GlobalSetting/SMSSetting/index.type.ts new file mode 100644 index 000000000..135f8ed78 --- /dev/null +++ b/packages/base/src/page/System/GlobalSetting/SMSSetting/index.type.ts @@ -0,0 +1,5 @@ +export type FormFields = { + enabled: boolean; + url: string; + token: string; +}; diff --git a/packages/base/src/page/System/GlobalSetting/index.tsx b/packages/base/src/page/System/GlobalSetting/index.tsx index 11e7268b9..23db77ea9 100644 --- a/packages/base/src/page/System/GlobalSetting/index.tsx +++ b/packages/base/src/page/System/GlobalSetting/index.tsx @@ -11,6 +11,7 @@ import OperationRecordExpiredHours from './components/OperationRecordExpiredHour import UrlAddressPrefixTips from './components/UrlAddressPrefixTips'; import CBOperationLogsExpiredHours from './components/CBOperationLogsExpiredHours'; import { ConfigFieldMapMeta } from '@actiontech/shared'; +import SMSSetting from './SMSSetting'; const GlobalSetting = () => { const { t } = useTranslation(); @@ -134,6 +135,9 @@ const GlobalSetting = () => { /> + {/* #if [ee] */} + + {/* #endif */} ); }; diff --git a/packages/shared/lib/components/VerificationCodeInput/VerificationCodeInput.tsx b/packages/shared/lib/components/VerificationCodeInput/VerificationCodeInput.tsx new file mode 100644 index 000000000..c155814ee --- /dev/null +++ b/packages/shared/lib/components/VerificationCodeInput/VerificationCodeInput.tsx @@ -0,0 +1,62 @@ +import { BasicInput } from '../BasicInput'; +import { BasicButton } from '../BasicButton'; +import { useCountDown } from 'ahooks'; +import { useState } from 'react'; +import { VerificationCodeInputStyleWrapper } from './style'; +import { useTranslation } from 'react-i18next'; +import { InputProps } from 'antd'; +import { AxiosResponse } from 'axios'; +import { useBoolean } from 'ahooks'; + +interface VerificationCodeFieldProps extends InputProps { + onSendCode?: () => Promise>; + interval?: number; // second +} + +const VerificationCodeInput: React.FC = ({ + onSendCode, + interval = 60, + ...restProps +}) => { + const { t } = useTranslation(); + + const [targetDate, setTargetDate] = useState(); + + const [sending, { setTrue: startSending, setFalse: finishSending }] = + useBoolean(); + + const [countdown] = useCountDown({ + targetDate + }); + + const onSendCodeClick = () => { + startSending(); + onSendCode?.() + .then(() => setTargetDate(Date.now() + interval * 1000)) + .finally(() => finishSending()); + }; + + return ( + + + + {countdown === 0 + ? t('common.verificationCodeInput.sendCode') + : t('common.verificationCodeInput.secondsLater', { + number: Math.round(countdown / 1000) + })} + + + ); +}; + +export default VerificationCodeInput; diff --git a/packages/shared/lib/components/VerificationCodeInput/index.tsx b/packages/shared/lib/components/VerificationCodeInput/index.tsx new file mode 100644 index 000000000..99ea18d33 --- /dev/null +++ b/packages/shared/lib/components/VerificationCodeInput/index.tsx @@ -0,0 +1 @@ +export { default as VerificationCodeInput } from './VerificationCodeInput'; diff --git a/packages/shared/lib/components/VerificationCodeInput/style.ts b/packages/shared/lib/components/VerificationCodeInput/style.ts new file mode 100644 index 000000000..fa81b8f41 --- /dev/null +++ b/packages/shared/lib/components/VerificationCodeInput/style.ts @@ -0,0 +1,10 @@ +import { styled } from '@mui/material/styles'; +import { Space } from 'antd'; + +export const VerificationCodeInputStyleWrapper = styled(Space)` + width: 100%; + + .ant-space-item:first-of-type { + flex: 1; + } +`; diff --git a/packages/shared/lib/components/index.ts b/packages/shared/lib/components/index.ts index 08257b79f..4a96ffaf6 100644 --- a/packages/shared/lib/components/index.ts +++ b/packages/shared/lib/components/index.ts @@ -47,3 +47,4 @@ export * from './SystemConfigurationHub'; export * from './TestDatabaseConnectButton'; export * from './ToggleTokens'; export * from './TypedRouter'; +export * from './VerificationCodeInput'; diff --git a/packages/shared/lib/features/usePermission/permissionManifest.ts b/packages/shared/lib/features/usePermission/permissionManifest.ts index f31f40b8a..e1483a725 100644 --- a/packages/shared/lib/features/usePermission/permissionManifest.ts +++ b/packages/shared/lib/features/usePermission/permissionManifest.ts @@ -386,6 +386,11 @@ export const PERMISSION_MANIFEST: Record< type: 'action', role: [SystemRole.admin, SystemRole.globalManager] }, + [PERMISSIONS.ACTIONS.BASE.SYSTEM.GLOBAL_SETTING.SMS_SERVICE]: { + id: PERMISSIONS.ACTIONS.BASE.SYSTEM.GLOBAL_SETTING.SMS_SERVICE, + type: 'action', + role: [SystemRole.admin, SystemRole.globalManager] + }, [PERMISSIONS.ACTIONS.BASE.SYSTEM.LICENSE.COLLECT_LICENSE]: { id: PERMISSIONS.ACTIONS.BASE.SYSTEM.LICENSE.COLLECT_LICENSE, type: 'action', diff --git a/packages/shared/lib/features/usePermission/permissions.ts b/packages/shared/lib/features/usePermission/permissions.ts index 0f152787c..337109c72 100644 --- a/packages/shared/lib/features/usePermission/permissions.ts +++ b/packages/shared/lib/features/usePermission/permissions.ts @@ -74,7 +74,8 @@ export const PERMISSIONS = { OPERATION_LOG_EXPIRED_HOURS: 'action:operation_log_expired_hours', CB_OPERATION_LOG_EXPIRED_HOURS: 'action:cb_operation_log_expired_hours', - URL_ADDRESS_PREFIX: 'action:url_address_prefix' + URL_ADDRESS_PREFIX: 'action:url_address_prefix', + SMS_SERVICE: 'action:sms_service' }, LICENSE: { COLLECT_LICENSE: 'action:collect_license', diff --git a/packages/shared/lib/locale/zh-CN/common.ts b/packages/shared/lib/locale/zh-CN/common.ts index 870e15486..3c7eb2ce4 100644 --- a/packages/shared/lib/locale/zh-CN/common.ts +++ b/packages/shared/lib/locale/zh-CN/common.ts @@ -73,6 +73,8 @@ export default { success: '成功', fail: '失败', + phone: '手机号', + theme: { light: '明亮模式', dark: '暗黑模式' @@ -256,5 +258,10 @@ export default { eeButtonText: '联系我们', bottomDesc: '各版本完整功能对比请参考:', functionalComparison: '功能对比' + }, + verificationCodeInput: { + code: '验证码', + sendCode: '发送验证码', + secondsLater: '{{number}}秒后重试' } }; diff --git a/packages/shared/lib/utils/Common.ts b/packages/shared/lib/utils/Common.ts index f97b7ae1d..715a9e69d 100644 --- a/packages/shared/lib/utils/Common.ts +++ b/packages/shared/lib/utils/Common.ts @@ -197,3 +197,10 @@ export const paramsSerializer = >(query: T) => { arrayFormat: 'none' }); }; + +export const maskPhoneNumber = (phone: string): string => { + if (!/^\d{11}$/.test(phone)) { + return phone; + } + return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2'); +}; From 69de8c73b15fb5e695544944d48aa2c5494fa3ab Mon Sep 17 00:00:00 2001 From: zzyangh <799463087@qq.com> Date: Fri, 21 Feb 2025 17:21:22 +0800 Subject: [PATCH 3/5] [test]: Update unit testing --- .../Account/__snapshots__/index.test.tsx.snap | 822 +++++++++++ .../base/src/page/Login/index.ce.test.tsx | 9 + packages/base/src/page/Login/index.test.tsx | 17 + .../__snapshots__/index.test.tsx.snap | 1236 +++++++++++++++++ .../page/System/GlobalSetting/index.test.tsx | 3 + .../base/src/testUtils/mockApi/global/data.ts | 6 + .../src/testUtils/mockApi/global/index.ts | 14 +- .../base/src/testUtils/mockApi/system/data.ts | 9 + .../src/testUtils/mockApi/system/index.ts | 35 +- .../__snapshots__/index.test.ts.snap | 9 + 10 files changed, 2158 insertions(+), 2 deletions(-) diff --git a/packages/base/src/page/Account/__snapshots__/index.test.tsx.snap b/packages/base/src/page/Account/__snapshots__/index.test.tsx.snap index 2d7e428b7..392f9743c 100644 --- a/packages/base/src/page/Account/__snapshots__/index.test.tsx.snap +++ b/packages/base/src/page/Account/__snapshots__/index.test.tsx.snap @@ -361,6 +361,272 @@ exports[`test base/page/Account should match snapshot 1`] = ` +
+
+
+
+
+
+
+
+
+
+
+
+ + +
+
+
+
@@ -730,6 +996,272 @@ exports[`test base/page/Account should match snapshot 2`] = ` +
+
+
+
+
+
+
+
+
+
+
+
+ + +
+
+
+
@@ -1447,6 +1979,296 @@ exports[`test base/page/Account should match snapshot when "getUserInfoLoading" +
+
+
+
+ + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+ + +
+
+
+
diff --git a/packages/base/src/page/Login/index.ce.test.tsx b/packages/base/src/page/Login/index.ce.test.tsx index 4b0c1beef..424d3563b 100644 --- a/packages/base/src/page/Login/index.ce.test.tsx +++ b/packages/base/src/page/Login/index.ce.test.tsx @@ -10,6 +10,10 @@ import { superRender } from '../../testUtils/customRender'; import Login from '.'; import { getBySelector } from '@actiontech/shared/lib/testUtil/customQuery'; import { createSpyFailResponse } from '@actiontech/shared/lib/testUtil/mockApi'; +import { + ignoreConsoleErrors, + UtilsConsoleErrorStringsEnum +} from '@actiontech/shared/lib/testUtil/common'; jest.mock('react-redux', () => ({ ...jest.requireActual('react-redux'), @@ -18,6 +22,11 @@ jest.mock('react-redux', () => ({ describe('page/Login-ce', () => { const dispatchSpy = jest.fn(); + + ignoreConsoleErrors([ + UtilsConsoleErrorStringsEnum.UNCONNECTED_FORM_COMPONENT + ]); + const customRender = (params = {}) => { return superRender(, undefined, { initStore: params }); }; diff --git a/packages/base/src/page/Login/index.test.tsx b/packages/base/src/page/Login/index.test.tsx index 08627432a..15d6b3f1b 100644 --- a/packages/base/src/page/Login/index.test.tsx +++ b/packages/base/src/page/Login/index.test.tsx @@ -15,6 +15,10 @@ import { StorageKey } from '@actiontech/shared/lib/enum'; import { OPEN_CLOUD_BEAVER_URL_PARAM_NAME } from '@actiontech/shared/lib/data/routePaths'; +import { + ignoreConsoleErrors, + UtilsConsoleErrorStringsEnum +} from '@actiontech/shared/lib/testUtil/common'; jest.mock('react-router-dom', () => { return { @@ -34,6 +38,10 @@ describe('page/Login-ee', () => { const assignMock = jest.fn(); const useSearchParamsSpy: jest.Mock = useSearchParams as jest.Mock; + ignoreConsoleErrors([ + UtilsConsoleErrorStringsEnum.UNCONNECTED_FORM_COMPONENT + ]); + const customRender = (params = {}) => { return superRender(, undefined, { initStore: params }); }; @@ -104,6 +112,7 @@ describe('page/Login-ee', () => { it('render with other search val', async () => { const requestGetOauth2Tip = dms.getOauth2Tips(); const requestLogin = dms.addSession(); + const verifyUserLoginSpy = dms.verifyUserLogin(); const LocalStorageWrapperSet = jest.spyOn(LocalStorageWrapper, 'set'); useSearchParamsSpy.mockReturnValue([ @@ -131,6 +140,7 @@ describe('page/Login-ee', () => { fireEvent.click(screen.getByText('登 录')); await act(async () => jest.advanceTimersByTime(300)); expect(baseElement).toMatchSnapshot(); + expect(verifyUserLoginSpy).toHaveBeenCalledTimes(1); await act(async () => jest.advanceTimersByTime(3000)); expect(requestLogin).toHaveBeenCalledTimes(1); expect(requestLogin).toHaveBeenCalledWith({ @@ -139,6 +149,7 @@ describe('page/Login-ee', () => { password: 'admin1' } }); + await act(async () => jest.advanceTimersByTime(3000)); expect(dispatchSpy).toHaveBeenCalledTimes(1); expect(dispatchSpy).toHaveBeenCalledWith({ type: 'user/updateToken', @@ -158,6 +169,7 @@ describe('page/Login-ee', () => { it('render with other search val', async () => { const requestGetOauth2Tip = dms.getOauth2Tips(); const requestLogin = dms.addSession(); + const verifyUserLoginSpy = dms.verifyUserLogin(); const LocalStorageWrapperSet = jest.spyOn(LocalStorageWrapper, 'set'); useSearchParamsSpy.mockReturnValue([ @@ -185,6 +197,7 @@ describe('page/Login-ee', () => { fireEvent.click(screen.getByText('登 录')); await act(async () => jest.advanceTimersByTime(300)); expect(baseElement).toMatchSnapshot(); + expect(verifyUserLoginSpy).toHaveBeenCalledTimes(1); await act(async () => jest.advanceTimersByTime(3000)); expect(requestLogin).toHaveBeenCalledTimes(1); expect(requestLogin).toHaveBeenCalledWith({ @@ -193,6 +206,7 @@ describe('page/Login-ee', () => { password: 'admin1' } }); + await act(async () => jest.advanceTimersByTime(3000)); expect(dispatchSpy).toHaveBeenCalledTimes(1); expect(dispatchSpy).toHaveBeenCalledWith({ type: 'user/updateToken', @@ -214,6 +228,7 @@ describe('page/Login-ee', () => { it('render search value with search params', async () => { const requestGetOauth2Tip = dms.getOauth2Tips(); const requestLogin = dms.addSession(); + const verifyUserLoginSpy = dms.verifyUserLogin(); const LocalStorageWrapperSet = jest.spyOn(LocalStorageWrapper, 'set'); useSearchParamsSpy.mockReturnValue([ @@ -241,6 +256,7 @@ describe('page/Login-ee', () => { fireEvent.click(screen.getByText('登 录')); await act(async () => jest.advanceTimersByTime(300)); expect(baseElement).toMatchSnapshot(); + expect(verifyUserLoginSpy).toHaveBeenCalledTimes(1); await act(async () => jest.advanceTimersByTime(3000)); expect(requestLogin).toHaveBeenCalledTimes(1); expect(requestLogin).toHaveBeenCalledWith({ @@ -249,6 +265,7 @@ describe('page/Login-ee', () => { password: 'admin1' } }); + await act(async () => jest.advanceTimersByTime(3000)); expect(navigateSpy).toHaveBeenCalled(); expect(navigateSpy).toHaveBeenCalledWith('/transit?from=cloudbeaver'); expect(LocalStorageWrapperSet).toHaveBeenCalled(); diff --git a/packages/base/src/page/System/GlobalSetting/__snapshots__/index.test.tsx.snap b/packages/base/src/page/System/GlobalSetting/__snapshots__/index.test.tsx.snap index 05d04441f..ed8946f98 100644 --- a/packages/base/src/page/System/GlobalSetting/__snapshots__/index.test.tsx.snap +++ b/packages/base/src/page/System/GlobalSetting/__snapshots__/index.test.tsx.snap @@ -188,6 +188,432 @@ exports[`base/System/GlobalSetting render api return no data 1`] = ` +
+
+
+
+
+
+
+
+
+
+
+
+ + +
+
+ + + + + + + + + +
+
+ + 短信服务类型 + + +
+ webhook +
+
+
+
+
+ + url + + +
+ absc1 +
+
+
+
+
+
+
+
+
+
@@ -405,6 +831,390 @@ exports[`base/System/GlobalSetting render snap 1`] = ` +
+
+
+
+ + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+ + +
+
+
+
@@ -598,6 +1408,432 @@ exports[`base/System/GlobalSetting render snap 2`] = ` +
+
+
+
+
+
+
+
+
+
+
+
+ + +
+
+ + + + + + + + + +
+
+ + 短信服务类型 + + +
+ webhook +
+
+
+
+
+ + url + + +
+ absc1 +
+
+
+
+
+
+
+
+
+
diff --git a/packages/base/src/page/System/GlobalSetting/index.test.tsx b/packages/base/src/page/System/GlobalSetting/index.test.tsx index 9fe5c073a..9c4420233 100644 --- a/packages/base/src/page/System/GlobalSetting/index.test.tsx +++ b/packages/base/src/page/System/GlobalSetting/index.test.tsx @@ -14,6 +14,7 @@ import { mockUseCurrentUser } from '@actiontech/shared/lib/testUtil/mockHook/moc describe('base/System/GlobalSetting', () => { let requestGetSystemVariables: jest.SpyInstance; let requestUpdateSystemVariables: jest.SpyInstance; + let getSmsConfigurationSpy: jest.SpyInstance; const customRender = () => { return superRender(); @@ -24,6 +25,7 @@ describe('base/System/GlobalSetting', () => { jest.useFakeTimers(); requestGetSystemVariables = system.getSystemVariables(); requestUpdateSystemVariables = system.updateSystemVariables(); + getSmsConfigurationSpy = system.getSmsConfiguration(); }); afterEach(() => { @@ -38,6 +40,7 @@ describe('base/System/GlobalSetting', () => { expect(baseElement).toMatchSnapshot(); await act(async () => jest.advanceTimersByTime(2600)); expect(requestGetSystemVariables).toHaveBeenCalled(); + expect(getSmsConfigurationSpy).toHaveBeenCalledTimes(1); expect(baseElement).toMatchSnapshot(); }); diff --git a/packages/base/src/testUtils/mockApi/global/data.ts b/packages/base/src/testUtils/mockApi/global/data.ts index 454445836..98966cd51 100644 --- a/packages/base/src/testUtils/mockApi/global/data.ts +++ b/packages/base/src/testUtils/mockApi/global/data.ts @@ -113,3 +113,9 @@ export const DBServicesList: IListDBService[] = [ enable_backup: false } ]; + +export const mockVerifyLoginData = { + verify_failed_msg: '', + user_uid: '700200', + two_factor_enabled: false +}; diff --git a/packages/base/src/testUtils/mockApi/global/index.ts b/packages/base/src/testUtils/mockApi/global/index.ts index 4d407bfe9..9901396e3 100644 --- a/packages/base/src/testUtils/mockApi/global/index.ts +++ b/packages/base/src/testUtils/mockApi/global/index.ts @@ -8,7 +8,8 @@ import { DBServicesList, GetUserPayload, oauth2Tips, - UserInfo + UserInfo, + mockVerifyLoginData } from './data'; import { mockProjectList } from '../project/data'; import Session from '@actiontech/shared/lib/api/base/service/Session'; @@ -37,6 +38,7 @@ class MockDMSGlobalApi implements MockSpyApy { this.DelDBService(); this.CheckDBServiceIsConnectable(); this.getProjectList(); + this.verifyUserLogin(); } public getCurrentUser() { @@ -186,6 +188,16 @@ class MockDMSGlobalApi implements MockSpyApy { ); return spy; } + + public verifyUserLogin() { + const spy = jest.spyOn(User, 'VerifyUserLogin'); + spy.mockImplementation(() => + createSpySuccessResponse({ + data: mockVerifyLoginData + }) + ); + return spy; + } } export default new MockDMSGlobalApi(); diff --git a/packages/base/src/testUtils/mockApi/system/data.ts b/packages/base/src/testUtils/mockApi/system/data.ts index 1d5d7d568..0e1f17e3b 100644 --- a/packages/base/src/testUtils/mockApi/system/data.ts +++ b/packages/base/src/testUtils/mockApi/system/data.ts @@ -97,3 +97,12 @@ export const mockCodingConfigurationData = { coding_url: 'https://g-izbz2140.coding.net/', is_coding_enabled: true }; + +export const mockSMSConfigurationData = { + enable: true, + url: 'absc1', + sms_type: 'webhook', + configuration: { + token: '1231' + } +}; diff --git a/packages/base/src/testUtils/mockApi/system/index.ts b/packages/base/src/testUtils/mockApi/system/index.ts index e42c415b6..0ff3ca929 100644 --- a/packages/base/src/testUtils/mockApi/system/index.ts +++ b/packages/base/src/testUtils/mockApi/system/index.ts @@ -15,7 +15,8 @@ import { successWechatTestReturn, successWebhookTestReturn, mockModuleRedHotsData, - mockCodingConfigurationData + mockCodingConfigurationData, + mockSMSConfigurationData } from './data'; import configuration from '@actiontech/shared/lib/api/sqle/service/configuration'; import DMSConfiguration from '@actiontech/shared/lib/api/base/service/Configuration'; @@ -61,6 +62,9 @@ class MockSystemApi implements MockSpyApy { this.getCodingConfiguration(); this.updateCodingConfiguration(); this.testCodingConfig(); + this.getSmsConfiguration(); + this.updateSmsConfiguration(); + this.testSmsConfiguration(); } public getLDAPConfig() { @@ -386,6 +390,35 @@ class MockSystemApi implements MockSpyApy { ); return spy; } + + public getSmsConfiguration() { + const spy = jest.spyOn(DMSConfiguration, 'GetSmsConfiguration'); + spy.mockImplementation(() => + createSpySuccessResponse({ + data: mockSMSConfigurationData + }) + ); + return spy; + } + + public updateSmsConfiguration() { + const spy = jest.spyOn(DMSConfiguration, 'UpdateSmsConfiguration'); + spy.mockImplementation(() => createSpySuccessResponse({})); + return spy; + } + + public testSmsConfiguration() { + const spy = jest.spyOn(DMSConfiguration, 'TestSmsConfiguration'); + spy.mockImplementation(() => + createSpySuccessResponse({ + data: { + is_smtp_send_normal: true, + send_error_message: 'ok' + } + }) + ); + return spy; + } } export default new MockSystemApi(); diff --git a/packages/shared/lib/features/usePermission/__tests__/__snapshots__/index.test.ts.snap b/packages/shared/lib/features/usePermission/__tests__/__snapshots__/index.test.ts.snap index 9ba7fc4f0..2c0c340cf 100644 --- a/packages/shared/lib/features/usePermission/__tests__/__snapshots__/index.test.ts.snap +++ b/packages/shared/lib/features/usePermission/__tests__/__snapshots__/index.test.ts.snap @@ -69,6 +69,7 @@ exports[`usePermission should match snapshot 1`] = ` "GLOBAL_SETTING": { "CB_OPERATION_LOG_EXPIRED_HOURS": "action:cb_operation_log_expired_hours", "OPERATION_LOG_EXPIRED_HOURS": "action:operation_log_expired_hours", + "SMS_SERVICE": "action:sms_service", "URL_ADDRESS_PREFIX": "action:url_address_prefix", }, "LICENSE": { @@ -1109,6 +1110,14 @@ exports[`usePermission should match snapshot 2`] = ` "projectArchived": false, "type": "action", }, + "action:sms_service": { + "id": "action:sms_service", + "role": [ + "admin", + "globalManager", + ], + "type": "action", + }, "action:sql_assignment": { "id": "action:sql_assignment", "projectArchived": false, From fe534c182559303051bc6afc082c1236514216f5 Mon Sep 17 00:00:00 2001 From: zzyangh <799463087@qq.com> Date: Fri, 21 Feb 2025 19:03:27 +0800 Subject: [PATCH 4/5] [fix](Login): Code conflicts --- .../src/page/Account/PersonalSMS/index.tsx | 7 +- .../src/page/Login/components/LoginForm.tsx | 64 +++++++++++++++---- .../Login/components/VerificationCodeForm.tsx | 4 +- packages/base/src/page/Login/index.tsx | 7 +- .../System/GlobalSetting/SMSSetting/index.tsx | 31 +++++---- packages/shared/lib/api/base/index.ts | 1 + 6 files changed, 77 insertions(+), 37 deletions(-) diff --git a/packages/base/src/page/Account/PersonalSMS/index.tsx b/packages/base/src/page/Account/PersonalSMS/index.tsx index 18c533cbf..3026e4698 100644 --- a/packages/base/src/page/Account/PersonalSMS/index.tsx +++ b/packages/base/src/page/Account/PersonalSMS/index.tsx @@ -14,8 +14,7 @@ import { useConfigSwitchControls } from '@actiontech/shared'; import { PersonalSMSProps } from './index.type'; -import User from '@actiontech/shared/lib/api/base/service/User'; -import SMS from '@actiontech/shared/lib/api/base/service/SMS'; +import { UserService, SMSService } from '@actiontech/shared/lib/api'; const PersonalSMS: React.FC = ({ userBaseInfo, @@ -66,7 +65,7 @@ const PersonalSMS: React.FC = ({ const onSubmit = async (isCodingEnabled: boolean, values?: FormFields) => { startSubmit(); if (isCodingEnabled) { - const verificationRes = await SMS.VerifySmsCode({ + const verificationRes = await SMSService.VerifySmsCode({ code: values?.code, username: userBaseInfo?.name }); @@ -81,7 +80,7 @@ const PersonalSMS: React.FC = ({ return; } } - User.UpdateCurrentUser({ + UserService.UpdateCurrentUser({ current_user: { two_factor_enabled: isCodingEnabled } diff --git a/packages/base/src/page/Login/components/LoginForm.tsx b/packages/base/src/page/Login/components/LoginForm.tsx index d7512ebab..a9556c47c 100644 --- a/packages/base/src/page/Login/components/LoginForm.tsx +++ b/packages/base/src/page/Login/components/LoginForm.tsx @@ -2,11 +2,15 @@ import { Form, Typography, Space, Checkbox } from 'antd'; import React, { useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { useRequest } from 'ahooks'; -import OAuth2 from '@actiontech/shared/lib/api/base/service/OAuth2'; -import { BasicInput, BasicButton } from '@actiontech/shared'; +import { BasicInput, BasicButton, BasicToolTip } from '@actiontech/shared'; import { LockFilled, UserFilled } from '@actiontech/icons'; import useThemeStyleData from '../../../hooks/useThemeStyleData'; import { LoginFormProps } from '../types'; +import { + ConfigurationService, + OAuth2Service +} from '@actiontech/shared/lib/api'; +import { SystemRole } from '@actiontech/shared/lib/enum'; const LoginForm: React.FC = ({ onSubmit, @@ -16,19 +20,64 @@ const LoginForm: React.FC = ({ }) => { const { t } = useTranslation(); + const username = Form.useWatch('username', form); + const { baseTheme } = useThemeStyleData(); const { run: getOauth2Tips, data: oauthConfig } = useRequest( () => { - return OAuth2.GetOauth2Tips().then((res) => res.data?.data ?? {}); + return OAuth2Service.GetOauth2Tips().then((res) => res.data?.data ?? {}); }, { manual: true } ); + const { run: getLoginBasicConfig, data: loginBasicConfig } = useRequest( + () => { + return ConfigurationService.GetLoginTips().then( + (res) => res.data?.data ?? {} + ); + }, + { + manual: true + } + ); + + const renderLoginButton = () => { + const disabledLoginButton = + username === SystemRole.admin + ? false + : !!loginBasicConfig?.disable_user_pwd_login; + + const loginNode = ( + + {loginBasicConfig?.login_button_text || t('dmsLogin.login')} + + ); + if (disabledLoginButton) { + return ( + + {loginNode} + + ); + } + + return loginNode; + }; + // #if [ee] useEffect(() => { + getLoginBasicConfig(); getOauth2Tips(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -102,14 +151,7 @@ const LoginForm: React.FC = ({ {/* #endif */} - - {t('dmsLogin.login')} - + {renderLoginButton()} {oauthConfig?.enable_oauth2 ? ( {oauthConfig?.login_tip} diff --git a/packages/base/src/page/Login/components/VerificationCodeForm.tsx b/packages/base/src/page/Login/components/VerificationCodeForm.tsx index cbfb4b111..d0c7c69da 100644 --- a/packages/base/src/page/Login/components/VerificationCodeForm.tsx +++ b/packages/base/src/page/Login/components/VerificationCodeForm.tsx @@ -6,7 +6,7 @@ import { import { Form, Typography, Space } from 'antd'; import { useTranslation } from 'react-i18next'; import { VerificationCodeReturnButtonStyleWrapper } from '../style'; -import SMS from '@actiontech/shared/lib/api/base/service/SMS'; +import { SMSService } from '@actiontech/shared/lib/api'; import { VerificationCodeFormProps } from '../types'; const VerificationCodeForm: React.FC = ({ @@ -20,7 +20,7 @@ const VerificationCodeForm: React.FC = ({ const { t } = useTranslation(); const onSendCode = () => { - return SMS.SendSmsCode({ username }); + return SMSService.SendSmsCode({ username }); }; return ( diff --git a/packages/base/src/page/Login/index.tsx b/packages/base/src/page/Login/index.tsx index 1840a203b..e72eda45f 100644 --- a/packages/base/src/page/Login/index.tsx +++ b/packages/base/src/page/Login/index.tsx @@ -2,7 +2,6 @@ import { message, Form } from 'antd'; import { useTranslation } from 'react-i18next'; import { useDispatch } from 'react-redux'; import { updateToken } from '../../store/user'; -import Session from '@actiontech/shared/lib/api/base/service/Session'; import LoginLayout from './components/LoginLayout'; import { useTypedNavigate, useTypedQuery, EmptyBox } from '@actiontech/shared'; import { LoginFormFieldValue, VerificationCodeFormFieldValue } from './types'; @@ -20,7 +19,7 @@ import { } from '@actiontech/shared/lib/data/routePaths'; import LoginForm from './components/LoginForm'; import VerificationCodeForm from './components/VerificationCodeForm'; -import User from '@actiontech/shared/lib/api/base/service/User'; +import { UserService, SessionService } from '@actiontech/shared/lib/api'; import { useState } from 'react'; const Login = () => { @@ -58,7 +57,7 @@ const Login = () => { const addSession = () => { const loginFormValues = loginForm.getFieldsValue(); const verificationCodeFormValues = verificationCodeForm.getFieldsValue(); - Session.AddSession({ + SessionService.AddSession({ session: { username: loginFormValues.username, password: loginFormValues.password, @@ -117,7 +116,7 @@ const Login = () => { // #endif setTrue(); // #if [ee] - User.VerifyUserLogin({ + UserService.VerifyUserLogin({ session: { username: formData.username, password: formData.password diff --git a/packages/base/src/page/System/GlobalSetting/SMSSetting/index.tsx b/packages/base/src/page/System/GlobalSetting/SMSSetting/index.tsx index c48d6b3cd..9d9fa0fe2 100644 --- a/packages/base/src/page/System/GlobalSetting/SMSSetting/index.tsx +++ b/packages/base/src/page/System/GlobalSetting/SMSSetting/index.tsx @@ -5,7 +5,7 @@ import { Spin, Typography } from 'antd'; import { CustomLabelContent } from '@actiontech/shared/lib/components/CustomForm'; import ConfigExtraButtons from './components/ConfigExtraButtons'; import ConfigField from './components/ConfigField'; -import baseConfiguration from '@actiontech/shared/lib/api/base/service/Configuration'; +import { ConfigurationService } from '@actiontech/shared/lib/api'; import { IGetSmsConfigurationReplyItem } from '@actiontech/shared/lib/api/base/service/common'; import { ResponseCode } from '@actiontech/shared/lib/enum'; import { FormFields } from './index.type'; @@ -52,9 +52,9 @@ const CodingSetting: React.FC = () => { refresh: refreshSmsInfo } = useRequest( () => - baseConfiguration - .GetSmsConfiguration() - .then((res) => res.data.data ?? {}), + ConfigurationService.GetSmsConfiguration().then( + (res) => res.data.data ?? {} + ), { onSuccess(res) { if (res) { @@ -89,18 +89,17 @@ const CodingSetting: React.FC = () => { const onSubmit = (isCodingEnabled: boolean, values?: FormFields) => { startSubmit(); - baseConfiguration - .UpdateSmsConfiguration({ - update_sms_configuration: { - enable_sms: isCodingEnabled, - url: values?.url, - configuration: values?.token - ? { - token: values?.token ?? '' - } - : undefined - } - }) + ConfigurationService.UpdateSmsConfiguration({ + update_sms_configuration: { + enable_sms: isCodingEnabled, + url: values?.url, + configuration: values?.token + ? { + token: values?.token ?? '' + } + : undefined + } + }) .then((res) => { if (res.data.code === ResponseCode.SUCCESS) { modifyFinish(); diff --git a/packages/shared/lib/api/base/index.ts b/packages/shared/lib/api/base/index.ts index 8a71803dd..0daaad5de 100644 --- a/packages/shared/lib/api/base/index.ts +++ b/packages/shared/lib/api/base/index.ts @@ -21,3 +21,4 @@ export { default as SessionService } from './service/Session'; export { default as UserService } from './service/User'; export { default as UserGroupService } from './service/UserGroup'; export { default as WebhookService } from './service/Webhook'; +export { default as SMSService } from './service/SMS'; From 01c72d4304c68473fc6a09e42164b1e1f782ee5d Mon Sep 17 00:00:00 2001 From: zzyangh <799463087@qq.com> Date: Fri, 21 Feb 2025 19:05:02 +0800 Subject: [PATCH 5/5] [fix]: Modify API import method --- .../src/page/Account/PersonalSMS/components/ConfigField.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/base/src/page/Account/PersonalSMS/components/ConfigField.tsx b/packages/base/src/page/Account/PersonalSMS/components/ConfigField.tsx index cc83298ad..1fc8e9c1c 100644 --- a/packages/base/src/page/Account/PersonalSMS/components/ConfigField.tsx +++ b/packages/base/src/page/Account/PersonalSMS/components/ConfigField.tsx @@ -2,13 +2,13 @@ import { useTranslation } from 'react-i18next'; import { FormItemLabel } from '@actiontech/shared/lib/components/CustomForm'; import { VerificationCodeInput } from '@actiontech/shared'; import { ConfigFieldProps } from '../index.type'; -import SMS from '@actiontech/shared/lib/api/base/service/SMS'; +import { SMSService } from '@actiontech/shared/lib/api'; const ConfigField: React.FC = ({ userPhone, username }) => { const { t } = useTranslation(); const onSendCode = () => { - return SMS.SendSmsCode({ username }); + return SMSService.SendSmsCode({ username }); }; return (