199 lines
6.2 KiB
Rust
199 lines
6.2 KiB
Rust
use std::ops::DerefMut;
|
|
use std::sync::Arc;
|
|
|
|
use crate::mailer::AFCloudMailer;
|
|
use crate::{
|
|
biz::collab::{
|
|
folder_view::{to_dto_view_icon, to_dto_view_layout},
|
|
ops::get_latest_collab_folder,
|
|
},
|
|
mailer::{WorkspaceAccessRequestApprovedMailerParam, WorkspaceAccessRequestMailerParam},
|
|
};
|
|
use access_control::workspace::WorkspaceAccessControl;
|
|
use anyhow::Context;
|
|
use app_error::AppError;
|
|
use appflowy_collaborate::collab::storage::CollabAccessControlStorage;
|
|
use database::{
|
|
access_request::{
|
|
insert_new_access_request, select_access_request_by_request_id, update_access_request_status,
|
|
},
|
|
collab::GetCollabOrigin,
|
|
pg_row::AFAccessRequestStatusColumn,
|
|
workspace::upsert_workspace_member_with_txn,
|
|
};
|
|
use database_entity::dto::AFRole;
|
|
use shared_entity::dto::access_request_dto::{AccessRequest, AccessRequestView};
|
|
use sqlx::PgPool;
|
|
use uuid::Uuid;
|
|
|
|
pub async fn create_access_request(
|
|
pg_pool: &PgPool,
|
|
mailer: AFCloudMailer,
|
|
appflowy_web_url: &str,
|
|
workspace_id: Uuid,
|
|
view_id: Uuid,
|
|
uid: i64,
|
|
) -> Result<Uuid, AppError> {
|
|
let request_id = insert_new_access_request(pg_pool, workspace_id, view_id, uid).await?;
|
|
let access_request = select_access_request_by_request_id(pg_pool, request_id).await?;
|
|
let cloned_mailer = mailer.clone();
|
|
let approve_url = format!(
|
|
"{}/app/approve-request?request_id={}",
|
|
appflowy_web_url, request_id
|
|
);
|
|
let email = access_request.workspace.owner_email.clone();
|
|
let recipient_name = access_request.workspace.owner_name.clone();
|
|
// 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();
|
|
let user_icon_url =
|
|
"https://cdn.pixabay.com/photo/2015/10/05/22/37/blank-profile-picture-973460_1280.png"
|
|
.to_string();
|
|
tokio::spawn(async move {
|
|
if let Err(err) = cloned_mailer
|
|
.send_workspace_access_request(
|
|
&recipient_name,
|
|
&email,
|
|
WorkspaceAccessRequestMailerParam {
|
|
user_icon_url,
|
|
username: access_request.requester.name,
|
|
workspace_name: access_request.workspace.workspace_name,
|
|
workspace_icon_url,
|
|
workspace_member_count: access_request.workspace.member_count.unwrap_or(0),
|
|
approve_url,
|
|
},
|
|
)
|
|
.await
|
|
{
|
|
tracing::error!("Failed to send access request email: {:?}", err);
|
|
};
|
|
});
|
|
Ok(request_id)
|
|
}
|
|
|
|
pub async fn get_access_request(
|
|
pg_pool: &PgPool,
|
|
collab_storage: &CollabAccessControlStorage,
|
|
access_request_id: Uuid,
|
|
user_uid: i64,
|
|
) -> Result<AccessRequest, AppError> {
|
|
let access_request_with_view_id =
|
|
select_access_request_by_request_id(pg_pool, access_request_id).await?;
|
|
|
|
if access_request_with_view_id.workspace.owner_uid != user_uid {
|
|
return Err(AppError::NotEnoughPermissions {
|
|
user: user_uid.to_string(),
|
|
workspace_id: access_request_with_view_id
|
|
.workspace
|
|
.workspace_id
|
|
.to_string(),
|
|
});
|
|
}
|
|
let folder = get_latest_collab_folder(
|
|
collab_storage,
|
|
GetCollabOrigin::Server,
|
|
&access_request_with_view_id
|
|
.workspace
|
|
.workspace_id
|
|
.to_string(),
|
|
)
|
|
.await?;
|
|
let view = folder.get_view(&access_request_with_view_id.view_id.to_string());
|
|
let access_request_view = view
|
|
.map(|v| AccessRequestView {
|
|
view_id: v.id.clone(),
|
|
name: v.name.clone(),
|
|
icon: v.icon.as_ref().map(|icon| to_dto_view_icon(icon.clone())),
|
|
layout: to_dto_view_layout(&v.layout),
|
|
})
|
|
.ok_or(AppError::MissingView(format!(
|
|
"the view {} is missing",
|
|
access_request_with_view_id.view_id
|
|
)))?;
|
|
let access_request = AccessRequest {
|
|
request_id: access_request_with_view_id.request_id,
|
|
workspace: access_request_with_view_id.workspace,
|
|
requester: access_request_with_view_id.requester,
|
|
view: access_request_view,
|
|
status: access_request_with_view_id.status,
|
|
created_at: access_request_with_view_id.created_at,
|
|
};
|
|
Ok(access_request)
|
|
}
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub async fn approve_or_reject_access_request(
|
|
pg_pool: &PgPool,
|
|
workspace_access_control: Arc<dyn WorkspaceAccessControl>,
|
|
mailer: AFCloudMailer,
|
|
appflowy_web_url: &str,
|
|
request_id: Uuid,
|
|
uid: i64,
|
|
is_approved: bool,
|
|
) -> Result<(), AppError> {
|
|
let access_request = select_access_request_by_request_id(pg_pool, request_id).await?;
|
|
workspace_access_control
|
|
.enforce_role(
|
|
&uid,
|
|
&access_request.workspace.workspace_id.to_string(),
|
|
AFRole::Owner,
|
|
)
|
|
.await?;
|
|
|
|
let mut txn = pg_pool.begin().await.context("approving request")?;
|
|
let role = AFRole::Member;
|
|
if is_approved {
|
|
upsert_workspace_member_with_txn(
|
|
&mut txn,
|
|
&access_request.workspace.workspace_id,
|
|
&access_request.requester.email,
|
|
role.clone(),
|
|
)
|
|
.await?;
|
|
workspace_access_control
|
|
.insert_role(
|
|
&access_request.requester.uid,
|
|
&access_request.workspace.workspace_id,
|
|
role.clone(),
|
|
)
|
|
.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
|
|
} else {
|
|
AFAccessRequestStatusColumn::Rejected
|
|
};
|
|
update_access_request_status(txn.deref_mut(), request_id, status).await?;
|
|
txn.commit().await.context("committing transaction")?;
|
|
Ok(())
|
|
}
|