diff --git a/.sqlx/query-fffe6f01abf0e5d8649a49b5793ccb92a9f823f07c363341357ea74bf4f4a16d.json b/.sqlx/query-fffe6f01abf0e5d8649a49b5793ccb92a9f823f07c363341357ea74bf4f4a16d.json new file mode 100644 index 00000000..9e933d77 --- /dev/null +++ b/.sqlx/query-fffe6f01abf0e5d8649a49b5793ccb92a9f823f07c363341357ea74bf4f4a16d.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE af_workspace\n SET default_published_view_id = NULL\n WHERE workspace_id = $1\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "fffe6f01abf0e5d8649a49b5793ccb92a9f823f07c363341357ea74bf4f4a16d" +} diff --git a/libs/client-api/src/http_publish.rs b/libs/client-api/src/http_publish.rs index e8d29247..daae7e06 100644 --- a/libs/client-api/src/http_publish.rs +++ b/libs/client-api/src/http_publish.rs @@ -217,6 +217,23 @@ impl Client { AppResponse::<()>::from_response(resp).await?.into_error() } + pub async fn delete_default_publish_view( + &self, + workspace_id: &str, + ) -> Result<(), AppResponseError> { + let url = format!( + "{}/api/workspace/{}/publish-default", + self.base_url, workspace_id + ); + let resp = self + .http_client_with_auth(Method::DELETE, &url) + .await? + .send() + .await?; + log_request_id(&resp); + AppResponse::<()>::from_response(resp).await?.into_error() + } + pub async fn get_default_publish_view_info( &self, workspace_id: &str, diff --git a/libs/database/src/publish.rs b/libs/database/src/publish.rs index 4f64d115..85311af5 100644 --- a/libs/database/src/publish.rs +++ b/libs/database/src/publish.rs @@ -112,6 +112,36 @@ pub async fn update_workspace_default_publish_view<'a, E: Executor<'a, Database Ok(()) } +#[inline] +pub async fn update_workspace_default_publish_view_set_null< + 'a, + E: Executor<'a, Database = Postgres>, +>( + executor: E, + workspace_id: &Uuid, +) -> Result<(), AppError> { + let res = sqlx::query!( + r#" + UPDATE af_workspace + SET default_published_view_id = NULL + WHERE workspace_id = $1 + "#, + workspace_id, + ) + .execute(executor) + .await?; + + if res.rows_affected() != 1 { + tracing::error!( + "Failed to unset workspace default publish view, workspace_id: {}, rows_affected: {}", + workspace_id, + res.rows_affected() + ); + } + + Ok(()) +} + #[inline] pub async fn select_workspace_publish_namespace<'a, E: Executor<'a, Database = Postgres>>( executor: E, diff --git a/src/api/workspace.rs b/src/api/workspace.rs index 15da8542..470fcc84 100644 --- a/src/api/workspace.rs +++ b/src/api/workspace.rs @@ -199,6 +199,7 @@ pub fn workspace_scope() -> Scope { .service( web::resource("/{workspace_id}/publish-default") .route(web::put().to(put_workspace_default_published_view_handler)) + .route(web::delete().to(delete_workspace_default_published_view_handler)) .route(web::get().to(get_workspace_published_default_info_handler)), ) .service( @@ -1148,7 +1149,6 @@ async fn put_workspace_default_published_view_handler( let new_default_pub_view_id = payload.into_inner().view_id; biz::workspace::publish::set_workspace_default_publish_view( &state.pg_pool, - &user_uuid, &workspace_id, &new_default_pub_view_id, ) @@ -1156,6 +1156,21 @@ async fn put_workspace_default_published_view_handler( Ok(Json(AppResponse::Ok())) } +async fn delete_workspace_default_published_view_handler( + user_uuid: UserUuid, + workspace_id: web::Path, + state: Data, +) -> Result>> { + let uid = state.user_cache.get_user_uid(&user_uuid).await?; + state + .workspace_access_control + .enforce_role(&uid, &workspace_id.to_string(), AFRole::Owner) + .await?; + biz::workspace::publish::unset_workspace_default_publish_view(&state.pg_pool, &workspace_id) + .await?; + Ok(Json(AppResponse::Ok())) +} + async fn get_workspace_published_default_info_handler( workspace_id: web::Path, state: Data, @@ -1180,13 +1195,8 @@ async fn put_publish_namespace_handler( .enforce_role(&uid, &workspace_id.to_string(), AFRole::Owner) .await?; let new_namespace = payload.into_inner().new_namespace; - biz::workspace::publish::set_workspace_namespace( - &state.pg_pool, - &user_uuid, - &workspace_id, - &new_namespace, - ) - .await?; + biz::workspace::publish::set_workspace_namespace(&state.pg_pool, &workspace_id, &new_namespace) + .await?; Ok(Json(AppResponse::Ok())) } @@ -1546,14 +1556,10 @@ async fn get_workspace_usage_handler( let uid = state.user_cache.get_user_uid(&user_uuid).await?; state .workspace_access_control - .enforce_action(&uid, &workspace_id.to_string(), Action::Read) + .enforce_role(&uid, &workspace_id.to_string(), AFRole::Owner) .await?; - let res = biz::workspace::ops::get_workspace_document_total_bytes( - &state.pg_pool, - &user_uuid, - &workspace_id, - ) - .await?; + let res = + biz::workspace::ops::get_workspace_document_total_bytes(&state.pg_pool, &workspace_id).await?; Ok(Json(AppResponse::Ok().with_data(res))) } diff --git a/src/biz/workspace/ops.rs b/src/biz/workspace/ops.rs index e488d561..5efee5d3 100644 --- a/src/biz/workspace/ops.rs +++ b/src/biz/workspace/ops.rs @@ -604,11 +604,8 @@ pub async fn update_workspace_member( pub async fn get_workspace_document_total_bytes( pg_pool: &PgPool, - user_uuid: &Uuid, workspace_id: &Uuid, ) -> Result { - check_workspace_owner(pg_pool, user_uuid, workspace_id).await?; - let byte_count = select_workspace_total_collab_bytes(pg_pool, workspace_id).await?; Ok(WorkspaceUsage { total_document_size: byte_count, @@ -646,19 +643,6 @@ pub async fn update_workspace_settings( Ok(setting) } -pub async fn check_workspace_owner( - pg_pool: &PgPool, - user_uuid: &Uuid, - workspace_id: &Uuid, -) -> Result<(), AppError> { - match select_user_is_workspace_owner(pg_pool, user_uuid, workspace_id).await? { - true => Ok(()), - false => Err(AppError::UserUnAuthorized( - "User is not the owner of the workspace".to_string(), - )), - } -} - async fn check_if_user_is_allowed_to_delete_comment( pg_pool: &PgPool, user_uuid: &Uuid, diff --git a/src/biz/workspace/publish.rs b/src/biz/workspace/publish.rs index 3ba13fce..f8c6489e 100644 --- a/src/biz/workspace/publish.rs +++ b/src/biz/workspace/publish.rs @@ -4,7 +4,7 @@ use database::{ publish::{ select_all_published_collab_info, select_default_published_view_id, select_default_published_view_id_for_namespace, update_published_collabs, - update_workspace_default_publish_view, + update_workspace_default_publish_view, update_workspace_default_publish_view_set_null, }, }; use database_entity::dto::PatchPublishedCollab; @@ -39,8 +39,6 @@ use crate::{ biz::collab::{folder_view::to_dto_folder_view_miminal, ops::get_latest_collab_folder}, }; -use super::ops::check_workspace_owner; - async fn check_workspace_owner_or_publisher( pg_pool: &PgPool, user_uuid: &Uuid, @@ -87,11 +85,9 @@ fn get_collab_s3_key(workspace_id: &Uuid, view_id: &Uuid) -> String { pub async fn set_workspace_namespace( pg_pool: &PgPool, - user_uuid: &Uuid, workspace_id: &Uuid, new_namespace: &str, ) -> Result<(), AppError> { - check_workspace_owner(pg_pool, user_uuid, workspace_id).await?; check_workspace_namespace(new_namespace).await?; if select_workspace_publish_namespace_exists(pg_pool, workspace_id, new_namespace).await? { return Err(AppError::PublishNamespaceAlreadyTaken( @@ -104,15 +100,21 @@ pub async fn set_workspace_namespace( pub async fn set_workspace_default_publish_view( pg_pool: &PgPool, - user_uuid: &Uuid, workspace_id: &Uuid, new_view_id: &Uuid, ) -> Result<(), AppError> { - check_workspace_owner(pg_pool, user_uuid, workspace_id).await?; update_workspace_default_publish_view(pg_pool, workspace_id, new_view_id).await?; Ok(()) } +pub async fn unset_workspace_default_publish_view( + pg_pool: &PgPool, + workspace_id: &Uuid, +) -> Result<(), AppError> { + update_workspace_default_publish_view_set_null(pg_pool, workspace_id).await?; + Ok(()) +} + pub async fn get_workspace_default_publish_view_info( pg_pool: &PgPool, workspace_id: &Uuid, diff --git a/tests/workspace/publish.rs b/tests/workspace/publish.rs index ce8a3498..4a955cb9 100644 --- a/tests/workspace/publish.rs +++ b/tests/workspace/publish.rs @@ -17,6 +17,7 @@ use collab_document::document::Document; use collab_entity::CollabType; use collab_folder::{CollabOrigin, Folder, UserId}; use itertools::Itertools; +use serde::{Deserialize, Serialize}; use shared_entity::dto::publish_dto::PublishDatabaseData; use std::collections::{HashMap, HashSet}; use std::thread::sleep; @@ -249,6 +250,16 @@ async fn test_publish_doc() { .unwrap(); assert_eq!(default_info_meta.info.view_id, view_id_1); assert_eq!(default_info_meta.meta.title, "my_title_1"); + + // Owner of workspace unset the default publish view + c.delete_default_publish_view(&workspace_id).await.unwrap(); + + // Public can no longer get default publish view info + let err = localhost_client() + .get_default_published_collab::>(&my_namespace) + .await + .unwrap_err(); + assert_eq!(err.code, ErrorCode::RecordNotFound, "{:?}", err); } { @@ -778,7 +789,7 @@ async fn workspace_member_publish_unpublish() { .unwrap(); } -#[derive(serde::Serialize, serde::Deserialize)] +#[derive(Debug, Serialize, Deserialize)] struct MyCustomMetadata { title: String, }