chore: create default workspace database (#454)

* chore: create default workspace database

* chore: fix clppy
This commit is contained in:
Nathan.fooo 2024-04-06 21:04:12 +08:00 committed by GitHub
parent 182fefdcd7
commit 428c3feef2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 405 additions and 56 deletions

326
Cargo.lock generated
View File

@ -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",

View File

@ -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 = []

View File

@ -90,7 +90,7 @@ pub async fn create_user<'a, E: Executor<'a, Database = Postgres>>(
user_uuid: &Uuid,
email: &str,
name: &str,
) -> Result<String, AppError> {
) -> Result<Uuid, AppError> {
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]

View File

@ -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<Vec<AFWorkspaceRow>, 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<Vec<AFWorkspaceRow>, 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)
}

View File

@ -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"] }

View File

@ -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())

View File

@ -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<T>(
uid: i64,
workspace_id: &str,
row: &AFWorkspaceRow,
txn: &mut Transaction<'_, sqlx::Postgres>,
templates: Vec<T>,
collab_storage: &Arc<CollabAccessControlStorage>,
@ -20,11 +26,30 @@ pub async fn initialize_workspace_for_user<T>(
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<CollabAccessControlStorage>,
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::<MapPrelim<Any>>(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(())
}

View File

@ -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<bool,
event!(tracing::Level::INFO, "create new user:{}", new_uid);
let workspace_id =
create_user(txn.deref_mut(), new_uid, &user_uuid, &user.email, &name).await?;
let workspace_row = select_workspace(txn.deref_mut(), &workspace_id).await?;
// It's essential to cache the user's role because subsequent actions will rely on this cached information.
state
.workspace_access_control
.insert_role(
&new_uid,
&Uuid::parse_str(&workspace_id).unwrap(),
AFRole::Owner,
)
.insert_role(&new_uid, &workspace_id, AFRole::Owner)
.await?;
// Create a workspace with the GetStarted template
initialize_workspace_for_user(
new_uid,
&workspace_id,
&workspace_row,
&mut txn,
vec![GetStartedDocumentTemplate],
&state.collab_access_control_storage,

View File

@ -74,22 +74,22 @@ pub async fn create_workspace_for_user(
) -> Result<AFWorkspace, AppResponseError> {
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)
}