diff --git a/api/controllers/console/workspace/members.py b/api/controllers/console/workspace/members.py index 27f0c7354c..5bb509dc2f 100644 --- a/api/controllers/console/workspace/members.py +++ b/api/controllers/console/workspace/members.py @@ -266,6 +266,20 @@ class OwnerTransfer(Resource): try: assert member is not None, "Member not found" TenantService.update_member_role(current_user.current_tenant, member, "owner", current_user) + + AccountService.send_new_owner_transfer_notify_email( + account=member, + email=member.email, + workspace_name=current_user.current_tenant.name, + ) + + AccountService.send_old_owner_transfer_notify_email( + account=current_user, + email=current_user.email, + workspace_name=current_user.current_tenant.name, + new_owner_email=member.email, + ) + except Exception as e: raise ValueError(str(e)) diff --git a/api/services/account_service.py b/api/services/account_service.py index 055e4b8dd8..49e1c8bc2d 100644 --- a/api/services/account_service.py +++ b/api/services/account_service.py @@ -55,7 +55,11 @@ from tasks.mail_account_deletion_task import send_account_deletion_verification_ from tasks.mail_change_mail_task import 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 send_owner_transfer_confirm_task +from tasks.mail_owner_transfer_task import ( + send_new_owner_transfer_notify_email_task, + send_old_owner_transfer_notify_email_task, + send_owner_transfer_confirm_task, +) from tasks.mail_reset_password_task import send_reset_password_mail_task @@ -485,6 +489,44 @@ class AccountService: cls.owner_transfer_rate_limiter.increment_rate_limit(account_email) return token + @classmethod + def send_old_owner_transfer_notify_email( + cls, + account: Optional[Account] = None, + email: Optional[str] = None, + language: Optional[str] = "en-US", + workspace_name: Optional[str] = "", + new_owner_email: Optional[str] = "", + ): + account_email = account.email if account else email + if account_email is None: + raise ValueError("Email must be provided.") + + send_old_owner_transfer_notify_email_task.delay( + language=language, + to=account_email, + workspace=workspace_name, + new_owner_email=new_owner_email, + ) + + @classmethod + def send_new_owner_transfer_notify_email( + cls, + account: Optional[Account] = None, + email: Optional[str] = None, + language: Optional[str] = "en-US", + workspace_name: Optional[str] = "", + ): + account_email = account.email if account else email + if account_email is None: + raise ValueError("Email must be provided.") + + send_new_owner_transfer_notify_email_task.delay( + language=language, + to=account_email, + workspace=workspace_name, + ) + @classmethod def generate_reset_password_token( cls, diff --git a/api/tasks/mail_owner_transfer_task.py b/api/tasks/mail_owner_transfer_task.py index bcd9ee7c8a..fab776898e 100644 --- a/api/tasks/mail_owner_transfer_task.py +++ b/api/tasks/mail_owner_transfer_task.py @@ -56,3 +56,108 @@ def send_owner_transfer_confirm_task(language: str, to: str, code: str, workspac ) except Exception: logging.exception("owner transfer confirm email mail to {} failed".format(to)) + + +@shared_task(queue="mail") +def send_old_owner_transfer_notify_email_task(language: str, to: str, code: str, workspace: str, new_owner_email: str): + """ + Async Send owner transfer confirm mail + :param language: Language in which the email should be sent (e.g., 'en', 'zh') + :param to: Recipient email address + :param code: Change email code + :param workspace: Workspace name + :param new_owner_email: New owner email + """ + if not mail.is_inited(): + return + + logging.info(click.style("Start change email mail to {}".format(to), fg="green")) + start_at = time.perf_counter() + # send change email mail using different languages + try: + if language == "zh-Hans": + template = "transfer_workspace_old_owner_notify_template_zh-CN.html" + system_features = FeatureService.get_system_features() + if system_features.branding.enabled: + template = "without-brand/transfer_workspace_old_owner_notify_template_zh-CN.html" + html_content = render_template( + template, to=to, code=code, WorkspaceName=workspace, NewOwnerEmail=new_owner_email + ) + mail.send(to=to, subject="工作区所有权已转移", html=html_content) + else: + html_content = render_template( + template, to=to, code=code, WorkspaceName=workspace, NewOwnerEmail=new_owner_email + ) + mail.send(to=to, subject="工作区所有权已转移", html=html_content) + else: + template = "transfer_workspace_old_owner_notify_template_en-US.html" + system_features = FeatureService.get_system_features() + if system_features.branding.enabled: + template = "without-brand/transfer_workspace_old_owner_notify_template_en-US.html" + html_content = render_template( + template, to=to, code=code, WorkspaceName=workspace, NewOwnerEmail=new_owner_email + ) + mail.send(to=to, subject="Workspace ownership has been transferred", html=html_content) + else: + html_content = render_template( + template, to=to, code=code, WorkspaceName=workspace, NewOwnerEmail=new_owner_email + ) + mail.send(to=to, subject="Workspace ownership has been transferred", html=html_content) + + end_at = time.perf_counter() + logging.info( + click.style( + "Send owner transfer confirm mail to {} succeeded: latency: {}".format(to, end_at - start_at), + fg="green", + ) + ) + except Exception: + logging.exception("owner transfer confirm email mail to {} failed".format(to)) + + +@shared_task(queue="mail") +def send_new_owner_transfer_notify_email_task(language: str, to: str, code: str, workspace: str): + """ + Async Send owner transfer confirm mail + :param language: Language in which the email should be sent (e.g., 'en', 'zh') + :param to: Recipient email address + :param code: Change email code + :param workspace: Workspace name + """ + if not mail.is_inited(): + return + + logging.info(click.style("Start change email mail to {}".format(to), fg="green")) + start_at = time.perf_counter() + # send change email mail using different languages + try: + if language == "zh-Hans": + template = "transfer_workspace_new_owner_notify_template_zh-CN.html" + system_features = FeatureService.get_system_features() + if system_features.branding.enabled: + template = "without-brand/transfer_workspace_new_owner_notify_template_zh-CN.html" + html_content = render_template(template, to=to, code=code, WorkspaceName=workspace) + mail.send(to=to, subject=f"您现在是 {workspace} 的所有者", html=html_content) + else: + html_content = render_template(template, to=to, code=code, WorkspaceName=workspace) + mail.send(to=to, subject=f"您现在是 {workspace} 的所有者", html=html_content) + else: + template = "transfer_workspace_new_owner_notify_template_en-US.html" + system_features = FeatureService.get_system_features() + if system_features.branding.enabled: + template = "without-brand/transfer_workspace_new_owner_notify_template_en-US.html" + html_content = render_template(template, to=to, code=code, WorkspaceName=workspace) + mail.send(to=to, subject=f"You are now the owner of {workspace}", html=html_content) + else: + html_content = render_template(template, to=to, code=code, WorkspaceName=workspace) + mail.send(to=to, subject=f"You are now the owner of {workspace}", html=html_content) + + end_at = time.perf_counter() + logging.info( + click.style( + "Send owner transfer confirm mail to {} succeeded: latency: {}".format(to, end_at - start_at), + fg="green", + ) + ) + except Exception: + logging.exception("owner transfer confirm email mail to {} failed".format(to)) diff --git a/api/templates/transfer_workspace_new_owner_notify_template_en-US.html b/api/templates/transfer_workspace_new_owner_notify_template_en-US.html new file mode 100644 index 0000000000..1d546174b1 --- /dev/null +++ b/api/templates/transfer_workspace_new_owner_notify_template_en-US.html @@ -0,0 +1,86 @@ + + + + + + + + +
+
+ + Dify Logo +
+

You are now the owner of {{WorkspaceName}}

+

You have been assigned as the new owner of the workspace "{{WorkspaceName}}". + + As the new owner, you now have full administrative privileges for this workspace. + + If you have any questions, please contact support@dify.ai.

+ +
+ + + \ No newline at end of file diff --git a/api/templates/transfer_workspace_new_owner_notify_template_zh-CN.html b/api/templates/transfer_workspace_new_owner_notify_template_zh-CN.html new file mode 100644 index 0000000000..ccb52f26c5 --- /dev/null +++ b/api/templates/transfer_workspace_new_owner_notify_template_zh-CN.html @@ -0,0 +1,86 @@ + + + + + + + + +
+
+ + Dify Logo +
+

您现在是 {{WorkspaceName}} 的所有者

+

您已被分配为工作空间“{{WorkspaceName}}”的新所有者。 + + 作为新所有者,您现在对该工作空间拥有完全的管理权限。 + + 如果您有任何问题,请联系support@dify.ai。

+ +
+ + + \ No newline at end of file diff --git a/api/templates/transfer_workspace_old_owner_notify_template_en-US.html b/api/templates/transfer_workspace_old_owner_notify_template_en-US.html new file mode 100644 index 0000000000..24be72f89c --- /dev/null +++ b/api/templates/transfer_workspace_old_owner_notify_template_en-US.html @@ -0,0 +1,88 @@ + + + + + + + + +
+
+ + Dify Logo +
+

Workspace ownership has been transferred

+

You have successfully transferred ownership of the workspace "{{WorkspaceName}}" to + {{NewOwnerEmail}}. + + You no longer have owner privileges for this workspace. Your access level has been changed to Admin. + + If you did not initiate this transfer or have concerns about this change, please contact support@dify.ai + immediately.

+ +
+ + + \ No newline at end of file diff --git a/api/templates/transfer_workspace_old_owner_notify_template_zh-CN.html b/api/templates/transfer_workspace_old_owner_notify_template_zh-CN.html new file mode 100644 index 0000000000..31a5b36fc9 --- /dev/null +++ b/api/templates/transfer_workspace_old_owner_notify_template_zh-CN.html @@ -0,0 +1,86 @@ + + + + + + + + +
+
+ + Dify Logo +
+

工作区所有权已转移

+

您已成功将工作空间“{{WorkspaceName}}”的所有权转移给{{NewOwnerEmail}}。 + + 您不再拥有此工作空间的拥有者权限。您的访问级别已更改为管理员。 + + 如果您没有发起此转移或对此变更有任何疑问,请立即联系support@dify.ai。

+ +
+ + + \ No newline at end of file diff --git a/api/templates/without-brand/transfer_workspace_new_owner_notify_template_en-US.html b/api/templates/without-brand/transfer_workspace_new_owner_notify_template_en-US.html new file mode 100644 index 0000000000..f131098093 --- /dev/null +++ b/api/templates/without-brand/transfer_workspace_new_owner_notify_template_en-US.html @@ -0,0 +1,81 @@ + + + + + + + + +
+

You are now the owner of {{WorkspaceName}}

+

You have been assigned as the new owner of the workspace "{{WorkspaceName}}". + + As the new owner, you now have full administrative privileges for this workspace. + + If you have any questions, please contact support@dify.ai.

+
+ + + \ No newline at end of file diff --git a/api/templates/without-brand/transfer_workspace_new_owner_notify_template_zh-CN.html b/api/templates/without-brand/transfer_workspace_new_owner_notify_template_zh-CN.html new file mode 100644 index 0000000000..f0fc9ade3d --- /dev/null +++ b/api/templates/without-brand/transfer_workspace_new_owner_notify_template_zh-CN.html @@ -0,0 +1,81 @@ + + + + + + + + +
+

您现在是 {{WorkspaceName}} 的所有者

+

您已被分配为工作空间“{{WorkspaceName}}”的新所有者。 + + 作为新所有者,您现在对该工作空间拥有完全的管理权限。 + + 如果您有任何问题,请联系support@dify.ai。

+
+ + + \ No newline at end of file diff --git a/api/templates/without-brand/transfer_workspace_old_owner_notify_template_en-US.html b/api/templates/without-brand/transfer_workspace_old_owner_notify_template_en-US.html new file mode 100644 index 0000000000..9ea3844c0e --- /dev/null +++ b/api/templates/without-brand/transfer_workspace_old_owner_notify_template_en-US.html @@ -0,0 +1,83 @@ + + + + + + + + +
+

Workspace ownership has been transferred

+

You have successfully transferred ownership of the workspace "{{WorkspaceName}}" to + {{NewOwnerEmail}}. + + You no longer have owner privileges for this workspace. Your access level has been changed to Admin. + + If you did not initiate this transfer or have concerns about this change, please contact support@dify.ai + immediately.

+
+ + + \ No newline at end of file diff --git a/api/templates/without-brand/transfer_workspace_old_owner_notify_template_zh-CN.html b/api/templates/without-brand/transfer_workspace_old_owner_notify_template_zh-CN.html new file mode 100644 index 0000000000..093865d2e5 --- /dev/null +++ b/api/templates/without-brand/transfer_workspace_old_owner_notify_template_zh-CN.html @@ -0,0 +1,81 @@ + + + + + + + + +
+

工作区所有权已转移

+

您已成功将工作空间“{{WorkspaceName}}”的所有权转移给{{NewOwnerEmail}}。 + + 您不再拥有此工作空间的拥有者权限。您的访问级别已更改为管理员。 + + 如果您没有发起此转移或对此变更有任何疑问,请立即联系support@dify.ai。

+
+ + + \ No newline at end of file