diff --git a/libs/database/src/workspace.rs b/libs/database/src/workspace.rs index 5c2c6af9..5d67256f 100644 --- a/libs/database/src/workspace.rs +++ b/libs/database/src/workspace.rs @@ -145,6 +145,32 @@ pub async fn select_user_is_workspace_owner( Ok(exists.unwrap_or(false)) } +pub async fn select_user_is_collab_publisher( + pg_pool: &PgPool, + user_uuid: &Uuid, + workspace_uuid: &Uuid, + doc_name: &str, +) -> Result { + let exists = sqlx::query_scalar!( + r#" + SELECT EXISTS( + SELECT 1 + FROM af_published_collab + WHERE workspace_id = $1 + AND doc_name = $2 + AND published_by = (SELECT uid FROM af_user WHERE uuid = $3) + ); + "#, + workspace_uuid, + doc_name, + user_uuid, + ) + .fetch_one(pg_pool) + .await?; + + Ok(exists.unwrap_or(false)) +} + #[inline] pub async fn select_user_role<'a, E: Executor<'a, Database = Postgres>>( exectuor: E, @@ -851,3 +877,32 @@ pub async fn insert_or_replace_publish_collab_meta<'a, E: Executor<'a, Database Ok(()) } + +#[inline] +pub async fn delete_published_collab<'a, E: Executor<'a, Database = Postgres>>( + executor: E, + workspace_id: &Uuid, + doc_name: &str, +) -> Result<(), AppError> { + let res = sqlx::query!( + r#" + DELETE FROM af_published_collab + WHERE workspace_id = $1 AND doc_name = $2 + "#, + workspace_id, + doc_name, + ) + .execute(executor) + .await?; + + if res.rows_affected() != 1 { + tracing::error!( + "Failed to delete published collab, workspace_id: {}, doc_name: {}, rows_affected: {}", + workspace_id, + doc_name, + res.rows_affected() + ); + } + + Ok(()) +} diff --git a/src/api/workspace.rs b/src/api/workspace.rs index fd019163..e5738ff9 100644 --- a/src/api/workspace.rs +++ b/src/api/workspace.rs @@ -132,10 +132,11 @@ pub fn workspace_scope() -> Scope { web::resource("/{workspace_id}/publish-namespace") .route(web::put().to(put_publish_namespace_handler)) ) - .service( - web::resource("/{workspace_id}/collab/{object_id}/publish") - .route(web::post().to(post_publish_collab_handler)) - ) + .service( + web::resource("/{workspace_id}/collab/{object_id}/publish") + .route(web::put().to(put_publish_collab_handler)) + .route(web::delete().to(delete_publish_collab_handler)) + ) .service( web::resource("/{workspace_id}/collab/{object_id}/member/list") .route(web::get().to(get_collab_member_list_handler)), @@ -963,7 +964,7 @@ async fn put_publish_namespace_handler( Ok(Json(AppResponse::Ok())) } -async fn post_publish_collab_handler( +async fn put_publish_collab_handler( path_param: web::Path<(Uuid, String)>, user_uuid: UserUuid, payload: String, @@ -982,6 +983,22 @@ async fn post_publish_collab_handler( Ok(Json(AppResponse::Ok())) } +async fn delete_publish_collab_handler( + path_param: web::Path<(Uuid, String)>, + user_uuid: UserUuid, + state: Data, +) -> Result>> { + let (workspace_id, doc_name) = path_param.into_inner(); + biz::workspace::ops::delete_published_workspace_collab( + &state.pg_pool, + &workspace_id, + &doc_name, + &user_uuid, + ) + .await?; + Ok(Json(AppResponse::Ok())) +} + #[instrument(level = "debug", skip(state, payload), err)] async fn get_collab_member_list_handler( payload: Json, diff --git a/src/biz/workspace/ops.rs b/src/biz/workspace/ops.rs index ce94cdfc..3846e789 100644 --- a/src/biz/workspace/ops.rs +++ b/src/biz/workspace/ops.rs @@ -17,9 +17,10 @@ use database::pg_row::{AFWorkspaceMemberRow, AFWorkspaceRow}; use database::resource_usage::get_all_workspace_blob_metadata; use database::user::select_uid_from_email; use database::workspace::{ - change_workspace_icon, delete_from_workspace, delete_workspace_members, get_invitation_by_id, - insert_or_replace_publish_collab_meta, insert_user_workspace, insert_workspace_invitation, - rename_workspace, select_all_user_workspaces, select_user_is_workspace_owner, select_workspace, + change_workspace_icon, delete_from_workspace, delete_published_collab, delete_workspace_members, + get_invitation_by_id, insert_or_replace_publish_collab_meta, insert_user_workspace, + insert_workspace_invitation, rename_workspace, select_all_user_workspaces, + select_user_is_collab_publisher, select_user_is_workspace_owner, select_workspace, select_workspace_invitations_for_user, select_workspace_member, select_workspace_member_list, select_workspace_settings, select_workspace_total_collab_bytes, update_updated_at_of_workspace, update_workspace_invitation_set_status_accepted, update_workspace_publish_namespace, @@ -138,6 +139,27 @@ pub async fn publish_workspace_collab( Ok(()) } +pub async fn delete_published_workspace_collab( + pg_pool: &PgPool, + workspace_id: &Uuid, + doc_name: &str, + user_uuid: &Uuid, +) -> Result<(), AppError> { + let is_owner = select_user_is_workspace_owner(pg_pool, user_uuid, workspace_id).await?; + if !is_owner { + let is_publisher = + select_user_is_collab_publisher(pg_pool, user_uuid, workspace_id, doc_name).await?; + if !is_publisher { + return Err(AppError::UserUnAuthorized( + "User is not the owner of the workspace or the publisher of the document".to_string(), + )); + } + } + + delete_published_collab(pg_pool, workspace_id, doc_name).await?; + Ok(()) +} + pub async fn publish_workspace_collab_data( _pg_pool: &PgPool, _workspace_id: &Uuid,