diff --git a/Cargo.lock b/Cargo.lock index 99802ed3..dcf9f29e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,6 +23,18 @@ dependencies = [ "tracing", ] +[[package]] +name = "accessory" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "850bb534b9dc04744fbbb71d30ad6d25a7e4cf6dc33e223c81ef3a92ebab4e0b" +dependencies = [ + "macroific", + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "actix" version = "0.13.3" @@ -964,6 +976,27 @@ dependencies = [ "serde", ] +[[package]] +name = "bindgen" +version = "0.65.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.48", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -1144,6 +1177,17 @@ dependencies = [ "bytes", ] +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "camino" version = "1.1.6" @@ -1212,6 +1256,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "0.1.10" @@ -1282,6 +1335,17 @@ dependencies = [ "inout", ] +[[package]] +name = "clang-sys" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" version = "4.4.18" @@ -1449,6 +1513,36 @@ dependencies = [ "yrs", ] +[[package]] +name = "collab-database" +version = "0.1.0" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=5f66f8c921646d7d8762cafc8bbec72d56c2e157#5f66f8c921646d7d8762cafc8bbec72d56c2e157" +dependencies = [ + "anyhow", + "async-trait", + "chrono", + "collab", + "collab-entity", + "collab-plugins", + "dashmap", + "getrandom 0.2.12", + "js-sys", + "lazy_static", + "nanoid", + "parking_lot 0.12.1", + "rayon", + "serde", + "serde_json", + "serde_repr", + "strum", + "strum_macros", + "thiserror", + "tokio", + "tokio-stream", + "tracing", + "uuid", +] + [[package]] name = "collab-document" version = "0.1.0" @@ -1525,6 +1619,44 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "collab-plugins" +version = "0.1.0" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=5f66f8c921646d7d8762cafc8bbec72d56c2e157#5f66f8c921646d7d8762cafc8bbec72d56c2e157" +dependencies = [ + "anyhow", + "async-stream", + "async-trait", + "bincode", + "bytes", + "chrono", + "collab", + "collab-entity", + "futures", + "futures-util", + "getrandom 0.2.12", + "indexed_db_futures", + "js-sys", + "lazy_static", + "parking_lot 0.12.1", + "rocksdb", + "serde", + "serde_json", + "similar", + "smallvec", + "thiserror", + "tokio", + "tokio-retry", + "tokio-stream", + "tracing", + "tracing-wasm", + "uuid", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "yrs", +] + [[package]] name = "collab-rt" version = "0.1.0" @@ -2016,6 +2148,18 @@ dependencies = [ "byteorder", ] +[[package]] +name = "delegate-display" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98a85201f233142ac819bbf6226e36d0b5e129a47bd325084674261c82d4cd66" +dependencies = [ + "macroific", + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "der" version = "0.7.8" @@ -2228,6 +2372,18 @@ dependencies = [ "regex", ] +[[package]] +name = "fancy_constructor" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f71f317e4af73b2f8f608fac190c52eac4b1879d2145df1db2fe48881ca69435" +dependencies = [ + "macroific", + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "fastrand" version = "2.0.1" @@ -2987,6 +3143,23 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206ca75c9c03ba3d4ace2460e57b189f39f43de612c2f85836e65c929701bb2d" +[[package]] +name = "indexed_db_futures" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cc2083760572ee02385ab8b7c02c20925d2dd1f97a1a25a8737a238608f1152" +dependencies = [ + "accessory", + "cfg-if 1.0.0", + "delegate-display", + "fancy_constructor", + "js-sys", + "uuid", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "indexmap" version = "2.2.5" @@ -3146,12 +3319,28 @@ dependencies = [ "spin 0.5.2", ] +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +[[package]] +name = "libloading" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +dependencies = [ + "cfg-if 1.0.0", + "windows-targets 0.52.0", +] + [[package]] name = "libm" version = "0.2.8" @@ -3169,6 +3358,21 @@ dependencies = [ "redox_syscall 0.4.1", ] +[[package]] +name = "librocksdb-sys" +version = "0.11.0+8.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3386f101bcb4bd252d8e9d2fb41ec3b0862a15a62b478c355b2982efa469e3e" +dependencies = [ + "bindgen", + "bzip2-sys", + "cc", + "glob", + "libc", + "libz-sys", + "zstd-sys", +] + [[package]] name = "libsqlite3-sys" version = "0.27.0" @@ -3180,6 +3384,17 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "libz-sys" +version = "1.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -3234,6 +3449,53 @@ dependencies = [ "libc", ] +[[package]] +name = "macroific" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05c00ac596022625d01047c421a0d97d7f09a18e429187b341c201cb631b9dd" +dependencies = [ + "macroific_attr_parse", + "macroific_core", + "macroific_macro", +] + +[[package]] +name = "macroific_attr_parse" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd94d5da95b30ae6e10621ad02340909346ad91661f3f8c0f2b62345e46a2f67" +dependencies = [ + "cfg-if 1.0.0", + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "macroific_core" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13198c120864097a565ccb3ff947672d969932b7975ebd4085732c9f09435e55" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "macroific_macro" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c9853143cbed7f1e41dc39fee95f9b361bec65c8dc2a01bf609be01b61f5ae" +dependencies = [ + "macroific_attr_parse", + "macroific_core", + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "markup5ever" version = "0.11.0" @@ -3730,6 +3992,12 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + [[package]] name = "pem" version = "1.1.1" @@ -4328,9 +4596,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.8.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -4639,6 +4907,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "rocksdb" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb6f170a4041d50a0ce04b0d2e14916d6ca863ea2e422689a5b694395d299ffe" +dependencies = [ + "libc", + "librocksdb-sys", +] + [[package]] name = "rsa" version = "0.9.6" @@ -4724,6 +5002,12 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc_version" version = "0.4.0" @@ -5111,6 +5395,12 @@ dependencies = [ "validator", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -5136,6 +5426,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" +[[package]] +name = "similar" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" + [[package]] name = "simple_asn1" version = "0.6.2" @@ -5515,6 +5811,25 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" + +[[package]] +name = "strum_macros" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.48", +] + [[package]] name = "subtle" version = "2.5.0" @@ -6201,12 +6516,14 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "uuid" -version = "1.7.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" dependencies = [ "getrandom 0.2.12", "serde", + "sha1_smol", + "wasm-bindgen", ] [[package]] @@ -6710,6 +7027,7 @@ dependencies = [ "async-trait", "bytes", "collab", + "collab-database", "collab-document", "collab-entity", "collab-folder", diff --git a/Cargo.toml b/Cargo.toml index 6723f595..0509ad0e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -203,6 +203,7 @@ collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "5f66f8 collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "5f66f8c921646d7d8762cafc8bbec72d56c2e157" } collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "5f66f8c921646d7d8762cafc8bbec72d56c2e157" } collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "5f66f8c921646d7d8762cafc8bbec72d56c2e157" } +collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "5f66f8c921646d7d8762cafc8bbec72d56c2e157" } [features] ai_enable = [] \ No newline at end of file diff --git a/libs/database/src/user.rs b/libs/database/src/user.rs index 3fa75cc8..a1e76a17 100644 --- a/libs/database/src/user.rs +++ b/libs/database/src/user.rs @@ -90,7 +90,7 @@ pub async fn create_user<'a, E: Executor<'a, Database = Postgres>>( user_uuid: &Uuid, email: &str, name: &str, -) -> Result { +) -> Result { let name = { if name.is_empty() { email @@ -132,7 +132,7 @@ pub async fn create_user<'a, E: Executor<'a, Database = Postgres>>( .fetch_one(executor) .await?; - Ok(row.workspace_id.to_string()) + Ok(row.workspace_id) } #[inline] diff --git a/libs/database/src/workspace.rs b/libs/database/src/workspace.rs index e36b7742..9cf2fa6f 100644 --- a/libs/database/src/workspace.rs +++ b/libs/database/src/workspace.rs @@ -577,43 +577,11 @@ pub async fn update_updated_at_of_workspace<'a, E: Executor<'a, Database = Postg Ok(()) } -/// Returns a list of workspaces that the user is a member of. -#[inline] -pub async fn select_user_workspace<'a, E: Executor<'a, Database = Postgres>>( - executor: E, - user_uuid: &Uuid, -) -> Result, AppError> { - let workspaces = sqlx::query_as!( - AFWorkspaceRow, - r#" - SELECT - w.workspace_id, - w.database_storage_id, - w.owner_uid, - (SELECT name FROM public.af_user WHERE uid = w.owner_uid) AS owner_name, - w.created_at, - w.workspace_type, - w.deleted_at, - w.workspace_name, - w.icon - FROM af_workspace w - JOIN af_workspace_member wm ON w.workspace_id = wm.workspace_id - WHERE wm.uid = ( - SELECT uid FROM public.af_user WHERE uuid = $1 - ); - "#, - user_uuid - ) - .fetch_all(executor) - .await?; - Ok(workspaces) -} - /// Returns a list of workspaces that the user is part of. /// User may owner or non-owner. #[inline] -pub async fn select_all_user_workspaces( - pool: &PgPool, +pub async fn select_all_user_workspaces<'a, E: Executor<'a, Database = Postgres>>( + executor: E, user_uuid: &Uuid, ) -> Result, AppError> { let workspaces = sqlx::query_as!( @@ -637,7 +605,7 @@ pub async fn select_all_user_workspaces( "#, user_uuid ) - .fetch_all(pool) + .fetch_all(executor) .await?; Ok(workspaces) } diff --git a/libs/workspace-template/Cargo.toml b/libs/workspace-template/Cargo.toml index 00c4d81e..13e5634d 100644 --- a/libs/workspace-template/Cargo.toml +++ b/libs/workspace-template/Cargo.toml @@ -13,6 +13,7 @@ collab = { version = "0.1.0"} collab-folder = { version = "0.1.0"} collab-document = { version = "0.1.0"} collab-entity = { version = "0.1.0"} +collab-database = { version = "0.1.0" } async-trait = "0.1.77" anyhow.workspace = true tokio = { workspace = true, features = ["sync"] } diff --git a/src/biz/user/user_info.rs b/src/biz/user/user_info.rs index 9b544938..470e39a4 100644 --- a/src/biz/user/user_info.rs +++ b/src/biz/user/user_info.rs @@ -1,6 +1,6 @@ use anyhow::Context; use app_error::AppError; -use database::workspace::{select_user_profile, select_user_workspace, select_workspace}; +use database::workspace::{select_all_user_workspaces, select_user_profile, select_workspace}; use database_entity::dto::{AFUserProfile, AFUserWorkspaceInfo, AFWorkspace}; use serde_json::json; use shared_entity::dto::auth_dto::UpdateUserParams; @@ -48,7 +48,7 @@ pub async fn get_user_workspace_info( let user_profile = AFUserProfile::try_from(row)?; // Get all workspaces that the user can access to - let workspaces = select_user_workspace(txn.deref_mut(), uuid) + let workspaces = select_all_user_workspaces(txn.deref_mut(), uuid) .await? .into_iter() .flat_map(|row| AFWorkspace::try_from(row).ok()) diff --git a/src/biz/user/user_init.rs b/src/biz/user/user_init.rs index b2f78a83..544d458c 100644 --- a/src/biz/user/user_init.rs +++ b/src/biz/user/user_init.rs @@ -1,6 +1,12 @@ use crate::biz::collab::storage::CollabAccessControlStorage; use app_error::AppError; + +use collab::core::origin::CollabOrigin; +use collab::preclude::{Any, Collab, MapPrelim}; +use collab_entity::define::WORKSPACE_DATABASES; +use collab_entity::CollabType; use database::collab::CollabStorage; +use database::pg_row::AFWorkspaceRow; use database_entity::dto::CollabParams; use sqlx::Transaction; use std::sync::Arc; @@ -12,7 +18,7 @@ use workspace_template::{WorkspaceTemplate, WorkspaceTemplateBuilder}; #[instrument(level = "debug", skip_all, err)] pub async fn initialize_workspace_for_user( uid: i64, - workspace_id: &str, + row: &AFWorkspaceRow, txn: &mut Transaction<'_, sqlx::Postgres>, templates: Vec, collab_storage: &Arc, @@ -20,11 +26,30 @@ pub async fn initialize_workspace_for_user( where T: WorkspaceTemplate + Send + Sync + 'static, { - let templates = WorkspaceTemplateBuilder::new(uid, workspace_id) + let workspace_id = row.workspace_id.to_string(); + let templates = WorkspaceTemplateBuilder::new(uid, &workspace_id) .with_templates(templates) .build() .await?; + // Create a workspace database object for given user + // The database_storage_id is auto-generated when the workspace is created. So, it should be available + if let Some(database_storage_id) = row.database_storage_id.as_ref() { + let workspace_database_object_id = database_storage_id.to_string(); + create_workspace_database_collab( + &workspace_id, + &uid, + &workspace_database_object_id, + collab_storage, + txn, + ) + .await?; + } else { + return Err(AppError::Internal(anyhow::anyhow!( + "Workspace database object id is missing" + ))); + } + debug!("create {} templates for user:{}", templates.len(), uid); for template in templates { let object_id = template.object_id; @@ -35,7 +60,7 @@ where collab_storage .insert_or_update_collab_with_transaction( - workspace_id, + &workspace_id, &uid, CollabParams { object_id, @@ -48,3 +73,41 @@ where } Ok(()) } + +async fn create_workspace_database_collab( + workspace_id: &str, + uid: &i64, + object_id: &str, + storage: &Arc, + txn: &mut Transaction<'_, sqlx::Postgres>, +) -> Result<(), AppError> { + let collab_type = CollabType::WorkspaceDatabase; + let collab = Collab::new_with_origin(CollabOrigin::Empty, object_id, vec![], false); + let _ = collab.with_origin_transact_mut(|txn| { + collab.create_array_with_txn::>(txn, WORKSPACE_DATABASES, vec![]); + Ok::<(), AppError>(()) + }); + + let encode_collab = collab + .encode_collab_v1(|collab| collab_type.validate(collab)) + .map_err(AppError::Internal)?; + + let encoded_collab_v1 = encode_collab + .encode_to_bytes() + .map_err(|err| AppError::Internal(anyhow::Error::from(err)))?; + + storage + .insert_or_update_collab_with_transaction( + workspace_id, + uid, + CollabParams { + object_id: object_id.to_string(), + encoded_collab_v1, + collab_type, + }, + txn, + ) + .await?; + + Ok(()) +} diff --git a/src/biz/user/user_verify.rs b/src/biz/user/user_verify.rs index 617c1221..b5483460 100644 --- a/src/biz/user/user_verify.rs +++ b/src/biz/user/user_verify.rs @@ -5,11 +5,12 @@ use anyhow::{Context, Result}; use app_error::AppError; use database::pg_row::AFUserNotification; use database::user::{create_user, is_user_exist}; +use database::workspace::select_workspace; use database_entity::dto::AFRole; use sqlx::types::uuid; use std::ops::DerefMut; use tracing::{event, instrument, trace}; -use uuid::Uuid; + use workspace_template::document::get_started::GetStartedDocumentTemplate; /// Verify the token from the gotrue server and create the user if it is a new user @@ -44,21 +45,18 @@ pub async fn verify_token(access_token: &str, state: &AppState) -> Result Result { let mut txn = pg_pool.begin().await?; let new_workspace_row = insert_user_workspace(&mut txn, user_uuid, workspace_name).await?; - let new_workspace = AFWorkspace::try_from(new_workspace_row)?; workspace_access_control - .insert_role(&user_uid, &new_workspace.workspace_id, AFRole::Owner) + .insert_role(&user_uid, &new_workspace_row.workspace_id, AFRole::Owner) .await?; // add create initial collab for user initialize_workspace_for_user( user_uid, - new_workspace.workspace_id.to_string().as_str(), + &new_workspace_row, &mut txn, vec![GetStartedDocumentTemplate], collab_storage, ) .await?; + let new_workspace = AFWorkspace::try_from(new_workspace_row)?; txn.commit().await?; Ok(new_workspace) }