From 952bce4196cca3ad6b2fe6cb276420dda9d270ab Mon Sep 17 00:00:00 2001 From: Yansong Zhang <916125788@qq.com> Date: Thu, 24 Jul 2025 15:26:35 +0800 Subject: [PATCH 1/5] check is email is freeze user email --- api/controllers/console/auth/error.py | 5 +++++ api/controllers/console/workspace/account.py | 4 ++++ api/services/account_service.py | 6 ++++++ 3 files changed, 15 insertions(+) diff --git a/api/controllers/console/auth/error.py b/api/controllers/console/auth/error.py index 8c5e23de58..7f5bfa8f45 100644 --- a/api/controllers/console/auth/error.py +++ b/api/controllers/console/auth/error.py @@ -113,3 +113,8 @@ class MemberNotInTenantError(BaseHTTPException): error_code = "member_not_in_tenant" description = "The member is not in the workspace." code = 400 + +class AccountInFreezeError(BaseHTTPException): + error_code = "account_in_freeze" + description = "This email is temporarily unavailable." + code = 400 \ No newline at end of file diff --git a/api/controllers/console/workspace/account.py b/api/controllers/console/workspace/account.py index 657016e0a8..961ca202c4 100644 --- a/api/controllers/console/workspace/account.py +++ b/api/controllers/console/workspace/account.py @@ -14,6 +14,7 @@ from controllers.console.auth.error import ( EmailCodeError, InvalidEmailError, InvalidTokenError, + AccountInFreezeError ) from controllers.console.error import AccountNotFound, EmailSendIpLimitError from controllers.console.workspace.error import ( @@ -488,6 +489,9 @@ class ChangeEmailResetApi(Resource): if not AccountService.check_email_unique(args["new_email"]): raise EmailAlreadyInUseError() + if AccountService.is_account_in_freeze(args["new_email"]): + raise AccountInFreezeError() + old_email = reset_data.get("old_email", "") if current_user.email != old_email: raise AccountNotFound() diff --git a/api/services/account_service.py b/api/services/account_service.py index eb57b675c4..e11f1580e5 100644 --- a/api/services/account_service.py +++ b/api/services/account_service.py @@ -671,6 +671,12 @@ class AccountService: return account + @classmethod + def is_account_in_freeze(cls, email: str) -> bool: + if dify_config.BILLING_ENABLED and BillingService.is_email_in_freeze(email): + return True + return False + @staticmethod @redis_fallback(default_return=None) def add_login_error_rate_limit(email: str) -> None: From a1de4fa4286f321e1dd93f5aaa3ff6a63161130b Mon Sep 17 00:00:00 2001 From: Yansong Zhang <916125788@qq.com> Date: Thu, 24 Jul 2025 15:26:41 +0800 Subject: [PATCH 2/5] check is email is freeze user email --- api/controllers/console/workspace/account.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/api/controllers/console/workspace/account.py b/api/controllers/console/workspace/account.py index 961ca202c4..fb16850540 100644 --- a/api/controllers/console/workspace/account.py +++ b/api/controllers/console/workspace/account.py @@ -9,12 +9,12 @@ from configs import dify_config from constants.languages import supported_language from controllers.console import api from controllers.console.auth.error import ( + AccountInFreezeError, EmailAlreadyInUseError, EmailChangeLimitError, EmailCodeError, InvalidEmailError, InvalidTokenError, - AccountInFreezeError ) from controllers.console.error import AccountNotFound, EmailSendIpLimitError from controllers.console.workspace.error import ( @@ -480,17 +480,19 @@ class ChangeEmailResetApi(Resource): parser.add_argument("token", type=str, required=True, nullable=False, location="json") args = parser.parse_args() + if AccountService.is_account_in_freeze(args["new_email"]): + raise AccountInFreezeError() + + if not AccountService.check_email_unique(args["new_email"]): + raise EmailAlreadyInUseError() + reset_data = AccountService.get_change_email_data(args["token"]) if not reset_data: raise InvalidTokenError() AccountService.revoke_change_email_token(args["token"]) - if not AccountService.check_email_unique(args["new_email"]): - raise EmailAlreadyInUseError() - if AccountService.is_account_in_freeze(args["new_email"]): - raise AccountInFreezeError() old_email = reset_data.get("old_email", "") if current_user.email != old_email: From c271c65c854f166914842f84a71c6655c4761d72 Mon Sep 17 00:00:00 2001 From: Yansong Zhang <916125788@qq.com> Date: Thu, 24 Jul 2025 15:29:23 +0800 Subject: [PATCH 3/5] fix ruff liner --- api/controllers/console/auth/error.py | 3 ++- api/controllers/console/workspace/account.py | 4 +--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/api/controllers/console/auth/error.py b/api/controllers/console/auth/error.py index 7f5bfa8f45..1984339add 100644 --- a/api/controllers/console/auth/error.py +++ b/api/controllers/console/auth/error.py @@ -114,7 +114,8 @@ class MemberNotInTenantError(BaseHTTPException): description = "The member is not in the workspace." code = 400 + class AccountInFreezeError(BaseHTTPException): error_code = "account_in_freeze" description = "This email is temporarily unavailable." - code = 400 \ No newline at end of file + code = 400 diff --git a/api/controllers/console/workspace/account.py b/api/controllers/console/workspace/account.py index fb16850540..9218ddf91d 100644 --- a/api/controllers/console/workspace/account.py +++ b/api/controllers/console/workspace/account.py @@ -485,15 +485,13 @@ class ChangeEmailResetApi(Resource): if not AccountService.check_email_unique(args["new_email"]): raise EmailAlreadyInUseError() - + reset_data = AccountService.get_change_email_data(args["token"]) if not reset_data: raise InvalidTokenError() AccountService.revoke_change_email_token(args["token"]) - - old_email = reset_data.get("old_email", "") if current_user.email != old_email: raise AccountNotFound() From ae49a2bdd9d833c3b5ed3a15aa1529ed38ac6dc1 Mon Sep 17 00:00:00 2001 From: JzoNg Date: Thu, 24 Jul 2025 16:11:07 +0800 Subject: [PATCH 4/5] add error message of email unavailable --- .../account-page/email-change-modal.tsx | 22 +++++++++++++++---- web/i18n/en-US/common.ts | 1 + web/i18n/ja-JP/common.ts | 1 + web/i18n/zh-Hans/common.ts | 1 + 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/web/app/account/account-page/email-change-modal.tsx b/web/app/account/account-page/email-change-modal.tsx index c3efad104a..bd00f27ac5 100644 --- a/web/app/account/account-page/email-change-modal.tsx +++ b/web/app/account/account-page/email-change-modal.tsx @@ -15,6 +15,8 @@ import { verifyEmail, } from '@/service/common' import { noop } from 'lodash-es' +import { asyncRunSafe } from '@/utils' +import type { ResponseError } from '@/service/fetch' type Props = { show: boolean @@ -39,6 +41,7 @@ const EmailChangeModal = ({ onClose, email, show }: Props) => { const [time, setTime] = useState(0) const [stepToken, setStepToken] = useState('') const [newEmailExited, setNewEmailExited] = useState(false) + const [unAvailableEmail, setUnAvailableEmail] = useState(false) const [isCheckingEmail, setIsCheckingEmail] = useState(false) const startCount = () => { @@ -124,9 +127,17 @@ const EmailChangeModal = ({ onClose, email, show }: Props) => { email, }) setNewEmailExited(false) + setUnAvailableEmail(false) } - catch { - setNewEmailExited(true) + catch (e: any) { + if (e.status === 400) { + const [, errRespData] = await asyncRunSafe(e.json()) + const { code } = errRespData || {} + if (code === 'email_already_in_use') + setNewEmailExited(true) + if (code === 'account_in_freeze') + setUnAvailableEmail(true) + } } finally { setIsCheckingEmail(false) @@ -291,15 +302,18 @@ const EmailChangeModal = ({ onClose, email, show }: Props) => { placeholder={t('common.account.changeEmail.emailPlaceholder')} value={mail} onChange={e => handleNewEmailValueChange(e.target.value)} - destructive={newEmailExited} + destructive={newEmailExited || unAvailableEmail} /> {newEmailExited && (
{t('common.account.changeEmail.existingEmail')}
)} + {unAvailableEmail && ( +
{t('common.account.changeEmail.unAvailableEmail')}
+ )}