fix: migrate anon user to appflowy cloud (#4106)

* chore: fix migrate anon user to appflowy cloud

* chore: remove log

* fix: test

* chore: fmt
This commit is contained in:
Nathan.fooo 2023-12-06 06:54:17 -08:00 committed by GitHub
parent d765806337
commit 4837d7f7fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 470 additions and 68 deletions

View File

@ -202,4 +202,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 8c681999c7764593c94846b2a64b44d86f7a27ac
COCOAPODS: 1.12.1
COCOAPODS: 1.11.3

View File

@ -346,9 +346,11 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEAD_CODE_STRIPPING = NO;
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = VHB67HRSZG;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = AppFlowy;
@ -359,6 +361,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = com.appflowy.appflowy.flutter;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
STRIP_STYLE = "non-global";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
@ -482,9 +485,11 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEAD_CODE_STRIPPING = NO;
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = VHB67HRSZG;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = AppFlowy;
@ -495,6 +500,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = com.appflowy.appflowy.flutter;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
STRIP_STYLE = "non-global";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
@ -513,6 +519,8 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEAD_CODE_STRIPPING = NO;
DEVELOPMENT_TEAM = VHB67HRSZG;
@ -526,6 +534,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = com.appflowy.appflowy.flutter;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
STRIP_STYLE = "non-global";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;

View File

@ -139,7 +139,7 @@ checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[package]]
name = "app-error"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9"
dependencies = [
"anyhow",
"reqwest",
@ -786,7 +786,7 @@ dependencies = [
[[package]]
name = "client-api"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9"
dependencies = [
"anyhow",
"app-error",
@ -1469,7 +1469,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
[[package]]
name = "database-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9"
dependencies = [
"anyhow",
"app-error",
@ -2828,7 +2828,7 @@ dependencies = [
[[package]]
name = "gotrue"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9"
dependencies = [
"anyhow",
"futures-util",
@ -2844,7 +2844,7 @@ dependencies = [
[[package]]
name = "gotrue-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9"
dependencies = [
"anyhow",
"app-error",
@ -3266,7 +3266,7 @@ dependencies = [
[[package]]
name = "infra"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9"
dependencies = [
"anyhow",
"reqwest",
@ -5026,7 +5026,7 @@ dependencies = [
[[package]]
name = "realtime-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9"
dependencies = [
"anyhow",
"bincode",
@ -5779,7 +5779,7 @@ dependencies = [
[[package]]
name = "shared_entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9"
dependencies = [
"anyhow",
"app-error",
@ -7669,7 +7669,7 @@ dependencies = [
[[package]]
name = "workspace-template"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9"
dependencies = [
"anyhow",
"async-trait",

View File

@ -57,7 +57,7 @@ custom-protocol = ["tauri/custom-protocol"]
# Run the script:
# scripts/tool/update_client_api_rev.sh new_rev_id
# ⚠️⚠️⚠️️
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "f13a03beada8b7c3991f98016ccc0b708b22090c" }
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "ee31a680b5789b8c7c5397e6eadd4dd7739526f9" }
# Please use the following script to update collab.
# Working directory: frontend
#

View File

@ -125,7 +125,7 @@ checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[package]]
name = "app-error"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9"
dependencies = [
"anyhow",
"reqwest",
@ -667,7 +667,7 @@ dependencies = [
[[package]]
name = "client-api"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9"
dependencies = [
"anyhow",
"app-error",
@ -1147,7 +1147,7 @@ dependencies = [
"cssparser-macros",
"dtoa-short",
"itoa",
"phf 0.11.2",
"phf 0.8.0",
"smallvec",
]
@ -1275,7 +1275,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
[[package]]
name = "database-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9"
dependencies = [
"anyhow",
"app-error",
@ -2469,7 +2469,7 @@ dependencies = [
[[package]]
name = "gotrue"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9"
dependencies = [
"anyhow",
"futures-util",
@ -2485,7 +2485,7 @@ dependencies = [
[[package]]
name = "gotrue-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9"
dependencies = [
"anyhow",
"app-error",
@ -2846,7 +2846,7 @@ dependencies = [
[[package]]
name = "infra"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9"
dependencies = [
"anyhow",
"reqwest",
@ -3651,7 +3651,7 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
dependencies = [
"phf_macros 0.8.0",
"phf_macros",
"phf_shared 0.8.0",
"proc-macro-hack",
]
@ -3671,7 +3671,6 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
dependencies = [
"phf_macros 0.11.2",
"phf_shared 0.11.2",
]
@ -3739,19 +3738,6 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "phf_macros"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
dependencies = [
"phf_generator 0.11.2",
"phf_shared 0.11.2",
"proc-macro2",
"quote",
"syn 2.0.31",
]
[[package]]
name = "phf_shared"
version = "0.8.0"
@ -3955,7 +3941,7 @@ checksum = "8bdf592881d821b83d471f8af290226c8d51402259e9bb5be7f9f8bdebbb11ac"
dependencies = [
"bytes",
"heck 0.4.1",
"itertools 0.11.0",
"itertools 0.10.5",
"log",
"multimap",
"once_cell",
@ -3976,7 +3962,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "265baba7fabd416cf5078179f7d2cbeca4ce7a9041111900675ea7c4cb8a4c32"
dependencies = [
"anyhow",
"itertools 0.11.0",
"itertools 0.10.5",
"proc-macro2",
"quote",
"syn 2.0.31",
@ -4315,7 +4301,7 @@ dependencies = [
[[package]]
name = "realtime-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9"
dependencies = [
"anyhow",
"bincode",
@ -4981,7 +4967,7 @@ dependencies = [
[[package]]
name = "shared_entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9"
dependencies = [
"anyhow",
"app-error",
@ -6319,7 +6305,7 @@ dependencies = [
[[package]]
name = "workspace-template"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9"
dependencies = [
"anyhow",
"async-trait",

View File

@ -99,7 +99,7 @@ incremental = false
# Run the script:
# scripts/tool/update_client_api_rev.sh new_rev_id
# ⚠️⚠️⚠️️
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "f13a03beada8b7c3991f98016ccc0b708b22090c" }
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "ee31a680b5789b8c7c5397e6eadd4dd7739526f9" }
# Please use the following script to update collab.
# Working directory: frontend
#

View File

@ -1,2 +1,3 @@
mod auth_test;
mod member_test;
mod sync_anon_data_test;

View File

@ -1,5 +1,7 @@
pub use anon_user_data::*;
pub use sync_new_user::*;
pub use sync_af_cloud_new_user::*;
pub use sync_supabase_new_user::*;
mod anon_user_data;
mod sync_new_user;
mod sync_af_cloud_new_user;
mod sync_supabase_new_user;

View File

@ -0,0 +1,369 @@
use std::future::Future;
use std::ops::Deref;
use std::pin::Pin;
use std::sync::Arc;
use anyhow::{anyhow, Error};
use collab::core::collab::MutexCollab;
use collab::preclude::Collab;
use collab_database::database::get_database_row_ids;
use collab_database::rows::database_row_document_id_from_row_id;
use collab_database::user::{get_database_with_views, DatabaseWithViews};
use collab_entity::{CollabObject, CollabType};
use collab_folder::{Folder, View, ViewLayout};
use parking_lot::Mutex;
use collab_integrate::{PersistenceError, RocksCollabDB, YrsDocAction};
use flowy_error::FlowyResult;
use flowy_user_deps::cloud::UserCloudService;
use crate::migrations::MigrationUser;
#[tracing::instrument(level = "info", skip_all, err)]
pub async fn sync_af_user_data_to_cloud(
user_service: Arc<dyn UserCloudService>,
device_id: &str,
new_user: &MigrationUser,
collab_db: &Arc<RocksCollabDB>,
) -> FlowyResult<()> {
let workspace_id = new_user.session.user_workspace.id.clone();
let uid = new_user.session.user_id;
let folder = Arc::new(
sync_folder(
uid,
&workspace_id,
device_id,
collab_db,
user_service.clone(),
)
.await?,
);
let database_records = sync_database_views(
uid,
&workspace_id,
device_id,
&new_user.session.user_workspace.database_views_aggregate_id,
collab_db,
user_service.clone(),
)
.await;
let views = folder.lock().get_current_workspace_views();
for view in views {
let view_id = view.id.clone();
if let Err(err) = sync_view(
uid,
folder.clone(),
database_records.clone(),
workspace_id.to_string(),
device_id.to_string(),
view,
collab_db.clone(),
user_service.clone(),
)
.await
{
tracing::error!("🔴sync {} failed: {:?}", view_id, err);
}
}
tokio::task::yield_now().await;
Ok(())
}
#[allow(clippy::too_many_arguments)]
fn sync_view(
uid: i64,
folder: Arc<MutexFolder>,
database_records: Vec<Arc<DatabaseWithViews>>,
workspace_id: String,
device_id: String,
view: Arc<View>,
collab_db: Arc<RocksCollabDB>,
user_service: Arc<dyn UserCloudService>,
) -> Pin<Box<dyn Future<Output = Result<(), Error>> + Send + Sync>> {
Box::pin(async move {
let collab_type = collab_type_from_view_layout(&view.layout);
let object_id = object_id_from_view(&view, &database_records)?;
tracing::debug!(
"sync view: {:?}:{} with object_id: {}",
view.layout,
view.id,
object_id
);
let collab_object = CollabObject::new(
uid,
object_id,
collab_type,
workspace_id.to_string(),
device_id.clone(),
);
match view.layout {
ViewLayout::Document => {
let encode_v1 = get_collab_encode_v1(uid, &collab_object, &collab_db)?;
tracing::info!(
"sync object: {} with update: {}",
collab_object,
encode_v1.len()
);
user_service
.create_collab_object(&collab_object, encode_v1)
.await?;
},
ViewLayout::Grid | ViewLayout::Board | ViewLayout::Calendar => {
let (database_encode_v1, row_ids) =
get_database_encode_v1(uid, &collab_object, &collab_db)?;
tracing::info!(
"sync object: {} with update: {}",
collab_object,
database_encode_v1.len()
);
user_service
.create_collab_object(&collab_object, database_encode_v1)
.await?;
// sync database's row
for row_id in row_ids {
tracing::debug!("sync row: {}", row_id);
let document_id = database_row_document_id_from_row_id(&row_id);
let database_row_collab_object = CollabObject::new(
uid,
row_id,
CollabType::DatabaseRow,
workspace_id.to_string(),
device_id.clone(),
);
let database_row_encode_v1 =
get_collab_encode_v1(uid, &database_row_collab_object, &collab_db)?;
tracing::info!(
"sync object: {} with update: {}",
database_row_collab_object,
database_row_encode_v1.len()
);
let _ = user_service
.create_collab_object(&database_row_collab_object, database_row_encode_v1)
.await;
let database_row_document = CollabObject::new(
uid,
document_id,
CollabType::Document,
workspace_id.to_string(),
device_id.to_string(),
);
// sync document in the row if exist
if let Ok(document_encode_v1) =
get_collab_encode_v1(uid, &database_row_document, &collab_db)
{
tracing::info!(
"sync database row document: {} with update: {}",
database_row_document,
document_encode_v1.len()
);
let _ = user_service
.create_collab_object(&database_row_document, document_encode_v1)
.await;
}
}
},
}
tokio::task::yield_now().await;
let child_views = folder.lock().views.get_views_belong_to(&view.id);
for child_view in child_views {
let cloned_child_view = child_view.clone();
if let Err(err) = Box::pin(sync_view(
uid,
folder.clone(),
database_records.clone(),
workspace_id.clone(),
device_id.to_string(),
child_view,
collab_db.clone(),
user_service.clone(),
))
.await
{
tracing::error!(
"🔴sync {:?}:{} failed: {:?}",
cloned_child_view.layout,
cloned_child_view.id,
err
)
}
tokio::task::yield_now().await;
}
Ok(())
})
}
fn get_collab_encode_v1(
uid: i64,
collab_object: &CollabObject,
collab_db: &Arc<RocksCollabDB>,
) -> Result<Vec<u8>, PersistenceError> {
let collab = Collab::new(uid, &collab_object.object_id, "phantom", vec![]);
let _ = collab.with_origin_transact_mut(|txn| {
collab_db
.read_txn()
.load_doc_with_txn(uid, &collab_object.object_id, txn)
})?;
Ok(collab.encode_collab_v1().encode_to_bytes()?)
}
fn get_database_encode_v1(
uid: i64,
collab_object: &CollabObject,
collab_db: &Arc<RocksCollabDB>,
) -> Result<(Vec<u8>, Vec<String>), PersistenceError> {
let collab = Collab::new(uid, &collab_object.object_id, "phantom", vec![]);
let _ = collab.with_origin_transact_mut(|txn| {
collab_db
.read_txn()
.load_doc_with_txn(uid, &collab_object.object_id, txn)
})?;
let row_ids = get_database_row_ids(&collab).unwrap_or_default();
Ok((collab.encode_collab_v1().encode_to_bytes()?, row_ids))
}
async fn sync_folder(
uid: i64,
workspace_id: &str,
device_id: &str,
collab_db: &Arc<RocksCollabDB>,
user_service: Arc<dyn UserCloudService>,
) -> Result<MutexFolder, Error> {
let (folder, encode_v1) = {
let collab = Collab::new(uid, workspace_id, "phantom", vec![]);
// Use the temporary result to short the lifetime of the TransactionMut
collab.with_origin_transact_mut(|txn| {
collab_db
.read_txn()
.load_doc_with_txn(uid, workspace_id, txn)
})?;
let data = collab.encode_collab_v1().encode_to_bytes();
(
MutexFolder::new(Folder::open(
uid,
Arc::new(MutexCollab::from_collab(collab)),
None,
)?),
data,
)
};
let encode_v1 = encode_v1?;
let collab_object = CollabObject::new(
uid,
workspace_id.to_string(),
CollabType::Folder,
workspace_id.to_string(),
device_id.to_string(),
);
tracing::info!(
"sync object: {} with update: {}",
collab_object,
encode_v1.len()
);
if let Err(err) = user_service
.create_collab_object(&collab_object, encode_v1)
.await
{
tracing::error!("🔴sync folder failed: {:?}", err);
}
Ok(folder)
}
async fn sync_database_views(
uid: i64,
workspace_id: &str,
device_id: &str,
database_views_aggregate_id: &str,
collab_db: &Arc<RocksCollabDB>,
user_service: Arc<dyn UserCloudService>,
) -> Vec<Arc<DatabaseWithViews>> {
let collab_object = CollabObject::new(
uid,
database_views_aggregate_id.to_string(),
CollabType::WorkspaceDatabase,
workspace_id.to_string(),
device_id.to_string(),
);
// Use the temporary result to short the lifetime of the TransactionMut
let result = {
let collab = Collab::new(uid, database_views_aggregate_id, "phantom", vec![]);
collab
.with_origin_transact_mut(|txn| {
collab_db
.read_txn()
.load_doc_with_txn(uid, database_views_aggregate_id, txn)
})
.map(|_| {
(
get_database_with_views(&collab),
collab.encode_collab_v1().encode_to_bytes(),
)
})
};
if let Ok((records, encode_v1)) = result {
if let Ok(encode_v1) = encode_v1 {
let _ = user_service
.create_collab_object(&collab_object, encode_v1)
.await;
}
records.into_iter().map(Arc::new).collect()
} else {
vec![]
}
}
struct MutexFolder(Mutex<Folder>);
impl MutexFolder {
pub fn new(folder: Folder) -> Self {
Self(Mutex::new(folder))
}
}
impl Deref for MutexFolder {
type Target = Mutex<Folder>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
unsafe impl Sync for MutexFolder {}
unsafe impl Send for MutexFolder {}
fn collab_type_from_view_layout(view_layout: &ViewLayout) -> CollabType {
match view_layout {
ViewLayout::Document => CollabType::Document,
ViewLayout::Grid | ViewLayout::Board | ViewLayout::Calendar => CollabType::Database,
}
}
fn object_id_from_view(
view: &Arc<View>,
database_records: &[Arc<DatabaseWithViews>],
) -> Result<String, Error> {
if view.layout.is_database() {
match database_records
.iter()
.find(|record| record.linked_views.contains(&view.id))
{
None => Err(anyhow!(
"🔴sync view: {} failed: no database for this view",
view.id
)),
Some(record) => Ok(record.database_id.clone()),
}
} else {
Ok(view.id.clone())
}
}

View File

@ -20,7 +20,7 @@ use flowy_user_deps::cloud::UserCloudService;
use crate::migrations::MigrationUser;
#[tracing::instrument(level = "info", skip_all, err)]
pub async fn sync_user_data_to_cloud(
pub async fn sync_supabase_user_data_to_cloud(
user_service: Arc<dyn UserCloudService>,
device_id: &str,
new_user: &MigrationUser,
@ -52,7 +52,7 @@ pub async fn sync_user_data_to_cloud(
let views = folder.lock().get_current_workspace_views();
for view in views {
let view_id = view.id.clone();
if let Err(err) = sync_views(
if let Err(err) = sync_view(
uid,
folder.clone(),
database_records.clone(),
@ -72,7 +72,7 @@ pub async fn sync_user_data_to_cloud(
}
#[allow(clippy::too_many_arguments)]
fn sync_views(
fn sync_view(
uid: i64,
folder: Arc<MutexFolder>,
database_records: Vec<Arc<DatabaseWithViews>>,
@ -172,10 +172,12 @@ fn sync_views(
},
}
tokio::task::yield_now().await;
let child_views = folder.lock().views.get_views_belong_to(&view.id);
for child_view in child_views {
let cloned_child_view = child_view.clone();
if let Err(err) = Box::pin(sync_views(
if let Err(err) = Box::pin(sync_view(
uid,
folder.clone(),
database_records.clone(),
@ -194,6 +196,7 @@ fn sync_views(
err
)
}
tokio::task::yield_now().await;
}
Ok(())
})

View File

@ -26,7 +26,9 @@ use flowy_user_deps::entities::*;
use lib_dispatch::prelude::af_spawn;
use lib_infra::box_any::BoxAny;
use crate::anon_user_upgrade::{migration_anon_user_on_sign_up, sync_user_data_to_cloud};
use crate::anon_user_upgrade::{
migration_anon_user_on_sign_up, sync_af_user_data_to_cloud, sync_supabase_user_data_to_cloud,
};
use crate::entities::{AuthStateChangedPB, AuthStatePB, UserProfilePB, UserSettingPB};
use crate::event_map::{DefaultUserStatusCallback, UserCloudServiceProvider, UserStatusCallback};
use crate::migrations::document_empty_content::HistoricalEmptyDocumentMigration;
@ -357,30 +359,30 @@ impl UserManager {
params: BoxAny,
) -> Result<UserProfile, FlowyError> {
// sign out the current user if there is one
let migration_user = self.get_migration_user(&authenticator).await;
let _ = self.sign_out().await;
self.update_authenticator(&authenticator).await;
let migration_user = self.get_migration_user(&authenticator).await;
let auth_service = self.cloud_services.get_user_service()?;
let response: AuthResponse = auth_service.sign_up(params).await?;
let user_profile = UserProfile::from((&response, &authenticator));
if user_profile.encryption_type.require_encrypt_secret() {
let new_user_profile = UserProfile::from((&response, &authenticator));
if new_user_profile.encryption_type.require_encrypt_secret() {
self
.resumable_sign_up
.lock()
.await
.replace(ResumableSignUp {
user_profile: user_profile.clone(),
user_profile: new_user_profile.clone(),
migration_user,
response,
authenticator,
});
} else {
self
.continue_sign_up(&user_profile, migration_user, response, &authenticator)
.continue_sign_up(&new_user_profile, migration_user, response, &authenticator)
.await?;
}
Ok(user_profile)
Ok(new_user_profile)
}
#[tracing::instrument(level = "info", skip(self))]
@ -408,7 +410,7 @@ impl UserManager {
#[tracing::instrument(level = "info", skip_all, err)]
async fn continue_sign_up(
&self,
user_profile: &UserProfile,
new_user_profile: &UserProfile,
migration_user: Option<MigrationUser>,
response: AuthResponse,
authenticator: &Authenticator,
@ -425,7 +427,7 @@ impl UserManager {
if response.is_new_user {
if let Some(old_user) = migration_user {
let new_user = MigrationUser {
user_profile: user_profile.clone(),
user_profile: new_user_profile.clone(),
session: new_session.clone(),
};
event!(
@ -435,7 +437,7 @@ impl UserManager {
new_user.user_profile.uid
);
self
.migrate_anon_user_data_to_cloud(&old_user, &new_user)
.migrate_anon_user_data_to_cloud(&old_user, &new_user, authenticator)
.await?;
let _ = self.database.close(old_user.session.user_id);
}
@ -454,7 +456,7 @@ impl UserManager {
.await
.did_sign_up(
response.is_new_user,
user_profile,
new_user_profile,
&new_session.user_workspace,
&self.user_config.device_id,
)
@ -791,20 +793,38 @@ impl UserManager {
&self,
old_user: &MigrationUser,
new_user: &MigrationUser,
authenticator: &Authenticator,
) -> Result<(), FlowyError> {
let old_collab_db = self.database.get_collab_db(old_user.session.user_id)?;
let new_collab_db = self.database.get_collab_db(new_user.session.user_id)?;
migration_anon_user_on_sign_up(old_user, &old_collab_db, new_user, &new_collab_db)?;
if let Err(err) = sync_user_data_to_cloud(
self.cloud_services.get_user_service()?,
&self.user_config.device_id,
new_user,
&new_collab_db,
)
.await
{
error!("Sync user data to cloud failed: {:?}", err);
match authenticator {
Authenticator::Supabase => {
if let Err(err) = sync_supabase_user_data_to_cloud(
self.cloud_services.get_user_service()?,
&self.user_config.device_id,
new_user,
&new_collab_db,
)
.await
{
error!("Sync user data to cloud failed: {:?}", err);
}
},
Authenticator::AppFlowyCloud => {
if let Err(err) = sync_af_user_data_to_cloud(
self.cloud_services.get_user_service()?,
&self.user_config.device_id,
new_user,
&new_collab_db,
)
.await
{
error!("Sync user data to cloud failed: {:?}", err);
}
},
_ => {},
}
// Save the old user workspace setting.

View File

@ -2,7 +2,7 @@ use flowy_user_deps::entities::UserProfile;
use crate::services::entities::Session;
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct MigrationUser {
pub user_profile: UserProfile,
pub session: Session,

View File

@ -34,6 +34,15 @@ pub struct UserDB {
impl UserDB {
pub fn new(paths: impl UserDBPath) -> Self {
// if let Some(mut db) = DB_MAP.try_write_for(Duration::from_millis(300)) {
// info!("clear sqlite db map");
// db.clear();
// }
//
// if let Some(mut collab_db) = COLLAB_DB_MAP.try_write_for(Duration::from_millis(300)) {
// info!("clear collab db map");
// collab_db.clear();
// }
Self {
paths: Box::new(paths),
}

View File

@ -1,4 +1,5 @@
use diesel::RunQueryDsl;
use tracing::instrument;
use flowy_error::FlowyResult;
use flowy_sqlite::schema::user_workspace_table;
@ -13,6 +14,7 @@ use crate::services::user_workspace_sql::UserWorkspaceTable;
const HISTORICAL_USER: &str = "af_historical_users";
impl UserManager {
#[instrument(skip_all)]
pub async fn get_migration_user(&self, auth_type: &Authenticator) -> Option<MigrationUser> {
// Only migrate the data if the user is login in as a guest and sign up as a new user if the current
// auth type is not [AuthType::Local].