From 1664fe869ed5c9165e2dce37d844c9a7631b89dc Mon Sep 17 00:00:00 2001 From: Khor Shu Heng <32997938+khorshuheng@users.noreply.github.com> Date: Wed, 2 Oct 2024 09:52:51 +0800 Subject: [PATCH] feat: send notification when access request is approved (#854) --- .../access_request_approved_notification.html | 131 +++++++++++++++++ email_template/config.js | 1 + email_template/config.production.js | 1 + .../access_request_approved_notification.html | 135 ++++++++++++++++++ src/api/access_request.rs | 19 ++- src/biz/access_request/ops.rs | 35 ++++- src/mailer.rs | 35 +++++ 7 files changed, 353 insertions(+), 4 deletions(-) create mode 100644 assets/mailer_templates/build_production/access_request_approved_notification.html create mode 100644 email_template/src/templates/access_request_approved_notification.html diff --git a/assets/mailer_templates/build_production/access_request_approved_notification.html b/assets/mailer_templates/build_production/access_request_approved_notification.html new file mode 100644 index 000000000..4b6f8e6ea --- /dev/null +++ b/assets/mailer_templates/build_production/access_request_approved_notification.html @@ -0,0 +1,131 @@ + + + + + + + + + + + Your access request has been approved + + + +
+ Workspace access request approved notification +  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏ +
+
+
+ + + + + + + +
+

+ Your request to access + {{ workspace_name }} + has been approved +

+
+ + + + + +
+
+ {{ workspace_name }} +
+
+
{{ workspace_name }}
+
+ {{ workspace_member_count }} members +
+
+ +
+ By clicking "View workspace" above, you confirm that you have read, + understood, and agreed to AppFlowy's + Terms & Conditions + and + Privacy Policy. +
+
+
+

+ + + +

+

+ Bring projects, knowledge, and teams together with the power of AI. +

+

+ + Maizzle + + + Maizzle + + + Maizzle + + + Maizzle + +

+
+
+
+ + \ No newline at end of file diff --git a/email_template/config.js b/email_template/config.js index 4338d118d..08ff1a252 100644 --- a/email_template/config.js +++ b/email_template/config.js @@ -30,6 +30,7 @@ module.exports = { userName: "John Doe", acceptUrl: "https://appflowy.io", approveUrl: "https://appflowy.io", + launchWorkspaceUrl: "https://appflowy.io", workspaceName: "AppFlowy", workspaceMembersCount: "100", workspaceIconURL: "https://cdn-icons-png.flaticon.com/512/1078/1078013.png", diff --git a/email_template/config.production.js b/email_template/config.production.js index 832821eb6..a2a859935 100644 --- a/email_template/config.production.js +++ b/email_template/config.production.js @@ -26,6 +26,7 @@ module.exports = { userName: "{{ username }}", acceptUrl: "{{ accept_url }}", approveUrl: "{{ approve_url }}", + launchWorkspaceUrl: "{{ launch_workspace_url }}", workspaceName: "{{ workspace_name }}", workspaceMembersCount: "{{ workspace_member_count }}", workspaceIconURL: "{{ workspace_icon_url }}", diff --git a/email_template/src/templates/access_request_approved_notification.html b/email_template/src/templates/access_request_approved_notification.html new file mode 100644 index 000000000..120e04f80 --- /dev/null +++ b/email_template/src/templates/access_request_approved_notification.html @@ -0,0 +1,135 @@ +--- +title: "Your access request has been approved" +preheader: "Workspace access request approved notification" +bodyClass: bg-purple-50 +--- + + +
+ + + + + + + +
+

+ Your request to access + {{ workspaceName }} + has been approved +

+ + + + + + +
+
+ {{ workspaceName }} +
+
+
{{ workspaceName }}
+
+ {{ workspaceMembersCount }} members +
+
+ +
View workspace
+
+
+ By clicking "View workspace" above, you confirm that you have read, + understood, and agreed to AppFlowy's + Terms & Conditions + and + Privacy Policy. +
+ +
+

+ + + +

+

+ Bring projects, knowledge, and teams together with the power of AI. +

+ +

+ + Maizzle + + + Maizzle + + + Maizzle + + + Maizzle + +

+
+
+
diff --git a/src/api/access_request.rs b/src/api/access_request.rs index ad577fc48..aafee8d4f 100644 --- a/src/api/access_request.rs +++ b/src/api/access_request.rs @@ -88,7 +88,22 @@ async fn post_approve_access_request_handler( let uid = state.user_cache.get_user_uid(&uuid).await?; let access_request_id = access_request_id.into_inner(); let is_approved = approve_access_request_params.is_approved; - approve_or_reject_access_request(&state.pg_pool, access_request_id, uid, *uuid, is_approved) - .await?; + let appflowy_web_url = state + .config + .appflowy_web_url + .clone() + .ok_or(AppError::Internal(anyhow!( + "AppFlowy web url has not been set" + )))?; + approve_or_reject_access_request( + &state.pg_pool, + state.mailer.clone(), + &appflowy_web_url, + access_request_id, + uid, + *uuid, + is_approved, + ) + .await?; Ok(Json(AppResponse::Ok())) } diff --git a/src/biz/access_request/ops.rs b/src/biz/access_request/ops.rs index bb3472b72..b001beb6d 100644 --- a/src/biz/access_request/ops.rs +++ b/src/biz/access_request/ops.rs @@ -21,7 +21,7 @@ use crate::{ folder_view::{to_dto_view_icon, to_view_layout}, ops::get_latest_collab_folder, }, - mailer::{Mailer, WorkspaceAccessRequestMailerParam}, + mailer::{Mailer, WorkspaceAccessRequestApprovedMailerParam, WorkspaceAccessRequestMailerParam}, }; pub async fn create_access_request( @@ -58,7 +58,7 @@ pub async fn create_access_request( workspace_name: access_request.workspace.workspace_name, workspace_icon_url, workspace_member_count: access_request.workspace.member_count.unwrap_or(0), - approve_url: approve_url.to_string(), + approve_url, }, ) .await @@ -110,6 +110,8 @@ pub async fn get_access_request( pub async fn approve_or_reject_access_request( pg_pool: &PgPool, + mailer: Mailer, + appflowy_web_url: &str, request_id: Uuid, uid: i64, user_uuid: Uuid, @@ -133,6 +135,35 @@ pub async fn approve_or_reject_access_request( role, ) .await?; + let cloned_mailer = mailer.clone(); + let launch_workspace_url = format!( + "{}/app/{}", + appflowy_web_url, &access_request.workspace.workspace_id + ); + + // use default icon until we have workspace icon + let workspace_icon_url = + "https://miro.medium.com/v2/resize:fit:2400/1*mTPfm7CwU31-tLhtLNkyJw.png".to_string(); + tokio::spawn(async move { + if let Err(err) = cloned_mailer + .send_workspace_access_request_approval_notification( + &access_request.requester.name, + &access_request.requester.email, + WorkspaceAccessRequestApprovedMailerParam { + workspace_name: access_request.workspace.workspace_name, + workspace_icon_url, + workspace_member_count: access_request.workspace.member_count.unwrap_or(0), + launch_workspace_url, + }, + ) + .await + { + tracing::error!( + "Failed to send access request approved notification email: {:?}", + err + ); + }; + }); } let status = if is_approved { AFAccessRequestStatusColumn::Approved diff --git a/src/mailer.rs b/src/mailer.rs index 1f07ea9f2..ae2c25d13 100644 --- a/src/mailer.rs +++ b/src/mailer.rs @@ -21,6 +21,8 @@ pub struct Mailer { pub const WORKSPACE_INVITE_TEMPLATE_NAME: &str = "workspace_invite"; pub const WORKSPACE_ACCESS_REQUEST_TEMPLATE_NAME: &str = "workspace_access_request"; +pub const WORKSPACE_ACCESS_REQUEST_APPROVED_NOTIFICATION_TEMPLATE_NAME: &str = + "workspace_access_request_approved_notification"; impl Mailer { pub async fn new( @@ -39,12 +41,19 @@ impl Mailer { include_str!("../assets/mailer_templates/build_production/workspace_invitation.html"); let access_request_template = include_str!("../assets/mailer_templates/build_production/access_request.html"); + let access_request_approved_notification_template = include_str!( + "../assets/mailer_templates/build_production/access_request_approved_notification.html" + ); let template_strings = HashMap::from([ (WORKSPACE_INVITE_TEMPLATE_NAME, workspace_invite_template), ( WORKSPACE_ACCESS_REQUEST_TEMPLATE_NAME, access_request_template, ), + ( + WORKSPACE_ACCESS_REQUEST_APPROVED_NOTIFICATION_TEMPLATE_NAME, + access_request_approved_notification_template, + ), ]); for (template_name, template_string) in template_strings { @@ -135,6 +144,24 @@ impl Mailer { ) .await } + + pub async fn send_workspace_access_request_approval_notification( + &self, + recipient_name: &str, + email: &str, + param: WorkspaceAccessRequestApprovedMailerParam, + ) -> Result<(), anyhow::Error> { + let subject = "Notification: Workspace access request approved"; + self + .send_email_template( + Some(recipient_name.to_string()), + email, + WORKSPACE_ACCESS_REQUEST_APPROVED_NOTIFICATION_TEMPLATE_NAME, + param, + subject, + ) + .await + } } #[derive(serde::Serialize)] @@ -156,3 +183,11 @@ pub struct WorkspaceAccessRequestMailerParam { pub workspace_member_count: i64, pub approve_url: String, } + +#[derive(serde::Serialize)] +pub struct WorkspaceAccessRequestApprovedMailerParam { + pub workspace_name: String, + pub workspace_icon_url: String, + pub workspace_member_count: i64, + pub launch_workspace_url: String, +}