diff --git a/api/.env.example b/api/.env.example index 2986402e9e..8d783af134 100644 --- a/api/.env.example +++ b/api/.env.example @@ -530,7 +530,6 @@ ENDPOINT_URL_TEMPLATE=http://localhost:5002/e/{hook_id} # Reset password token expiry minutes RESET_PASSWORD_TOKEN_EXPIRY_MINUTES=5 -EMAIL_REGISTER_TOKEN_EXPIRY_MINUTES=5 CHANGE_EMAIL_TOKEN_EXPIRY_MINUTES=5 OWNER_TRANSFER_TOKEN_EXPIRY_MINUTES=5 diff --git a/api/configs/feature/__init__.py b/api/configs/feature/__init__.py index 0d6f4e416e..899fecea7c 100644 --- a/api/configs/feature/__init__.py +++ b/api/configs/feature/__init__.py @@ -31,12 +31,6 @@ class SecurityConfig(BaseSettings): description="Duration in minutes for which a password reset token remains valid", default=5, ) - - EMAIL_REGISTER_TOKEN_EXPIRY_MINUTES: PositiveInt = Field( - description="Duration in minutes for which a email register token remains valid", - default=5, - ) - CHANGE_EMAIL_TOKEN_EXPIRY_MINUTES: PositiveInt = Field( description="Duration in minutes for which a change email token remains valid", default=5, @@ -645,11 +639,6 @@ class AuthConfig(BaseSettings): default=86400, ) - EMAIL_REGISTER_LOCKOUT_DURATION: PositiveInt = Field( - description="Time (in seconds) a user must wait before retrying email register after exceeding the rate limit.", - default=86400, - ) - class ModerationConfig(BaseSettings): """ diff --git a/api/controllers/console/__init__.py b/api/controllers/console/__init__.py index 9634f3ca17..5ad7645969 100644 --- a/api/controllers/console/__init__.py +++ b/api/controllers/console/__init__.py @@ -70,16 +70,7 @@ from .app import ( ) # Import auth controllers -from .auth import ( - activate, - data_source_bearer_auth, - data_source_oauth, - email_register, - forgot_password, - login, - oauth, - oauth_server, -) +from .auth import activate, data_source_bearer_auth, data_source_oauth, forgot_password, login, oauth, oauth_server # Import billing controllers from .billing import billing, compliance diff --git a/api/controllers/console/auth/email_register.py b/api/controllers/console/auth/email_register.py deleted file mode 100644 index 458e70c8de..0000000000 --- a/api/controllers/console/auth/email_register.py +++ /dev/null @@ -1,154 +0,0 @@ -from flask import request -from flask_restx import Resource, reqparse -from sqlalchemy import select -from sqlalchemy.orm import Session - -from constants.languages import languages -from controllers.console import api -from controllers.console.auth.error import ( - EmailAlreadyInUseError, - EmailCodeError, - EmailRegisterLimitError, - InvalidEmailError, - InvalidTokenError, - PasswordMismatchError, -) -from controllers.console.error import AccountInFreezeError, EmailSendIpLimitError -from controllers.console.wraps import email_password_login_enabled, email_register_enabled, setup_required -from extensions.ext_database import db -from libs.helper import email, extract_remote_ip -from libs.password import valid_password -from models.account import Account -from services.account_service import AccountService -from services.errors.account import AccountRegisterError -from services.errors.workspace import WorkSpaceNotAllowedCreateError, WorkspacesLimitExceededError - - -class EmailRegisterSendEmailApi(Resource): - @setup_required - @email_password_login_enabled - @email_register_enabled - def post(self): - parser = reqparse.RequestParser() - parser.add_argument("email", type=email, required=True, location="json") - parser.add_argument("language", type=str, required=False, location="json") - args = parser.parse_args() - - ip_address = extract_remote_ip(request) - if AccountService.is_email_send_ip_limit(ip_address): - raise EmailSendIpLimitError() - - if args["language"] is not None and args["language"] == "zh-Hans": - language = "zh-Hans" - else: - language = "en-US" - - with Session(db.engine) as session: - account = session.execute(select(Account).filter_by(email=args["email"])).scalar_one_or_none() - token = None - token = AccountService.send_email_register_email(email=args["email"], account=account, language=language) - return {"result": "success", "data": token} - - -class EmailRegisterCheckApi(Resource): - @setup_required - @email_password_login_enabled - @email_register_enabled - def post(self): - parser = reqparse.RequestParser() - parser.add_argument("email", type=str, required=True, location="json") - parser.add_argument("code", type=str, required=True, location="json") - parser.add_argument("token", type=str, required=True, nullable=False, location="json") - args = parser.parse_args() - - user_email = args["email"] - - is_email_register_error_rate_limit = AccountService.is_email_register_error_rate_limit(args["email"]) - if is_email_register_error_rate_limit: - raise EmailRegisterLimitError() - - token_data = AccountService.get_email_register_data(args["token"]) - if token_data is None: - raise InvalidTokenError() - - if user_email != token_data.get("email"): - raise InvalidEmailError() - - if args["code"] != token_data.get("code"): - AccountService.add_email_register_error_rate_limit(args["email"]) - raise EmailCodeError() - - # Verified, revoke the first token - AccountService.revoke_email_register_token(args["token"]) - - # Refresh token data by generating a new token - _, new_token = AccountService.generate_email_register_token( - user_email, code=args["code"], additional_data={"phase": "register"} - ) - - AccountService.reset_email_register_error_rate_limit(args["email"]) - return {"is_valid": True, "email": token_data.get("email"), "token": new_token} - - -class EmailRegisterResetApi(Resource): - @setup_required - @email_password_login_enabled - @email_register_enabled - def post(self): - parser = reqparse.RequestParser() - parser.add_argument("token", type=str, required=True, nullable=False, location="json") - parser.add_argument("new_password", type=valid_password, required=True, nullable=False, location="json") - parser.add_argument("password_confirm", type=valid_password, required=True, nullable=False, location="json") - args = parser.parse_args() - - # Validate passwords match - if args["new_password"] != args["password_confirm"]: - raise PasswordMismatchError() - - # Validate token and get register data - register_data = AccountService.get_email_register_data(args["token"]) - if not register_data: - raise InvalidTokenError() - # Must use token in reset phase - if register_data.get("phase", "") != "register": - raise InvalidTokenError() - - # Revoke token to prevent reuse - AccountService.revoke_email_register_token(args["token"]) - - email = register_data.get("email", "") - - with Session(db.engine) as session: - account = session.execute(select(Account).filter_by(email=email)).scalar_one_or_none() - - if account: - raise EmailAlreadyInUseError() - else: - account = self._create_new_account(email, args["password_confirm"]) - token_pair = AccountService.login(account=account, ip_address=extract_remote_ip(request)) - AccountService.reset_login_error_rate_limit(email) - - return {"result": "success", "data": token_pair.model_dump()} - - def _create_new_account(self, email, password): - # Create new account if allowed - try: - account = AccountService.create_account_and_tenant( - email=email, - name=email, - password=password, - interface_language=languages[0], - ) - except WorkSpaceNotAllowedCreateError: - pass - except WorkspacesLimitExceededError: - pass - except AccountRegisterError: - raise AccountInFreezeError() - - return account - - -api.add_resource(EmailRegisterSendEmailApi, "/email-register/send-email") -api.add_resource(EmailRegisterCheckApi, "/email-register/validity") -api.add_resource(EmailRegisterResetApi, "/email-register") diff --git a/api/controllers/console/auth/error.py b/api/controllers/console/auth/error.py index 9cda8c90b1..7853bef917 100644 --- a/api/controllers/console/auth/error.py +++ b/api/controllers/console/auth/error.py @@ -31,12 +31,6 @@ class PasswordResetRateLimitExceededError(BaseHTTPException): code = 429 -class EmailRegisterRateLimitExceededError(BaseHTTPException): - error_code = "email_register_rate_limit_exceeded" - description = "Too many email register emails have been sent. Please try again in 1 minute." - code = 429 - - class EmailChangeRateLimitExceededError(BaseHTTPException): error_code = "email_change_rate_limit_exceeded" description = "Too many email change emails have been sent. Please try again in 1 minute." @@ -91,12 +85,6 @@ class EmailPasswordResetLimitError(BaseHTTPException): code = 429 -class EmailRegisterLimitError(BaseHTTPException): - error_code = "email_register_limit" - description = "Too many failed email register attempts. Please try again in 24 hours." - code = 429 - - class EmailChangeLimitError(BaseHTTPException): error_code = "email_change_limit" description = "Too many failed email change attempts. Please try again in 24 hours." diff --git a/api/controllers/console/auth/forgot_password.py b/api/controllers/console/auth/forgot_password.py index d7558e0f67..ede0696854 100644 --- a/api/controllers/console/auth/forgot_password.py +++ b/api/controllers/console/auth/forgot_password.py @@ -6,6 +6,7 @@ from flask_restx import Resource, reqparse from sqlalchemy import select from sqlalchemy.orm import Session +from constants.languages import languages from controllers.console import api from controllers.console.auth.error import ( EmailCodeError, @@ -14,7 +15,7 @@ from controllers.console.auth.error import ( InvalidTokenError, PasswordMismatchError, ) -from controllers.console.error import AccountNotFound, EmailSendIpLimitError +from controllers.console.error import AccountInFreezeError, AccountNotFound, EmailSendIpLimitError from controllers.console.wraps import email_password_login_enabled, setup_required from events.tenant_event import tenant_was_created from extensions.ext_database import db @@ -22,6 +23,8 @@ from libs.helper import email, extract_remote_ip from libs.password import hash_password, valid_password from models.account import Account from services.account_service import AccountService, TenantService +from services.errors.account import AccountRegisterError +from services.errors.workspace import WorkSpaceNotAllowedCreateError, WorkspacesLimitExceededError from services.feature_service import FeatureService @@ -45,13 +48,15 @@ class ForgotPasswordSendEmailApi(Resource): with Session(db.engine) as session: account = session.execute(select(Account).filter_by(email=args["email"])).scalar_one_or_none() - - token = AccountService.send_reset_password_email( - account=account, - email=args["email"], - language=language, - is_allow_register=FeatureService.get_system_features().is_allow_register, - ) + token = None + if account is None: + if FeatureService.get_system_features().is_allow_register: + token = AccountService.send_reset_password_email(email=args["email"], language=language) + return {"result": "fail", "data": token, "code": "account_not_found"} + else: + raise AccountNotFound() + else: + token = AccountService.send_reset_password_email(account=account, email=args["email"], language=language) return {"result": "success", "data": token} @@ -132,7 +137,7 @@ class ForgotPasswordResetApi(Resource): if account: self._update_existing_account(account, password_hashed, salt, session) else: - raise AccountNotFound() + self._create_new_account(email, args["password_confirm"]) return {"result": "success"} @@ -152,6 +157,22 @@ class ForgotPasswordResetApi(Resource): account.current_tenant = tenant tenant_was_created.send(tenant) + def _create_new_account(self, email, password): + # Create new account if allowed + try: + AccountService.create_account_and_tenant( + email=email, + name=email, + password=password, + interface_language=languages[0], + ) + except WorkSpaceNotAllowedCreateError: + pass + except WorkspacesLimitExceededError: + pass + except AccountRegisterError: + raise AccountInFreezeError() + api.add_resource(ForgotPasswordSendEmailApi, "/forgot-password") api.add_resource(ForgotPasswordCheckApi, "/forgot-password/validity") diff --git a/api/controllers/console/auth/login.py b/api/controllers/console/auth/login.py index 3b35ab3c23..b11bc0c6ac 100644 --- a/api/controllers/console/auth/login.py +++ b/api/controllers/console/auth/login.py @@ -26,6 +26,7 @@ from controllers.console.error import ( from controllers.console.wraps import email_password_login_enabled, setup_required from events.tenant_event import tenant_was_created from libs.helper import email, extract_remote_ip +from libs.password import valid_password from models.account import Account from services.account_service import AccountService, RegisterService, TenantService from services.billing_service import BillingService @@ -43,9 +44,10 @@ class LoginApi(Resource): """Authenticate user and login.""" parser = reqparse.RequestParser() parser.add_argument("email", type=email, required=True, location="json") - parser.add_argument("password", type=str, required=True, location="json") + parser.add_argument("password", type=valid_password, required=True, location="json") parser.add_argument("remember_me", type=bool, required=False, default=False, location="json") parser.add_argument("invite_token", type=str, required=False, default=None, location="json") + parser.add_argument("language", type=str, required=False, default="en-US", location="json") args = parser.parse_args() if dify_config.BILLING_ENABLED and BillingService.is_email_in_freeze(args["email"]): @@ -59,6 +61,11 @@ class LoginApi(Resource): if invitation: invitation = RegisterService.get_invitation_if_token_valid(None, args["email"], invitation) + if args["language"] is not None and args["language"] == "zh-Hans": + language = "zh-Hans" + else: + language = "en-US" + try: if invitation: data = invitation.get("data", {}) @@ -73,6 +80,12 @@ class LoginApi(Resource): except services.errors.account.AccountPasswordError: AccountService.add_login_error_rate_limit(args["email"]) raise AuthenticationFailedError() + except services.errors.account.AccountNotFoundError: + if FeatureService.get_system_features().is_allow_register: + token = AccountService.send_reset_password_email(email=args["email"], language=language) + return {"result": "fail", "data": token, "code": "account_not_found"} + else: + raise AccountNotFound() # SELF_HOSTED only have one workspace tenants = TenantService.get_join_tenants(account) if len(tenants) == 0: @@ -120,12 +133,13 @@ class ResetPasswordSendEmailApi(Resource): except AccountRegisterError: raise AccountInFreezeError() - token = AccountService.send_reset_password_email( - email=args["email"], - account=account, - language=language, - is_allow_register=FeatureService.get_system_features().is_allow_register, - ) + if account is None: + if FeatureService.get_system_features().is_allow_register: + token = AccountService.send_reset_password_email(email=args["email"], language=language) + else: + raise AccountNotFound() + else: + token = AccountService.send_reset_password_email(account=account, language=language) return {"result": "success", "data": token} diff --git a/api/controllers/console/wraps.py b/api/controllers/console/wraps.py index 092071481e..e375fe285b 100644 --- a/api/controllers/console/wraps.py +++ b/api/controllers/console/wraps.py @@ -242,19 +242,6 @@ def email_password_login_enabled(view: Callable[P, R]): return decorated -def email_register_enabled(view): - @wraps(view) - def decorated(*args, **kwargs): - features = FeatureService.get_system_features() - if features.is_allow_register: - return view(*args, **kwargs) - - # otherwise, return 403 - abort(403) - - return decorated - - def enable_change_email(view: Callable[P, R]): @wraps(view) def decorated(*args: P.args, **kwargs: P.kwargs): diff --git a/api/libs/email_i18n.py b/api/libs/email_i18n.py index 9dde87d800..3c039dff53 100644 --- a/api/libs/email_i18n.py +++ b/api/libs/email_i18n.py @@ -21,7 +21,6 @@ class EmailType(Enum): """Enumeration of supported email types.""" RESET_PASSWORD = "reset_password" - RESET_PASSWORD_WHEN_ACCOUNT_NOT_EXIST = "reset_password_when_account_not_exist" INVITE_MEMBER = "invite_member" EMAIL_CODE_LOGIN = "email_code_login" CHANGE_EMAIL_OLD = "change_email_old" @@ -35,9 +34,6 @@ class EmailType(Enum): ENTERPRISE_CUSTOM = "enterprise_custom" QUEUE_MONITOR_ALERT = "queue_monitor_alert" DOCUMENT_CLEAN_NOTIFY = "document_clean_notify" - EMAIL_REGISTER = "email_register" - EMAIL_REGISTER_WHEN_ACCOUNT_EXIST = "email_register_when_account_exist" - RESET_PASSWORD_WHEN_ACCOUNT_NOT_EXIST_NO_REGISTER = "reset_password_when_account_not_exist_no_register" class EmailLanguage(Enum): @@ -445,54 +441,6 @@ def create_default_email_config() -> EmailI18nConfig: branded_template_path="clean_document_job_mail_template_zh-CN.html", ), }, - EmailType.EMAIL_REGISTER: { - EmailLanguage.EN_US: EmailTemplate( - subject="Register Your {application_title} Account", - template_path="register_email_template_en-US.html", - branded_template_path="without-brand/register_email_template_en-US.html", - ), - EmailLanguage.ZH_HANS: EmailTemplate( - subject="注册您的 {application_title} 账户", - template_path="register_email_template_zh-CN.html", - branded_template_path="without-brand/register_email_template_zh-CN.html", - ), - }, - EmailType.EMAIL_REGISTER_WHEN_ACCOUNT_EXIST: { - EmailLanguage.EN_US: EmailTemplate( - subject="Register Your {application_title} Account", - template_path="register_email_when_account_exist_template_en-US.html", - branded_template_path="without-brand/register_email_when_account_exist_template_en-US.html", - ), - EmailLanguage.ZH_HANS: EmailTemplate( - subject="注册您的 {application_title} 账户", - template_path="register_email_when_account_exist_template_zh-CN.html", - branded_template_path="without-brand/register_email_when_account_exist_template_zh-CN.html", - ), - }, - EmailType.RESET_PASSWORD_WHEN_ACCOUNT_NOT_EXIST: { - EmailLanguage.EN_US: EmailTemplate( - subject="Reset Your {application_title} Password", - template_path="reset_password_mail_when_account_not_exist_template_en-US.html", - branded_template_path="without-brand/reset_password_mail_when_account_not_exist_template_en-US.html", - ), - EmailLanguage.ZH_HANS: EmailTemplate( - subject="重置您的 {application_title} 密码", - template_path="reset_password_mail_when_account_not_exist_template_zh-CN.html", - branded_template_path="without-brand/reset_password_mail_when_account_not_exist_template_zh-CN.html", - ), - }, - EmailType.RESET_PASSWORD_WHEN_ACCOUNT_NOT_EXIST_NO_REGISTER: { - EmailLanguage.EN_US: EmailTemplate( - subject="Reset Your {application_title} Password", - template_path="reset_password_mail_when_account_not_exist_no_register_template_en-US.html", - branded_template_path="without-brand/reset_password_mail_when_account_not_exist_no_register_template_en-US.html", - ), - EmailLanguage.ZH_HANS: EmailTemplate( - subject="重置您的 {application_title} 密码", - template_path="reset_password_mail_when_account_not_exist_no_register_template_zh-CN.html", - branded_template_path="without-brand/reset_password_mail_when_account_not_exist_no_register_template_zh-CN.html", - ), - }, } return EmailI18nConfig(templates=templates) diff --git a/api/services/account_service.py b/api/services/account_service.py index 8438423f2e..a76792f88e 100644 --- a/api/services/account_service.py +++ b/api/services/account_service.py @@ -37,6 +37,7 @@ from services.billing_service import BillingService from services.errors.account import ( AccountAlreadyInTenantError, AccountLoginError, + AccountNotFoundError, AccountNotLinkTenantError, AccountPasswordError, AccountRegisterError, @@ -64,11 +65,7 @@ from tasks.mail_owner_transfer_task import ( send_old_owner_transfer_notify_email_task, send_owner_transfer_confirm_task, ) -from tasks.mail_register_task import send_email_register_mail_task, send_email_register_mail_task_when_account_exist -from tasks.mail_reset_password_task import ( - send_reset_password_mail_task, - send_reset_password_mail_task_when_account_not_exist, -) +from tasks.mail_reset_password_task import send_reset_password_mail_task logger = logging.getLogger(__name__) @@ -85,7 +82,6 @@ REFRESH_TOKEN_EXPIRY = timedelta(days=dify_config.REFRESH_TOKEN_EXPIRE_DAYS) class AccountService: reset_password_rate_limiter = RateLimiter(prefix="reset_password_rate_limit", max_attempts=1, time_window=60 * 1) - email_register_rate_limiter = RateLimiter(prefix="email_register_rate_limit", max_attempts=1, time_window=60 * 1) email_code_login_rate_limiter = RateLimiter( prefix="email_code_login_rate_limit", max_attempts=1, time_window=60 * 1 ) @@ -99,7 +95,6 @@ class AccountService: FORGOT_PASSWORD_MAX_ERROR_LIMITS = 5 CHANGE_EMAIL_MAX_ERROR_LIMITS = 5 OWNER_TRANSFER_MAX_ERROR_LIMITS = 5 - EMAIL_REGISTER_MAX_ERROR_LIMITS = 5 @staticmethod def _get_refresh_token_key(refresh_token: str) -> str: @@ -176,7 +171,7 @@ class AccountService: account = db.session.query(Account).filter_by(email=email).first() if not account: - raise AccountPasswordError("Invalid email or password.") + raise AccountNotFoundError() if account.status == AccountStatus.BANNED.value: raise AccountLoginError("Account is banned.") @@ -438,7 +433,6 @@ class AccountService: account: Optional[Account] = None, email: Optional[str] = None, language: str = "en-US", - is_allow_register: bool = False, ): account_email = account.email if account else email if account_email is None: @@ -451,54 +445,14 @@ class AccountService: code, token = cls.generate_reset_password_token(account_email, account) - if account: - send_reset_password_mail_task.delay( - language=language, - to=account_email, - code=code, - ) - else: - send_reset_password_mail_task_when_account_not_exist.delay( - language=language, - to=account_email, - is_allow_register=is_allow_register, - ) + send_reset_password_mail_task.delay( + language=language, + to=account_email, + code=code, + ) cls.reset_password_rate_limiter.increment_rate_limit(account_email) return token - @classmethod - def send_email_register_email( - cls, - account: Optional[Account] = None, - email: Optional[str] = None, - language: str = "en-US", - ): - account_email = account.email if account else email - if account_email is None: - raise ValueError("Email must be provided.") - - if cls.email_register_rate_limiter.is_rate_limited(account_email): - from controllers.console.auth.error import EmailRegisterRateLimitExceededError - - raise EmailRegisterRateLimitExceededError() - - code, token = cls.generate_email_register_token(account_email) - - if account: - send_email_register_mail_task_when_account_exist.delay( - language=language, - to=account_email, - ) - - else: - send_email_register_mail_task.delay( - language=language, - to=account_email, - code=code, - ) - cls.email_register_rate_limiter.increment_rate_limit(account_email) - return token - @classmethod def send_change_email_email( cls, @@ -631,19 +585,6 @@ class AccountService: ) return code, token - @classmethod - def generate_email_register_token( - cls, - email: str, - code: Optional[str] = None, - additional_data: dict[str, Any] = {}, - ): - if not code: - code = "".join([str(secrets.randbelow(exclusive_upper_bound=10)) for _ in range(6)]) - additional_data["code"] = code - token = TokenManager.generate_token(email=email, token_type="email_register", additional_data=additional_data) - return code, token - @classmethod def generate_change_email_token( cls, @@ -682,10 +623,6 @@ class AccountService: def revoke_reset_password_token(cls, token: str): TokenManager.revoke_token(token, "reset_password") - @classmethod - def revoke_email_register_token(cls, token: str): - TokenManager.revoke_token(token, "email_register") - @classmethod def revoke_change_email_token(cls, token: str): TokenManager.revoke_token(token, "change_email") @@ -698,10 +635,6 @@ class AccountService: def get_reset_password_data(cls, token: str) -> Optional[dict[str, Any]]: return TokenManager.get_token_data(token, "reset_password") - @classmethod - def get_email_register_data(cls, token: str) -> Optional[dict[str, Any]]: - return TokenManager.get_token_data(token, "email_register") - @classmethod def get_change_email_data(cls, token: str) -> Optional[dict[str, Any]]: return TokenManager.get_token_data(token, "change_email") @@ -809,16 +742,6 @@ class AccountService: count = int(count) + 1 redis_client.setex(key, dify_config.FORGOT_PASSWORD_LOCKOUT_DURATION, count) - @staticmethod - @redis_fallback(default_return=None) - def add_email_register_error_rate_limit(email: str) -> None: - key = f"email_register_error_rate_limit:{email}" - count = redis_client.get(key) - if count is None: - count = 0 - count = int(count) + 1 - redis_client.setex(key, dify_config.EMAIL_REGISTER_LOCKOUT_DURATION, count) - @staticmethod @redis_fallback(default_return=False) def is_forgot_password_error_rate_limit(email: str) -> bool: @@ -838,24 +761,6 @@ class AccountService: key = f"forgot_password_error_rate_limit:{email}" redis_client.delete(key) - @staticmethod - @redis_fallback(default_return=False) - def is_email_register_error_rate_limit(email: str) -> bool: - key = f"email_register_error_rate_limit:{email}" - count = redis_client.get(key) - if count is None: - return False - count = int(count) - if count > AccountService.EMAIL_REGISTER_MAX_ERROR_LIMITS: - return True - return False - - @staticmethod - @redis_fallback(default_return=None) - def reset_email_register_error_rate_limit(email: str): - key = f"email_register_error_rate_limit:{email}" - redis_client.delete(key) - @staticmethod @redis_fallback(default_return=None) def add_change_email_error_rate_limit(email: str): diff --git a/api/tasks/mail_register_task.py b/api/tasks/mail_register_task.py deleted file mode 100644 index acf2852649..0000000000 --- a/api/tasks/mail_register_task.py +++ /dev/null @@ -1,86 +0,0 @@ -import logging -import time - -import click -from celery import shared_task - -from configs import dify_config -from extensions.ext_mail import mail -from libs.email_i18n import EmailType, get_email_i18n_service - -logger = logging.getLogger(__name__) - - -@shared_task(queue="mail") -def send_email_register_mail_task(language: str, to: str, code: str) -> None: - """ - Send email register email with internationalization support. - - Args: - language: Language code for email localization - to: Recipient email address - code: Email register code - """ - if not mail.is_inited(): - return - - logger.info(click.style(f"Start email register mail to {to}", fg="green")) - start_at = time.perf_counter() - - try: - email_service = get_email_i18n_service() - email_service.send_email( - email_type=EmailType.EMAIL_REGISTER, - language_code=language, - to=to, - template_context={ - "to": to, - "code": code, - }, - ) - - end_at = time.perf_counter() - logger.info( - click.style(f"Send email register mail to {to} succeeded: latency: {end_at - start_at}", fg="green") - ) - except Exception: - logger.exception("Send email register mail to %s failed", to) - - -@shared_task(queue="mail") -def send_email_register_mail_task_when_account_exist(language: str, to: str) -> None: - """ - Send email register email with internationalization support when account exist. - - Args: - language: Language code for email localization - to: Recipient email address - """ - if not mail.is_inited(): - return - - logger.info(click.style(f"Start email register mail to {to}", fg="green")) - start_at = time.perf_counter() - - try: - login_url = f"{dify_config.CONSOLE_WEB_URL}/signin" - reset_password_url = f"{dify_config.CONSOLE_WEB_URL}/reset-password" - - email_service = get_email_i18n_service() - email_service.send_email( - email_type=EmailType.EMAIL_REGISTER_WHEN_ACCOUNT_EXIST, - language_code=language, - to=to, - template_context={ - "to": to, - "login_url": login_url, - "reset_password_url": reset_password_url, - }, - ) - - end_at = time.perf_counter() - logger.info( - click.style(f"Send email register mail to {to} succeeded: latency: {end_at - start_at}", fg="green") - ) - except Exception: - logger.exception("Send email register mail to %s failed", to) diff --git a/api/tasks/mail_reset_password_task.py b/api/tasks/mail_reset_password_task.py index 1739562588..545db84fde 100644 --- a/api/tasks/mail_reset_password_task.py +++ b/api/tasks/mail_reset_password_task.py @@ -4,7 +4,6 @@ import time import click from celery import shared_task -from configs import dify_config from extensions.ext_mail import mail from libs.email_i18n import EmailType, get_email_i18n_service @@ -45,47 +44,3 @@ def send_reset_password_mail_task(language: str, to: str, code: str): ) except Exception: logger.exception("Send password reset mail to %s failed", to) - - -@shared_task(queue="mail") -def send_reset_password_mail_task_when_account_not_exist(language: str, to: str, is_allow_register: bool) -> None: - """ - Send reset password email with internationalization support when account not exist. - - Args: - language: Language code for email localization - to: Recipient email address - """ - if not mail.is_inited(): - return - - logger.info(click.style(f"Start password reset mail to {to}", fg="green")) - start_at = time.perf_counter() - - try: - if is_allow_register: - sign_up_url = f"{dify_config.CONSOLE_WEB_URL}/signup" - email_service = get_email_i18n_service() - email_service.send_email( - email_type=EmailType.RESET_PASSWORD_WHEN_ACCOUNT_NOT_EXIST, - language_code=language, - to=to, - template_context={ - "to": to, - "sign_up_url": sign_up_url, - }, - ) - else: - email_service = get_email_i18n_service() - email_service.send_email( - email_type=EmailType.RESET_PASSWORD_WHEN_ACCOUNT_NOT_EXIST_NO_REGISTER, - language_code=language, - to=to, - ) - - end_at = time.perf_counter() - logger.info( - click.style(f"Send password reset mail to {to} succeeded: latency: {end_at - start_at}", fg="green") - ) - except Exception: - logger.exception("Send password reset mail to %s failed", to) diff --git a/api/templates/register_email_template_en-US.html b/api/templates/register_email_template_en-US.html deleted file mode 100644 index e0fec59100..0000000000 --- a/api/templates/register_email_template_en-US.html +++ /dev/null @@ -1,87 +0,0 @@ - - - -
- - - - -
- Dify Sign-up Code
-Your sign-up code for Dify - - Copy and paste this code, this code will only be valid for the next 5 minutes.
-If you didn't request this code, don't worry. You can safely ignore this email.
-
- Dify 注册验证码
-您的 Dify 注册验证码 - - 复制并粘贴此验证码,注意验证码仅在接下来的 5 分钟内有效。
-如果您没有请求,请不要担心。您可以安全地忽略此电子邮件。
-
- It looks like you’re signing up with an existing account
-Hi, - We noticed you tried to sign up, but this email is already registered with an existing account. - - Please log in here:
-- Log In -
-- If you forgot your password, you can reset it here:
-- Reset Password -
-If you didn’t request this action, you can safely ignore this email. - Need help? Feel free to contact us at support@dify.ai.
-
- 您似乎正在使用现有账户注册
-Hi, - 我们注意到您尝试注册,但此电子邮件已与现有账户注册。 - - 请在此登录:
-- 登录 -
-- 如果您忘记了密码,可以在此重置:
-- 重置密码 -
-如果您没有请求此操作,您可以安全地忽略此电子邮件。 - - 需要帮助?随时联系我们 at support@dify.ai。
-
- It looks like you’re resetting a password with an unregistered email
-Hi, - We noticed you tried to reset your password, but this email is not associated with any account. -
-If you didn’t request this action, you can safely ignore this email. - Need help? Feel free to contact us at support@dify.ai.
-
- 看起来您正在使用未注册的电子邮件重置密码
-Hi, - 我们注意到您尝试重置密码,但此电子邮件未与任何账户关联。
-如果您没有请求此操作,您可以安全地忽略此电子邮件。 - 需要帮助?随时联系我们 at support@dify.ai。
-
- It looks like you’re resetting a password with an unregistered email
-Hi, - We noticed you tried to reset your password, but this email is not associated with any account. - - Please sign up here:
-- [Sign Up] -
-If you didn’t request this action, you can safely ignore this email. - Need help? Feel free to contact us at support@dify.ai.
-
- 看起来您正在使用未注册的电子邮件重置密码
-Hi, - 我们注意到您尝试重置密码,但此电子邮件未与任何账户关联。 - - 请在此注册:
-- [注册] -
-如果您没有请求此操作,您可以安全地忽略此电子邮件。 - 需要帮助?随时联系我们 at support@dify.ai。
-{{application_title}} Sign-up Code
-Your sign-up code for Dify - - Copy and paste this code, this code will only be valid for the next 5 minutes.
-If you didn't request this code, don't worry. You can safely ignore this email.
-{{application_title}} 注册验证码
-您的 {{application_title}} 注册验证码 - - 复制并粘贴此验证码,注意验证码仅在接下来的 5 分钟内有效。
-如果您没有请求此验证码,请不要担心。您可以安全地忽略此电子邮件。
-It looks like you’re signing up with an existing account
-Hi, - We noticed you tried to sign up, but this email is already registered with an existing account. - - Please log in here:
-- Log In -
-- If you forgot your password, you can reset it here:
-- Reset Password -
-If you didn’t request this action, you can safely ignore this email. - Need help? Feel free to contact us at support@dify.ai.
-您似乎正在使用现有账户注册
-Hi, - 我们注意到您尝试注册,但此电子邮件已与现有账户注册。 - - 请在此登录:
-- 登录 -
-- 如果您忘记了密码,可以在此重置:
-- 重置密码 -
-如果您没有请求此操作,您可以安全地忽略此电子邮件。 - - 需要帮助?随时联系我们 at support@dify.ai。
-It looks like you’re resetting a password with an unregistered email
-Hi, - We noticed you tried to reset your password, but this email is not associated with any account. -
-If you didn’t request this action, you can safely ignore this email. - Need help? Feel free to contact us at support@dify.ai.
-看起来您正在使用未注册的电子邮件重置密码
-Hi, - 我们注意到您尝试重置密码,但此电子邮件未与任何账户关联。 -
-如果您没有请求此操作,您可以安全地忽略此电子邮件。 - 需要帮助?随时联系我们 at support@dify.ai。
-It looks like you’re resetting a password with an unregistered email
-Hi, - We noticed you tried to reset your password, but this email is not associated with any account. - - Please sign up here:
-- [Sign Up] -
-If you didn’t request this action, you can safely ignore this email. - Need help? Feel free to contact us at support@dify.ai.
-看起来您正在使用未注册的电子邮件重置密码
-Hi, - 我们注意到您尝试重置密码,但此电子邮件未与任何账户关联。 - - 请在此注册:
-- [注册] -
-如果您没有请求此操作,您可以安全地忽略此电子邮件。 - 需要帮助?随时联系我们 at support@dify.ai。
-