Co-authored-by: Yansong Zhang <916125788@qq.com>tags/1.7.1
| @@ -494,6 +494,10 @@ class ChangeEmailResetApi(Resource): | |||
| updated_account = AccountService.update_account(current_user, email=args["new_email"]) | |||
| AccountService.send_change_email_completed_notify_email( | |||
| email=args["new_email"], | |||
| ) | |||
| return updated_account | |||
| @@ -25,6 +25,7 @@ class EmailType(Enum): | |||
| EMAIL_CODE_LOGIN = "email_code_login" | |||
| CHANGE_EMAIL_OLD = "change_email_old" | |||
| CHANGE_EMAIL_NEW = "change_email_new" | |||
| CHANGE_EMAIL_COMPLETED = "change_email_completed" | |||
| OWNER_TRANSFER_CONFIRM = "owner_transfer_confirm" | |||
| OWNER_TRANSFER_OLD_NOTIFY = "owner_transfer_old_notify" | |||
| OWNER_TRANSFER_NEW_NOTIFY = "owner_transfer_new_notify" | |||
| @@ -344,6 +345,18 @@ def create_default_email_config() -> EmailI18nConfig: | |||
| branded_template_path="without-brand/change_mail_confirm_new_template_zh-CN.html", | |||
| ), | |||
| }, | |||
| EmailType.CHANGE_EMAIL_COMPLETED: { | |||
| EmailLanguage.EN_US: EmailTemplate( | |||
| subject="Your login email has been changed", | |||
| template_path="change_mail_completed_template_en-US.html", | |||
| branded_template_path="without-brand/change_mail_completed_template_en-US.html", | |||
| ), | |||
| EmailLanguage.ZH_HANS: EmailTemplate( | |||
| subject="您的登录邮箱已更改", | |||
| template_path="change_mail_completed_template_zh-CN.html", | |||
| branded_template_path="without-brand/change_mail_completed_template_zh-CN.html", | |||
| ), | |||
| }, | |||
| EmailType.OWNER_TRANSFER_CONFIRM: { | |||
| EmailLanguage.EN_US: EmailTemplate( | |||
| subject="Verify Your Request to Transfer Workspace Ownership", | |||
| @@ -54,7 +54,10 @@ from services.errors.workspace import WorkSpaceNotAllowedCreateError, Workspaces | |||
| from services.feature_service import FeatureService | |||
| from tasks.delete_account_task import delete_account_task | |||
| from tasks.mail_account_deletion_task import send_account_deletion_verification_code | |||
| from tasks.mail_change_mail_task import send_change_mail_task | |||
| from tasks.mail_change_mail_task import ( | |||
| send_change_mail_completed_notification_task, | |||
| send_change_mail_task, | |||
| ) | |||
| from tasks.mail_email_code_login import send_email_code_login_mail_task | |||
| from tasks.mail_invite_member_task import send_invite_member_mail_task | |||
| from tasks.mail_owner_transfer_task import ( | |||
| @@ -461,6 +464,22 @@ class AccountService: | |||
| cls.change_email_rate_limiter.increment_rate_limit(account_email) | |||
| return token | |||
| @classmethod | |||
| def send_change_email_completed_notify_email( | |||
| cls, | |||
| account: Optional[Account] = None, | |||
| email: Optional[str] = None, | |||
| language: Optional[str] = "en-US", | |||
| ): | |||
| account_email = account.email if account else email | |||
| if account_email is None: | |||
| raise ValueError("Email must be provided.") | |||
| send_change_mail_completed_notification_task.delay( | |||
| language=language, | |||
| to=account_email, | |||
| ) | |||
| @classmethod | |||
| def send_owner_transfer_email( | |||
| cls, | |||
| @@ -5,7 +5,7 @@ import click | |||
| from celery import shared_task # type: ignore | |||
| from extensions.ext_mail import mail | |||
| from libs.email_i18n import get_email_i18n_service | |||
| from libs.email_i18n import EmailType, get_email_i18n_service | |||
| @shared_task(queue="mail") | |||
| @@ -40,3 +40,41 @@ def send_change_mail_task(language: str, to: str, code: str, phase: str) -> None | |||
| ) | |||
| except Exception: | |||
| logging.exception("Send change email mail to {} failed".format(to)) | |||
| @shared_task(queue="mail") | |||
| def send_change_mail_completed_notification_task(language: str, to: str) -> None: | |||
| """ | |||
| Send change email completed notification with internationalization support. | |||
| Args: | |||
| language: Language code for email localization | |||
| to: Recipient email address | |||
| """ | |||
| if not mail.is_inited(): | |||
| return | |||
| logging.info(click.style("Start change email completed notify mail to {}".format(to), fg="green")) | |||
| start_at = time.perf_counter() | |||
| try: | |||
| email_service = get_email_i18n_service() | |||
| email_service.send_email( | |||
| email_type=EmailType.CHANGE_EMAIL_COMPLETED, | |||
| language_code=language, | |||
| to=to, | |||
| template_context={ | |||
| "to": to, | |||
| "email": to, | |||
| }, | |||
| ) | |||
| end_at = time.perf_counter() | |||
| logging.info( | |||
| click.style( | |||
| "Send change email completed mail to {} succeeded: latency: {}".format(to, end_at - start_at), | |||
| fg="green", | |||
| ) | |||
| ) | |||
| except Exception: | |||
| logging.exception("Send change email completed mail to {} failed".format(to)) | |||
| @@ -0,0 +1,135 @@ | |||
| <!DOCTYPE html> | |||
| <html> | |||
| <head> | |||
| <style> | |||
| body { | |||
| font-family: 'Arial', sans-serif; | |||
| line-height: 16pt; | |||
| color: #101828; | |||
| background-color: #e9ebf0; | |||
| margin: 0; | |||
| padding: 0; | |||
| } | |||
| .container { | |||
| width: 504px; | |||
| min-height: 374px; | |||
| margin: 40px auto; | |||
| padding: 0 48px; | |||
| background-color: #fcfcfd; | |||
| border-radius: 16px; | |||
| border: 1px solid #ffffff; | |||
| box-shadow: 0px 3px 10px -2px rgba(9, 9, 11, 0.08), 0px 2px 4px -2px rgba(9, 9, 11, 0.06); | |||
| } | |||
| .header { | |||
| padding-top: 36px; | |||
| padding-bottom: 24px; | |||
| } | |||
| .header img { | |||
| max-width: 63px; | |||
| height: auto; | |||
| } | |||
| .title { | |||
| margin: 0; | |||
| padding-top: 8px; | |||
| padding-bottom: 16px; | |||
| color: #101828; | |||
| font-size: 24px; | |||
| font-family: Inter; | |||
| font-style: normal; | |||
| font-weight: 600; | |||
| line-height: 120%; /* 28.8px */ | |||
| } | |||
| .description { | |||
| color: #354052; | |||
| font-size: 14px; | |||
| font-family: Inter; | |||
| font-style: normal; | |||
| font-weight: 400; | |||
| line-height: 20px; /* 142.857% */ | |||
| letter-spacing: -0.07px; | |||
| } | |||
| .content1 { | |||
| margin: 0; | |||
| padding-top: 16px; | |||
| padding-bottom: 12px; | |||
| } | |||
| .content2 { | |||
| margin: 0; | |||
| } | |||
| .content3 { | |||
| margin: 0; | |||
| padding-bottom: 12px; | |||
| } | |||
| .code-content { | |||
| margin-bottom: 8px; | |||
| padding: 16px 32px; | |||
| text-align: center; | |||
| border-radius: 16px; | |||
| background-color: #f2f4f7; | |||
| } | |||
| .code { | |||
| color: #101828; | |||
| font-family: Inter; | |||
| font-size: 30px; | |||
| font-style: normal; | |||
| font-weight: 700; | |||
| line-height: 36px; | |||
| } | |||
| .tips { | |||
| margin: 0; | |||
| padding-top: 12px; | |||
| padding-bottom: 16px; | |||
| color: #354052; | |||
| font-size: 14px; | |||
| font-family: Inter; | |||
| font-style: normal; | |||
| font-weight: 400; | |||
| line-height: 20px; /* 142.857% */ | |||
| letter-spacing: -0.07px; | |||
| } | |||
| .support { | |||
| color: #354052; | |||
| font-weight: 500; | |||
| text-decoration: none; | |||
| } | |||
| .support:hover { | |||
| color: #354052; | |||
| font-weight: 500; | |||
| text-decoration: underline; | |||
| } | |||
| </style> | |||
| </head> | |||
| <body> | |||
| <div class="container"> | |||
| <div class="header"> | |||
| <!-- Optional: Add a logo or a header image here --> | |||
| <img src="https://assets.dify.ai/images/logo.png" alt="Dify Logo" /> | |||
| </div> | |||
| <p class="title">Your login email has been changed</p> | |||
| <div class="description"> | |||
| <p class="content1">You can now log into Dify with your new email address:</p> | |||
| </div> | |||
| <div class="code-content"> | |||
| <span class="code">{{email}}</span> | |||
| </div> | |||
| <p class="tips">If you did not make this change, email <a class="support" href="mailto:support@dify.ai">support@dify.ai</a>.</p> | |||
| </div> | |||
| </body> | |||
| </html> | |||
| @@ -0,0 +1,135 @@ | |||
| <!DOCTYPE html> | |||
| <html> | |||
| <head> | |||
| <style> | |||
| body { | |||
| font-family: 'Arial', sans-serif; | |||
| line-height: 16pt; | |||
| color: #101828; | |||
| background-color: #e9ebf0; | |||
| margin: 0; | |||
| padding: 0; | |||
| } | |||
| .container { | |||
| width: 504px; | |||
| min-height: 374px; | |||
| margin: 40px auto; | |||
| padding: 0 48px; | |||
| background-color: #fcfcfd; | |||
| border-radius: 16px; | |||
| border: 1px solid #ffffff; | |||
| box-shadow: 0px 3px 10px -2px rgba(9, 9, 11, 0.08), 0px 2px 4px -2px rgba(9, 9, 11, 0.06); | |||
| } | |||
| .header { | |||
| padding-top: 36px; | |||
| padding-bottom: 24px; | |||
| } | |||
| .header img { | |||
| max-width: 63px; | |||
| height: auto; | |||
| } | |||
| .title { | |||
| margin: 0; | |||
| padding-top: 8px; | |||
| padding-bottom: 16px; | |||
| color: #101828; | |||
| font-size: 24px; | |||
| font-family: Inter; | |||
| font-style: normal; | |||
| font-weight: 600; | |||
| line-height: 120%; /* 28.8px */ | |||
| } | |||
| .description { | |||
| color: #354052; | |||
| font-size: 14px; | |||
| font-family: Inter; | |||
| font-style: normal; | |||
| font-weight: 400; | |||
| line-height: 20px; /* 142.857% */ | |||
| letter-spacing: -0.07px; | |||
| } | |||
| .content1 { | |||
| margin: 0; | |||
| padding-top: 16px; | |||
| padding-bottom: 12px; | |||
| } | |||
| .content2 { | |||
| margin: 0; | |||
| } | |||
| .content3 { | |||
| margin: 0; | |||
| padding-bottom: 12px; | |||
| } | |||
| .code-content { | |||
| margin-bottom: 8px; | |||
| padding: 16px 32px; | |||
| text-align: center; | |||
| border-radius: 16px; | |||
| background-color: #f2f4f7; | |||
| } | |||
| .code { | |||
| color: #101828; | |||
| font-family: Inter; | |||
| font-size: 30px; | |||
| font-style: normal; | |||
| font-weight: 700; | |||
| line-height: 36px; | |||
| } | |||
| .tips { | |||
| margin: 0; | |||
| padding-top: 12px; | |||
| padding-bottom: 16px; | |||
| color: #354052; | |||
| font-size: 14px; | |||
| font-family: Inter; | |||
| font-style: normal; | |||
| font-weight: 400; | |||
| line-height: 20px; /* 142.857% */ | |||
| letter-spacing: -0.07px; | |||
| } | |||
| .support { | |||
| color: #354052; | |||
| font-weight: 500; | |||
| text-decoration: none; | |||
| } | |||
| .support:hover { | |||
| color: #354052; | |||
| font-weight: 500; | |||
| text-decoration: underline; | |||
| } | |||
| </style> | |||
| </head> | |||
| <body> | |||
| <div class="container"> | |||
| <div class="header"> | |||
| <!-- Optional: Add a logo or a header image here --> | |||
| <img src="https://assets.dify.ai/images/logo.png" alt="Dify Logo" /> | |||
| </div> | |||
| <p class="title">您的登录邮箱已更改</p> | |||
| <div class="description"> | |||
| <p class="content1">您现在可以使用新的电子邮件地址登录 Dify:</p> | |||
| </div> | |||
| <div class="code-content"> | |||
| <span class="code">{{email}}</span> | |||
| </div> | |||
| <p class="tips">如果您没有进行此更改,请发送电子邮件至 <a class="support" href="mailto:support@dify.ai">support@dify.ai</a>。</p> | |||
| </div> | |||
| </body> | |||
| </html> | |||
| @@ -0,0 +1,132 @@ | |||
| <!DOCTYPE html> | |||
| <html> | |||
| <head> | |||
| <style> | |||
| body { | |||
| font-family: 'Arial', sans-serif; | |||
| line-height: 16pt; | |||
| color: #101828; | |||
| background-color: #e9ebf0; | |||
| margin: 0; | |||
| padding: 0; | |||
| } | |||
| .container { | |||
| width: 504px; | |||
| min-height: 374px; | |||
| margin: 40px auto; | |||
| padding: 0 48px; | |||
| background-color: #fcfcfd; | |||
| border-radius: 16px; | |||
| border: 1px solid #ffffff; | |||
| box-shadow: 0px 3px 10px -2px rgba(9, 9, 11, 0.08), 0px 2px 4px -2px rgba(9, 9, 11, 0.06); | |||
| } | |||
| .header { | |||
| padding-top: 36px; | |||
| padding-bottom: 24px; | |||
| } | |||
| .header img { | |||
| max-width: 63px; | |||
| height: auto; | |||
| } | |||
| .title { | |||
| margin: 0; | |||
| padding-top: 8px; | |||
| padding-bottom: 16px; | |||
| color: #101828; | |||
| font-size: 24px; | |||
| font-family: Inter; | |||
| font-style: normal; | |||
| font-weight: 600; | |||
| line-height: 120%; /* 28.8px */ | |||
| } | |||
| .description { | |||
| color: #354052; | |||
| font-size: 14px; | |||
| font-family: Inter; | |||
| font-style: normal; | |||
| font-weight: 400; | |||
| line-height: 20px; /* 142.857% */ | |||
| letter-spacing: -0.07px; | |||
| } | |||
| .content1 { | |||
| margin: 0; | |||
| padding-top: 16px; | |||
| padding-bottom: 12px; | |||
| } | |||
| .content2 { | |||
| margin: 0; | |||
| } | |||
| .content3 { | |||
| margin: 0; | |||
| padding-bottom: 12px; | |||
| } | |||
| .code-content { | |||
| margin-bottom: 8px; | |||
| padding: 16px 32px; | |||
| text-align: center; | |||
| border-radius: 16px; | |||
| background-color: #f2f4f7; | |||
| } | |||
| .code { | |||
| color: #101828; | |||
| font-family: Inter; | |||
| font-size: 30px; | |||
| font-style: normal; | |||
| font-weight: 700; | |||
| line-height: 36px; | |||
| } | |||
| .tips { | |||
| margin: 0; | |||
| padding-top: 12px; | |||
| padding-bottom: 16px; | |||
| color: #354052; | |||
| font-size: 14px; | |||
| font-family: Inter; | |||
| font-style: normal; | |||
| font-weight: 400; | |||
| line-height: 20px; /* 142.857% */ | |||
| letter-spacing: -0.07px; | |||
| } | |||
| .support { | |||
| color: #354052; | |||
| font-weight: 500; | |||
| text-decoration: none; | |||
| } | |||
| .support:hover { | |||
| color: #354052; | |||
| font-weight: 500; | |||
| text-decoration: underline; | |||
| } | |||
| </style> | |||
| </head> | |||
| <body> | |||
| <div class="container"> | |||
| <div class="header"></div> | |||
| <p class="title">Your login email has been changed</p> | |||
| <div class="description"> | |||
| <p class="content1">You can now log into {{application_title}} with your new email address:</p> | |||
| </div> | |||
| <div class="code-content"> | |||
| <span class="code">{{email}}</span> | |||
| </div> | |||
| <p class="tips">If you did not make this change, please ignore this email or contact support immediately.</p> | |||
| </div> | |||
| </body> | |||
| </html> | |||
| @@ -0,0 +1,132 @@ | |||
| <!DOCTYPE html> | |||
| <html> | |||
| <head> | |||
| <style> | |||
| body { | |||
| font-family: 'Arial', sans-serif; | |||
| line-height: 16pt; | |||
| color: #101828; | |||
| background-color: #e9ebf0; | |||
| margin: 0; | |||
| padding: 0; | |||
| } | |||
| .container { | |||
| width: 504px; | |||
| min-height: 374px; | |||
| margin: 40px auto; | |||
| padding: 0 48px; | |||
| background-color: #fcfcfd; | |||
| border-radius: 16px; | |||
| border: 1px solid #ffffff; | |||
| box-shadow: 0px 3px 10px -2px rgba(9, 9, 11, 0.08), 0px 2px 4px -2px rgba(9, 9, 11, 0.06); | |||
| } | |||
| .header { | |||
| padding-top: 36px; | |||
| padding-bottom: 24px; | |||
| } | |||
| .header img { | |||
| max-width: 63px; | |||
| height: auto; | |||
| } | |||
| .title { | |||
| margin: 0; | |||
| padding-top: 8px; | |||
| padding-bottom: 16px; | |||
| color: #101828; | |||
| font-size: 24px; | |||
| font-family: Inter; | |||
| font-style: normal; | |||
| font-weight: 600; | |||
| line-height: 120%; /* 28.8px */ | |||
| } | |||
| .description { | |||
| color: #354052; | |||
| font-size: 14px; | |||
| font-family: Inter; | |||
| font-style: normal; | |||
| font-weight: 400; | |||
| line-height: 20px; /* 142.857% */ | |||
| letter-spacing: -0.07px; | |||
| } | |||
| .content1 { | |||
| margin: 0; | |||
| padding-top: 16px; | |||
| padding-bottom: 12px; | |||
| } | |||
| .content2 { | |||
| margin: 0; | |||
| } | |||
| .content3 { | |||
| margin: 0; | |||
| padding-bottom: 12px; | |||
| } | |||
| .code-content { | |||
| margin-bottom: 8px; | |||
| padding: 16px 32px; | |||
| text-align: center; | |||
| border-radius: 16px; | |||
| background-color: #f2f4f7; | |||
| } | |||
| .code { | |||
| color: #101828; | |||
| font-family: Inter; | |||
| font-size: 30px; | |||
| font-style: normal; | |||
| font-weight: 700; | |||
| line-height: 36px; | |||
| } | |||
| .tips { | |||
| margin: 0; | |||
| padding-top: 12px; | |||
| padding-bottom: 16px; | |||
| color: #354052; | |||
| font-size: 14px; | |||
| font-family: Inter; | |||
| font-style: normal; | |||
| font-weight: 400; | |||
| line-height: 20px; /* 142.857% */ | |||
| letter-spacing: -0.07px; | |||
| } | |||
| .support { | |||
| color: #354052; | |||
| font-weight: 500; | |||
| text-decoration: none; | |||
| } | |||
| .support:hover { | |||
| color: #354052; | |||
| font-weight: 500; | |||
| text-decoration: underline; | |||
| } | |||
| </style> | |||
| </head> | |||
| <body> | |||
| <div class="container"> | |||
| <div class="header"></div> | |||
| <p class="title">您的登录邮箱已更改</p> | |||
| <div class="description"> | |||
| <p class="content1">您现在可以使用新的电子邮件地址登录 {{application_title}}:</p> | |||
| </div> | |||
| <div class="code-content"> | |||
| <span class="code">{{email}}</span> | |||
| </div> | |||
| <p class="tips">如果您没有进行此更改,请忽略此电子邮件或立即联系支持。</p> | |||
| </div> | |||
| </body> | |||
| </html> | |||