fix: auto patch workspace folder without space (#1107)

This commit is contained in:
Khor Shu Heng 2025-01-03 21:28:36 +08:00 committed by GitHub
parent 424796ef36
commit a61a5c58cc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 135 additions and 28 deletions

14
Cargo.lock generated
View File

@ -2145,7 +2145,7 @@ dependencies = [
[[package]] [[package]]
name = "collab" name = "collab"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c45a2120361f94bbedb787cdd2192a38c94c7f5f#c45a2120361f94bbedb787cdd2192a38c94c7f5f" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d51fecb64762855f9e54648c78ab1ee0d5404f97#d51fecb64762855f9e54648c78ab1ee0d5404f97"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arc-swap", "arc-swap",
@ -2170,7 +2170,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-database" name = "collab-database"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c45a2120361f94bbedb787cdd2192a38c94c7f5f#c45a2120361f94bbedb787cdd2192a38c94c7f5f" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d51fecb64762855f9e54648c78ab1ee0d5404f97#d51fecb64762855f9e54648c78ab1ee0d5404f97"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -2210,7 +2210,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-document" name = "collab-document"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c45a2120361f94bbedb787cdd2192a38c94c7f5f#c45a2120361f94bbedb787cdd2192a38c94c7f5f" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d51fecb64762855f9e54648c78ab1ee0d5404f97#d51fecb64762855f9e54648c78ab1ee0d5404f97"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arc-swap", "arc-swap",
@ -2231,7 +2231,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-entity" name = "collab-entity"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c45a2120361f94bbedb787cdd2192a38c94c7f5f#c45a2120361f94bbedb787cdd2192a38c94c7f5f" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d51fecb64762855f9e54648c78ab1ee0d5404f97#d51fecb64762855f9e54648c78ab1ee0d5404f97"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
@ -2251,7 +2251,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-folder" name = "collab-folder"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c45a2120361f94bbedb787cdd2192a38c94c7f5f#c45a2120361f94bbedb787cdd2192a38c94c7f5f" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d51fecb64762855f9e54648c78ab1ee0d5404f97#d51fecb64762855f9e54648c78ab1ee0d5404f97"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arc-swap", "arc-swap",
@ -2273,7 +2273,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-importer" name = "collab-importer"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c45a2120361f94bbedb787cdd2192a38c94c7f5f#c45a2120361f94bbedb787cdd2192a38c94c7f5f" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d51fecb64762855f9e54648c78ab1ee0d5404f97#d51fecb64762855f9e54648c78ab1ee0d5404f97"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-recursion", "async-recursion",
@ -2382,7 +2382,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-user" name = "collab-user"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c45a2120361f94bbedb787cdd2192a38c94c7f5f#c45a2120361f94bbedb787cdd2192a38c94c7f5f" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d51fecb64762855f9e54648c78ab1ee0d5404f97#d51fecb64762855f9e54648c78ab1ee0d5404f97"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",

View File

@ -320,13 +320,13 @@ lto = false
[patch.crates-io] [patch.crates-io]
# It's diffcult to resovle different version with the same crate used in AppFlowy Frontend and the Client-API crate. # It's diffcult to resovle different version with the same crate used in AppFlowy Frontend and the Client-API crate.
# So using patch to workaround this issue. # So using patch to workaround this issue.
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c45a2120361f94bbedb787cdd2192a38c94c7f5f" } collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d51fecb64762855f9e54648c78ab1ee0d5404f97" }
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c45a2120361f94bbedb787cdd2192a38c94c7f5f" } collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d51fecb64762855f9e54648c78ab1ee0d5404f97" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c45a2120361f94bbedb787cdd2192a38c94c7f5f" } collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d51fecb64762855f9e54648c78ab1ee0d5404f97" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c45a2120361f94bbedb787cdd2192a38c94c7f5f" } collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d51fecb64762855f9e54648c78ab1ee0d5404f97" }
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c45a2120361f94bbedb787cdd2192a38c94c7f5f" } collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d51fecb64762855f9e54648c78ab1ee0d5404f97" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c45a2120361f94bbedb787cdd2192a38c94c7f5f" } collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d51fecb64762855f9e54648c78ab1ee0d5404f97" }
collab-importer = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c45a2120361f94bbedb787cdd2192a38c94c7f5f" } collab-importer = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d51fecb64762855f9e54648c78ab1ee0d5404f97" }
[features] [features]
history = [] history = []

