Compare commits

...

5 Commits

Author SHA1 Message Date
JzoNg 30393043b1 add error handle for email unavailable 10 months ago
JzoNg ae49a2bdd9 add error message of email unavailable 10 months ago
Yansong Zhang c271c65c85 fix ruff liner 10 months ago
Yansong Zhang a1de4fa428 check is email is freeze user email 10 months ago
Yansong Zhang 952bce4196 check is email is freeze user email 10 months ago

@ -113,3 +113,9 @@ class MemberNotInTenantError(BaseHTTPException):
error_code = "member_not_in_tenant" error_code = "member_not_in_tenant"
description = "The member is not in the workspace." description = "The member is not in the workspace."
code = 400 code = 400
class AccountInFreezeError(BaseHTTPException):
error_code = "account_in_freeze"
description = "This email is temporarily unavailable."
code = 400

@ -9,6 +9,7 @@ from configs import dify_config
from constants.languages import supported_language from constants.languages import supported_language
from controllers.console import api from controllers.console import api
from controllers.console.auth.error import ( from controllers.console.auth.error import (
AccountInFreezeError,
EmailAlreadyInUseError, EmailAlreadyInUseError,
EmailChangeLimitError, EmailChangeLimitError,
EmailCodeError, EmailCodeError,
@ -479,15 +480,18 @@ class ChangeEmailResetApi(Resource):
parser.add_argument("token", type=str, required=True, nullable=False, location="json") parser.add_argument("token", type=str, required=True, nullable=False, location="json")
args = parser.parse_args() 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"]) reset_data = AccountService.get_change_email_data(args["token"])
if not reset_data: if not reset_data:
raise InvalidTokenError() raise InvalidTokenError()
AccountService.revoke_change_email_token(args["token"]) AccountService.revoke_change_email_token(args["token"])
if not AccountService.check_email_unique(args["new_email"]):
raise EmailAlreadyInUseError()
old_email = reset_data.get("old_email", "") old_email = reset_data.get("old_email", "")
if current_user.email != old_email: if current_user.email != old_email:
raise AccountNotFound() raise AccountNotFound()
@ -507,6 +511,8 @@ class CheckEmailUnique(Resource):
parser = reqparse.RequestParser() parser = reqparse.RequestParser()
parser.add_argument("email", type=email, required=True, location="json") parser.add_argument("email", type=email, required=True, location="json")
args = parser.parse_args() args = parser.parse_args()
if AccountService.is_account_in_freeze(args["new_email"]):
raise AccountInFreezeError()
if not AccountService.check_email_unique(args["email"]): if not AccountService.check_email_unique(args["email"]):
raise EmailAlreadyInUseError() raise EmailAlreadyInUseError()
return {"result": "success"} return {"result": "success"}

@ -671,6 +671,12 @@ class AccountService:
return account 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 @staticmethod
@redis_fallback(default_return=None) @redis_fallback(default_return=None)
def add_login_error_rate_limit(email: str) -> None: def add_login_error_rate_limit(email: str) -> None:

@ -15,6 +15,8 @@ import {
verifyEmail, verifyEmail,
} from '@/service/common' } from '@/service/common'
import { noop } from 'lodash-es' import { noop } from 'lodash-es'
import { asyncRunSafe } from '@/utils'
import type { ResponseError } from '@/service/fetch'
type Props = { type Props = {
show: boolean show: boolean
@ -39,6 +41,7 @@ const EmailChangeModal = ({ onClose, email, show }: Props) => {
const [time, setTime] = useState<number>(0) const [time, setTime] = useState<number>(0)
const [stepToken, setStepToken] = useState<string>('') const [stepToken, setStepToken] = useState<string>('')
const [newEmailExited, setNewEmailExited] = useState<boolean>(false) const [newEmailExited, setNewEmailExited] = useState<boolean>(false)
const [unAvailableEmail, setUnAvailableEmail] = useState<boolean>(false)
const [isCheckingEmail, setIsCheckingEmail] = useState<boolean>(false) const [isCheckingEmail, setIsCheckingEmail] = useState<boolean>(false)
const startCount = () => { const startCount = () => {
@ -124,9 +127,17 @@ const EmailChangeModal = ({ onClose, email, show }: Props) => {
email, email,
}) })
setNewEmailExited(false) setNewEmailExited(false)
setUnAvailableEmail(false)
} }
catch { catch (e: any) {
if (e.status === 400) {
const [, errRespData] = await asyncRunSafe<ResponseError>(e.json())
const { code } = errRespData || {}
if (code === 'email_already_in_use')
setNewEmailExited(true) setNewEmailExited(true)
if (code === 'account_in_freeze')
setUnAvailableEmail(true)
}
} }
finally { finally {
setIsCheckingEmail(false) setIsCheckingEmail(false)
@ -291,15 +302,18 @@ const EmailChangeModal = ({ onClose, email, show }: Props) => {
placeholder={t('common.account.changeEmail.emailPlaceholder')} placeholder={t('common.account.changeEmail.emailPlaceholder')}
value={mail} value={mail}
onChange={e => handleNewEmailValueChange(e.target.value)} onChange={e => handleNewEmailValueChange(e.target.value)}
destructive={newEmailExited} destructive={newEmailExited || unAvailableEmail}
/> />
{newEmailExited && ( {newEmailExited && (
<div className='body-xs-regular mt-1 py-0.5 text-text-destructive'>{t('common.account.changeEmail.existingEmail')}</div> <div className='body-xs-regular mt-1 py-0.5 text-text-destructive'>{t('common.account.changeEmail.existingEmail')}</div>
)} )}
{unAvailableEmail && (
<div className='body-xs-regular mt-1 py-0.5 text-text-destructive'>{t('common.account.changeEmail.unAvailableEmail')}</div>
)}
</div> </div>
<div className='mt-3 space-y-2'> <div className='mt-3 space-y-2'>
<Button <Button
disabled={!mail || newEmailExited || isCheckingEmail || !isValidEmail(mail)} disabled={!mail || newEmailExited || unAvailableEmail || isCheckingEmail || !isValidEmail(mail)}
className='!w-full' className='!w-full'
variant='primary' variant='primary'
onClick={sendCodeToNewEmail} onClick={sendCodeToNewEmail}

@ -248,6 +248,7 @@ const translation = {
emailLabel: 'New email', emailLabel: 'New email',
emailPlaceholder: 'Enter a new email', emailPlaceholder: 'Enter a new email',
existingEmail: 'A user with this email already exists.', existingEmail: 'A user with this email already exists.',
unAvailableEmail: 'This email is temporarily unavailable.',
sendVerifyCode: 'Send verification code', sendVerifyCode: 'Send verification code',
continue: 'Continue', continue: 'Continue',
changeTo: 'Change to {{email}}', changeTo: 'Change to {{email}}',

@ -249,6 +249,7 @@ const translation = {
emailLabel: '新しいメール', emailLabel: '新しいメール',
emailPlaceholder: '新しいメールを入力してください', emailPlaceholder: '新しいメールを入力してください',
existingEmail: 'このメールアドレスのユーザーは既に存在します', existingEmail: 'このメールアドレスのユーザーは既に存在します',
unAvailableEmail: 'このメールアドレスは現在使用できません。',
sendVerifyCode: '確認コードを送信', sendVerifyCode: '確認コードを送信',
continue: '続行', continue: '続行',
changeTo: '{{email}} に変更', changeTo: '{{email}} に変更',

@ -248,6 +248,7 @@ const translation = {
emailLabel: '新邮箱', emailLabel: '新邮箱',
emailPlaceholder: '输入新邮箱', emailPlaceholder: '输入新邮箱',
existingEmail: '该邮箱已存在', existingEmail: '该邮箱已存在',
unAvailableEmail: '该邮箱暂时无法使用。',
sendVerifyCode: '发送验证码', sendVerifyCode: '发送验证码',
continue: '继续', continue: '继续',
changeTo: '更改为 {{email}}', changeTo: '更改为 {{email}}',

Loading…
Cancel
Save