Co-authored-by: Yansong Zhang <916125788@qq.com>tags/1.7.1
| updated_account = AccountService.update_account(current_user, email=args["new_email"]) | 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 | return updated_account | ||||
| EMAIL_CODE_LOGIN = "email_code_login" | EMAIL_CODE_LOGIN = "email_code_login" | ||||
| CHANGE_EMAIL_OLD = "change_email_old" | CHANGE_EMAIL_OLD = "change_email_old" | ||||
| CHANGE_EMAIL_NEW = "change_email_new" | CHANGE_EMAIL_NEW = "change_email_new" | ||||
| CHANGE_EMAIL_COMPLETED = "change_email_completed" | |||||
| OWNER_TRANSFER_CONFIRM = "owner_transfer_confirm" | OWNER_TRANSFER_CONFIRM = "owner_transfer_confirm" | ||||
| OWNER_TRANSFER_OLD_NOTIFY = "owner_transfer_old_notify" | OWNER_TRANSFER_OLD_NOTIFY = "owner_transfer_old_notify" | ||||
| OWNER_TRANSFER_NEW_NOTIFY = "owner_transfer_new_notify" | OWNER_TRANSFER_NEW_NOTIFY = "owner_transfer_new_notify" | ||||
| branded_template_path="without-brand/change_mail_confirm_new_template_zh-CN.html", | 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: { | EmailType.OWNER_TRANSFER_CONFIRM: { | ||||
| EmailLanguage.EN_US: EmailTemplate( | EmailLanguage.EN_US: EmailTemplate( | ||||
| subject="Verify Your Request to Transfer Workspace Ownership", | subject="Verify Your Request to Transfer Workspace Ownership", |
| from services.feature_service import FeatureService | from services.feature_service import FeatureService | ||||
| from tasks.delete_account_task import delete_account_task | from tasks.delete_account_task import delete_account_task | ||||
| from tasks.mail_account_deletion_task import send_account_deletion_verification_code | 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_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_invite_member_task import send_invite_member_mail_task | ||||
| from tasks.mail_owner_transfer_task import ( | from tasks.mail_owner_transfer_task import ( | ||||
| cls.change_email_rate_limiter.increment_rate_limit(account_email) | cls.change_email_rate_limiter.increment_rate_limit(account_email) | ||||
| return token | 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 | @classmethod | ||||
| def send_owner_transfer_email( | def send_owner_transfer_email( | ||||
| cls, | cls, |
| from celery import shared_task # type: ignore | from celery import shared_task # type: ignore | ||||
| from extensions.ext_mail import mail | 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") | @shared_task(queue="mail") | ||||
| ) | ) | ||||
| except Exception: | except Exception: | ||||
| logging.exception("Send change email mail to {} failed".format(to)) | 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)) |
| <!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> | |||||
| <!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> | |||||
| <!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> | |||||
| <!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> | |||||