View File

@ -2024,10 +2024,13 @@ async fn get_workspace_folder_handler(
user_uuid: UserUuid, user_uuid: UserUuid,
workspace_id: web::Path<Uuid>, workspace_id: web::Path<Uuid>,
state: Data<AppState>, state: Data<AppState>,
server: Data<RealtimeServerAddr>,
query: web::Query<QueryWorkspaceFolder>, query: web::Query<QueryWorkspaceFolder>,
req: HttpRequest,
) -> Result<Json<AppResponse<FolderView>>> { ) -> Result<Json<AppResponse<FolderView>>> {
let depth = query.depth.unwrap_or(1); let depth = query.depth.unwrap_or(1);
let uid = state.user_cache.get_user_uid(&user_uuid).await?; let uid = state.user_cache.get_user_uid(&user_uuid).await?;
let user = realtime_user_for_web_request(req.headers(), uid)?;
let workspace_id = workspace_id.into_inner(); let workspace_id = workspace_id.into_inner();
state state
.workspace_access_control .workspace_access_control
@ -2039,9 +2042,11 @@ async fn get_workspace_folder_handler(
workspace_id.to_string() workspace_id.to_string()
}; };
let folder_view = biz::collab::ops::get_user_workspace_structure( let folder_view = biz::collab::ops::get_user_workspace_structure(
&state.metrics.appflowy_web_metrics,
server,
&state.collab_access_control_storage, &state.collab_access_control_storage,
&state.pg_pool, &state.pg_pool,
uid, user,
workspace_id, workspace_id,
depth, depth,
&root_view_id, &root_view_id,

View File

@ -2,9 +2,7 @@ use std::collections::HashSet;
use app_error::AppError; use app_error::AppError;
use chrono::DateTime; use chrono::DateTime;
use collab_folder::{ use collab_folder::{Folder, SectionItem, SpacePermission, ViewLayout as CollabFolderViewLayout};
hierarchy_builder::SpacePermission, Folder, SectionItem, ViewLayout as CollabFolderViewLayout,
};
use shared_entity::dto::workspace_dto::{ use shared_entity::dto::workspace_dto::{
self, FavoriteFolderView, FolderView, FolderViewMinimal, RecentFolderView, TrashFolderView, self, FavoriteFolderView, FolderView, FolderViewMinimal, RecentFolderView, TrashFolderView,
ViewLayout, ViewLayout,

View File

@ -1,6 +1,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use actix_web::web::Data;
use app_error::AppError; use app_error::AppError;
use appflowy_collaborate::collab::storage::CollabAccessControlStorage; use appflowy_collaborate::collab::storage::CollabAccessControlStorage;
use chrono::DateTime; use chrono::DateTime;
@ -24,8 +25,12 @@ use collab_database::workspace_database::WorkspaceDatabaseBody;
use collab_document::document::Document; use collab_document::document::Document;
use collab_entity::CollabType; use collab_entity::CollabType;
use collab_entity::EncodedCollab; use collab_entity::EncodedCollab;
use collab_folder::hierarchy_builder::NestedChildViewBuilder;
use collab_folder::CollabOrigin; use collab_folder::CollabOrigin;
use collab_folder::Folder;
use collab_folder::SectionItem; use collab_folder::SectionItem;
use collab_folder::SpaceInfo;
use collab_rt_entity::user::RealtimeUser;
use database::collab::select_last_updated_database_row_ids; use database::collab::select_last_updated_database_row_ids;
use database::collab::select_workspace_database_oid; use database::collab::select_workspace_database_oid;
use database::collab::{CollabStorage, GetCollabOrigin}; use database::collab::{CollabStorage, GetCollabOrigin};
@ -48,8 +53,12 @@ use sqlx::PgPool;
use std::ops::DerefMut; use std::ops::DerefMut;
use yrs::Map; use yrs::Map;
use crate::api::metrics::AppFlowyWebMetrics;
use crate::api::ws::RealtimeServerAddr;
use crate::biz::collab::folder_view::check_if_view_is_space;
use crate::biz::collab::utils::get_database_row_doc_changes; use crate::biz::collab::utils::get_database_row_doc_changes;
use crate::biz::workspace::ops::broadcast_update_with_timeout; use crate::biz::workspace::ops::broadcast_update_with_timeout;
use crate::biz::workspace::page_view::update_workspace_folder_data;
use access_control::collab::CollabAccessControl; use access_control::collab::CollabAccessControl;
use anyhow::Context; use anyhow::Context;
use database_entity::dto::{ use database_entity::dto::{
@ -82,6 +91,8 @@ use super::utils::type_options_serde;
use super::utils::write_to_database_row; use super::utils::write_to_database_row;
use super::utils::CreatedRowDocument; use super::utils::CreatedRowDocument;
use super::utils::DocChanges; use super::utils::DocChanges;
use super::utils::DEFAULT_SPACE_ICON;
use super::utils::DEFAULT_SPACE_ICON_COLOR;
/// Create a new collab member /// Create a new collab member
/// If the collab member already exists, return [AppError::RecordAlreadyExists] /// If the collab member already exists, return [AppError::RecordAlreadyExists]
@ -293,10 +304,95 @@ pub async fn get_user_trash_folder_views(
Ok(section_items_to_trash_folder_view(&section_items, &folder)) Ok(section_items_to_trash_folder_view(&section_items, &folder))
} }
#[allow(clippy::too_many_arguments)]
fn patch_old_workspace_folder(
user: RealtimeUser,
workspace_id: &str,
folder: &mut Folder,
child_view_id_without_space: &[String],
) -> Result<Vec<u8>, AppError> {
let encoded_update = {
let space_id = Uuid::new_v4().to_string();
let space_view = NestedChildViewBuilder::new(user.uid, workspace_id.to_string())
.with_view_id(space_id.clone())
.with_name("General")
.with_extra(|extra| {
extra
.with_space_info(SpaceInfo {
space_icon: Some(DEFAULT_SPACE_ICON.to_string()),
space_icon_color: Some(DEFAULT_SPACE_ICON_COLOR.to_string()),
..Default::default()
})
.build()
})
.build()
.view;
let mut txn = folder.collab.transact_mut();
folder.body.views.insert(&mut txn, space_view, None);
for (i, current_view_id) in child_view_id_without_space.iter().enumerate() {
let previous_view_id = if i == 0 {
None
} else {
Some(child_view_id_without_space[i - 1].clone())
};
folder
.body
.move_nested_view(&mut txn, current_view_id, &space_id, previous_view_id);
}
txn.encode_update_v1()
};
Ok(encoded_update)
}
async fn fix_old_workspace_folder(
appflowy_web_metrics: &AppFlowyWebMetrics,
server: Data<RealtimeServerAddr>,
user: RealtimeUser,
mut folder: Folder,
workspace_id: Uuid,
) -> Result<Folder, AppError> {
let root_view = folder.get_view(&workspace_id.to_string()).ok_or_else(|| {
AppError::InvalidRequest(format!(
"Failed to get view for workspace_id: {}",
workspace_id
))
})?;
let direct_workspace_children: Vec<String> = root_view
.children
.iter()
.map(|view_id| view_id.to_string())
.collect();
let has_at_least_one_space = direct_workspace_children
.iter()
.filter_map(|view_id| folder.get_view(view_id))
.any(|view| check_if_view_is_space(&view));
if !has_at_least_one_space {
let folder_update = patch_old_workspace_folder(
user.clone(),
&workspace_id.to_string(),
&mut folder,
&direct_workspace_children,
)?;
update_workspace_folder_data(
appflowy_web_metrics,
server,
user,
workspace_id,
folder_update,
)
.await?;
}
Ok(folder)
}
#[allow(clippy::too_many_arguments)]
pub async fn get_user_workspace_structure( pub async fn get_user_workspace_structure(
appflowy_web_metrics: &AppFlowyWebMetrics,
server: Data<RealtimeServerAddr>,
collab_storage: &CollabAccessControlStorage, collab_storage: &CollabAccessControlStorage,
pg_pool: &PgPool, pg_pool: &PgPool,
uid: i64, user: RealtimeUser,
workspace_id: Uuid, workspace_id: Uuid,
depth: u32, depth: u32,
root_view_id: &str, root_view_id: &str,
@ -310,10 +406,13 @@ pub async fn get_user_workspace_structure(
} }
let folder = get_latest_collab_folder( let folder = get_latest_collab_folder(
collab_storage, collab_storage,
GetCollabOrigin::User { uid }, GetCollabOrigin::User { uid: user.uid },
&workspace_id.to_string(), &workspace_id.to_string(),
) )
.await?; .await?;
let patched_folder =
fix_old_workspace_folder(appflowy_web_metrics, server, user, folder, workspace_id).await?;
let publish_view_ids = select_published_view_ids_for_workspace(pg_pool, workspace_id).await?; let publish_view_ids = select_published_view_ids_for_workspace(pg_pool, workspace_id).await?;
let publish_view_ids: HashSet<String> = publish_view_ids let publish_view_ids: HashSet<String> = publish_view_ids
.into_iter() .into_iter()
@ -322,7 +421,7 @@ pub async fn get_user_workspace_structure(
collab_folder_to_folder_view( collab_folder_to_folder_view(
workspace_id, workspace_id,
root_view_id, root_view_id,
&folder, &patched_folder,
depth, depth,
&publish_view_ids, &publish_view_ids,
) )

View File

@ -34,6 +34,9 @@ use std::collections::HashSet;
use std::sync::Arc; use std::sync::Arc;
use yrs::Map; use yrs::Map;
pub const DEFAULT_SPACE_ICON: &str = "interface_essential/home-3";
pub const DEFAULT_SPACE_ICON_COLOR: &str = "0xFFA34AFD";
pub fn get_row_details_serde( pub fn get_row_details_serde(
row_detail: RowDetail, row_detail: RowDetail,
field_by_id_name_uniq: &HashMap<String, Field>, field_by_id_name_uniq: &HashMap<String, Field>,

View File

@ -36,7 +36,7 @@ use collab_document::document::Document;
use collab_document::document_data::default_document_data; use collab_document::document_data::default_document_data;
use collab_entity::{CollabType, EncodedCollab}; use collab_entity::{CollabType, EncodedCollab};
use collab_folder::hierarchy_builder::NestedChildViewBuilder; use collab_folder::hierarchy_builder::NestedChildViewBuilder;
use collab_folder::{timestamp, CollabOrigin, Folder}; use collab_folder::{timestamp, CollabOrigin, Folder, SpaceInfo};
use collab_rt_entity::user::RealtimeUser; use collab_rt_entity::user::RealtimeUser;
use database::collab::{select_workspace_database_oid, CollabStorage, GetCollabOrigin}; use database::collab::{select_workspace_database_oid, CollabStorage, GetCollabOrigin};
use database::publish::select_published_view_ids_for_workspace; use database::publish::select_published_view_ids_for_workspace;
@ -419,12 +419,14 @@ async fn add_new_space_to_folder(
.with_view_id(view_id) .with_view_id(view_id)
.with_name(name) .with_name(name)
.with_extra(|builder| { .with_extra(|builder| {
let mut extra = builder builder
.is_space(true, to_space_permission(space_permission)) .with_space_info(SpaceInfo {
.build(); space_icon: Some(space_icon.to_string()),
extra["space_icon_color"] = json!(space_icon_color); space_icon_color: Some(space_icon_color.to_string()),
extra["space_icon"] = json!(space_icon); space_permission: to_space_permission(space_permission),
extra ..Default::default()
})
.build()
}) })
.build() .build()
.view; .view;