chore: checking workspace state consistent after switching workspace (#5201)

* refactor: getting workspace id

* refactor: check workspace id is match for http response

* refactor: check http repsonse in valid by checing the workspace id

* chore: update log

* chore: fix test

* chore: fix test

* chore: add test

* chore: update test
This commit is contained in:
Nathan.fooo 2024-04-26 09:44:07 +08:00 committed by GitHub
parent 65a289648e
commit cc66147bc0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
51 changed files with 980 additions and 575 deletions

View File

@ -184,12 +184,6 @@ void _resolveHomeDeps(GetIt getIt) {
(user, _) => UserListener(userProfile: user), (user, _) => UserListener(userProfile: user),
); );
getIt.registerFactoryParam<WorkspaceBloc, UserProfilePB, void>(
(user, _) => WorkspaceBloc(
userService: UserBackendService(userId: user.id),
),
);
// share // share
getIt.registerFactoryParam<DocumentShareBloc, ViewPB, void>( getIt.registerFactoryParam<DocumentShareBloc, ViewPB, void>(
(view, _) => DocumentShareBloc(view: view), (view, _) => DocumentShareBloc(view: view),

View File

@ -162,7 +162,7 @@ checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
[[package]] [[package]]
name = "app-error" name = "app-error"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode", "bincode",
@ -740,7 +740,7 @@ dependencies = [
[[package]] [[package]]
name = "client-api" name = "client-api"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"again", "again",
"anyhow", "anyhow",
@ -786,7 +786,7 @@ dependencies = [
[[package]] [[package]]
name = "client-websocket" name = "client-websocket"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"futures-channel", "futures-channel",
"futures-util", "futures-util",
@ -860,7 +860,7 @@ dependencies = [
[[package]] [[package]]
name = "collab" name = "collab"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -884,7 +884,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-database" name = "collab-database"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -914,7 +914,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-document" name = "collab-document"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",
@ -933,7 +933,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-entity" name = "collab-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
@ -948,7 +948,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-folder" name = "collab-folder"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
@ -986,7 +986,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-plugins" name = "collab-plugins"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-stream", "async-stream",
@ -1025,7 +1025,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-rt-entity" name = "collab-rt-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode", "bincode",
@ -1050,7 +1050,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-rt-protocol" name = "collab-rt-protocol"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode", "bincode",
@ -1064,7 +1064,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-user" name = "collab-user"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",
@ -1404,7 +1404,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
[[package]] [[package]]
name = "database-entity" name = "database-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -2770,7 +2770,7 @@ dependencies = [
[[package]] [[package]]
name = "gotrue" name = "gotrue"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"futures-util", "futures-util",
@ -2787,7 +2787,7 @@ dependencies = [
[[package]] [[package]]
name = "gotrue-entity" name = "gotrue-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -3219,7 +3219,7 @@ dependencies = [
[[package]] [[package]]
name = "infra" name = "infra"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"reqwest", "reqwest",
@ -5707,7 +5707,7 @@ dependencies = [
[[package]] [[package]]
name = "shared-entity" name = "shared-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",

View File

@ -87,7 +87,7 @@ yrs = { git = "https://github.com/appflowy/y-crdt", rev = "3f25bb510ca5274e7657d
# Run the script: # Run the script:
# scripts/tool/update_client_api_rev.sh new_rev_id # scripts/tool/update_client_api_rev.sh new_rev_id
# ⚠️⚠️⚠️️ # ⚠️⚠️⚠️️
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" } client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "54dfeb552752eb0e4d887af3a0affaa80863943d" }
# Please use the following script to update collab. # Please use the following script to update collab.
# Working directory: frontend # Working directory: frontend
# #
@ -97,10 +97,10 @@ client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "fda
# To switch to the local path, run: # To switch to the local path, run:
# scripts/tool/update_collab_source.sh # scripts/tool/update_collab_source.sh
# ⚠️⚠️⚠️️ # ⚠️⚠️⚠️️
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" } collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" } collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" } collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" } collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" } collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" } collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" } collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }

View File

@ -112,6 +112,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"af-persistence", "af-persistence",
"af-user", "af-user",
"anyhow",
"collab", "collab",
"collab-integrate", "collab-integrate",
"console_error_panic_hook", "console_error_panic_hook",
@ -215,7 +216,7 @@ checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
[[package]] [[package]]
name = "app-error" name = "app-error"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode", "bincode",
@ -541,7 +542,7 @@ dependencies = [
[[package]] [[package]]
name = "client-api" name = "client-api"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"again", "again",
"anyhow", "anyhow",
@ -587,7 +588,7 @@ dependencies = [
[[package]] [[package]]
name = "client-websocket" name = "client-websocket"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"futures-channel", "futures-channel",
"futures-util", "futures-util",
@ -631,7 +632,7 @@ dependencies = [
[[package]] [[package]]
name = "collab" name = "collab"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -655,7 +656,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-document" name = "collab-document"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",
@ -674,7 +675,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-entity" name = "collab-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
@ -689,7 +690,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-folder" name = "collab-folder"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
@ -727,7 +728,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-plugins" name = "collab-plugins"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-stream", "async-stream",
@ -765,7 +766,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-rt-entity" name = "collab-rt-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode", "bincode",
@ -790,7 +791,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-rt-protocol" name = "collab-rt-protocol"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode", "bincode",
@ -804,7 +805,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-user" name = "collab-user"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",
@ -1001,7 +1002,7 @@ checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
[[package]] [[package]]
name = "database-entity" name = "database-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -1774,7 +1775,7 @@ dependencies = [
[[package]] [[package]]
name = "gotrue" name = "gotrue"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"futures-util", "futures-util",
@ -1791,7 +1792,7 @@ dependencies = [
[[package]] [[package]]
name = "gotrue-entity" name = "gotrue-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -2092,7 +2093,7 @@ dependencies = [
[[package]] [[package]]
name = "infra" name = "infra"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"reqwest", "reqwest",
@ -3718,7 +3719,7 @@ dependencies = [
[[package]] [[package]]
name = "shared-entity" name = "shared-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -4980,4 +4981,4 @@ dependencies = [
[[patch.unused]] [[patch.unused]]
name = "collab-database" name = "collab-database"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"

View File

@ -55,7 +55,7 @@ codegen-units = 1
# Run the script: # Run the script:
# scripts/tool/update_client_api_rev.sh new_rev_id # scripts/tool/update_client_api_rev.sh new_rev_id
# ⚠️⚠️⚠️️ # ⚠️⚠️⚠️️
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" } client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "54dfeb552752eb0e4d887af3a0affaa80863943d" }
# Please use the following script to update collab. # Please use the following script to update collab.
# Working directory: frontend # Working directory: frontend
# #
@ -65,10 +65,10 @@ client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "fda
# To switch to the local path, run: # To switch to the local path, run:
# scripts/tool/update_collab_source.sh # scripts/tool/update_collab_source.sh
# ⚠️⚠️⚠️️ # ⚠️⚠️⚠️️
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" } collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" } collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" } collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" } collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" } collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" } collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" } collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }

View File

@ -36,7 +36,7 @@ wasm-bindgen-futures.workspace = true
uuid.workspace = true uuid.workspace = true
serde-wasm-bindgen.workspace = true serde-wasm-bindgen.workspace = true
js-sys = "0.3.67" js-sys = "0.3.67"
anyhow = "1.0"
# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size # `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
# compared to the default allocator's ~10K. However, it is slower than the default # compared to the default allocator's ~10K. However, it is slower than the default

View File

@ -4,7 +4,7 @@ use crate::integrate::server::ServerProviderWASM;
use af_persistence::store::AppFlowyWASMStore; use af_persistence::store::AppFlowyWASMStore;
use af_user::authenticate_user::AuthenticateUser; use af_user::authenticate_user::AuthenticateUser;
use af_user::manager::UserManager; use af_user::manager::UserManager;
use collab_integrate::collab_builder::AppFlowyCollabBuilder; use collab_integrate::collab_builder::{AppFlowyCollabBuilder, WorkspaceCollabIntegrate};
use flowy_document::manager::DocumentManager; use flowy_document::manager::DocumentManager;
use flowy_error::FlowyResult; use flowy_error::FlowyResult;
use flowy_folder::manager::FolderManager; use flowy_folder::manager::FolderManager;
@ -27,13 +27,13 @@ impl AppFlowyWASMCore {
pub async fn new(device_id: &str, cloud_config: AFCloudConfiguration) -> FlowyResult<Self> { pub async fn new(device_id: &str, cloud_config: AFCloudConfiguration) -> FlowyResult<Self> {
let runtime = Arc::new(AFPluginRuntime::new().unwrap()); let runtime = Arc::new(AFPluginRuntime::new().unwrap());
let server_provider = Rc::new(ServerProviderWASM::new(device_id, cloud_config)); let server_provider = Rc::new(ServerProviderWASM::new(device_id, cloud_config));
let collab_builder = Arc::new(AppFlowyCollabBuilder::new(
server_provider.clone(),
device_id.to_string(),
));
let store = Rc::new(AppFlowyWASMStore::new().await?); let store = Rc::new(AppFlowyWASMStore::new().await?);
let auth_user = Rc::new(AuthenticateUser::new(store.clone()).await?); let auth_user = Rc::new(AuthenticateUser::new(store.clone()).await?);
let collab_builder = Arc::new(AppFlowyCollabBuilder::new(
device_id.to_string(),
server_provider.clone(),
WorkspaceCollabIntegrateImpl(auth_user.clone()),
));
let document_manager = DocumentDepsResolver::resolve( let document_manager = DocumentDepsResolver::resolve(
Rc::downgrade(&auth_user), Rc::downgrade(&auth_user),
@ -75,3 +75,15 @@ impl AppFlowyWASMCore {
}) })
} }
} }
struct WorkspaceCollabIntegrateImpl(Rc<AuthenticateUser>);
impl WorkspaceCollabIntegrate for WorkspaceCollabIntegrateImpl {
fn workspace_id(&self) -> Result<String, anyhow::Error> {
let workspace_id = self.0.workspace_id()?;
Ok(workspace_id)
}
fn device_id(&self) -> Result<String, anyhow::Error> {
Ok("fake device id".to_string())
}
}

View File

@ -153,7 +153,7 @@ checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
[[package]] [[package]]
name = "app-error" name = "app-error"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode", "bincode",
@ -714,7 +714,7 @@ dependencies = [
[[package]] [[package]]
name = "client-api" name = "client-api"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"again", "again",
"anyhow", "anyhow",
@ -760,7 +760,7 @@ dependencies = [
[[package]] [[package]]
name = "client-websocket" name = "client-websocket"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"futures-channel", "futures-channel",
"futures-util", "futures-util",
@ -843,7 +843,7 @@ dependencies = [
[[package]] [[package]]
name = "collab" name = "collab"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -867,7 +867,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-database" name = "collab-database"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -897,7 +897,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-document" name = "collab-document"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",
@ -916,7 +916,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-entity" name = "collab-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
@ -931,7 +931,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-folder" name = "collab-folder"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
@ -969,7 +969,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-plugins" name = "collab-plugins"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-stream", "async-stream",
@ -1008,7 +1008,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-rt-entity" name = "collab-rt-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode", "bincode",
@ -1033,7 +1033,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-rt-protocol" name = "collab-rt-protocol"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode", "bincode",
@ -1047,7 +1047,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-user" name = "collab-user"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",
@ -1391,7 +1391,7 @@ checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
[[package]] [[package]]
name = "database-entity" name = "database-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -2844,7 +2844,7 @@ dependencies = [
[[package]] [[package]]
name = "gotrue" name = "gotrue"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"futures-util", "futures-util",
@ -2861,7 +2861,7 @@ dependencies = [
[[package]] [[package]]
name = "gotrue-entity" name = "gotrue-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -3298,7 +3298,7 @@ dependencies = [
[[package]] [[package]]
name = "infra" name = "infra"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"reqwest", "reqwest",
@ -5802,7 +5802,7 @@ dependencies = [
[[package]] [[package]]
name = "shared-entity" name = "shared-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",

View File

@ -86,7 +86,7 @@ yrs = { git = "https://github.com/appflowy/y-crdt", rev = "3f25bb510ca5274e7657d
# Run the script: # Run the script:
# scripts/tool/update_client_api_rev.sh new_rev_id # scripts/tool/update_client_api_rev.sh new_rev_id
# ⚠️⚠️⚠️️ # ⚠️⚠️⚠️️
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" } client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "54dfeb552752eb0e4d887af3a0affaa80863943d" }
# Please use the following script to update collab. # Please use the following script to update collab.
# Working directory: frontend # Working directory: frontend
# #
@ -96,10 +96,10 @@ client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "fda
# To switch to the local path, run: # To switch to the local path, run:
# scripts/tool/update_collab_source.sh # scripts/tool/update_collab_source.sh
# ⚠️⚠️⚠️️ # ⚠️⚠️⚠️️
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" } collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" } collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" } collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" } collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" } collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" } collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" } collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }

View File

@ -163,7 +163,7 @@ checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
[[package]] [[package]]
name = "app-error" name = "app-error"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode", "bincode",
@ -696,7 +696,7 @@ dependencies = [
[[package]] [[package]]
name = "client-api" name = "client-api"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"again", "again",
"anyhow", "anyhow",
@ -742,7 +742,7 @@ dependencies = [
[[package]] [[package]]
name = "client-websocket" name = "client-websocket"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"futures-channel", "futures-channel",
"futures-util", "futures-util",
@ -785,7 +785,7 @@ dependencies = [
[[package]] [[package]]
name = "collab" name = "collab"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -809,7 +809,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-database" name = "collab-database"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -839,7 +839,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-document" name = "collab-document"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",
@ -858,7 +858,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-entity" name = "collab-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
@ -873,7 +873,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-folder" name = "collab-folder"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
@ -911,7 +911,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-plugins" name = "collab-plugins"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-stream", "async-stream",
@ -950,7 +950,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-rt-entity" name = "collab-rt-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode", "bincode",
@ -975,7 +975,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-rt-protocol" name = "collab-rt-protocol"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode", "bincode",
@ -989,7 +989,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-user" name = "collab-user"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",
@ -1326,7 +1326,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
[[package]] [[package]]
name = "database-entity" name = "database-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -1610,6 +1610,7 @@ dependencies = [
"flowy-storage", "flowy-storage",
"flowy-user", "flowy-user",
"flowy-user-pub", "flowy-user-pub",
"futures",
"futures-util", "futures-util",
"lib-dispatch", "lib-dispatch",
"lib-infra", "lib-infra",
@ -2595,7 +2596,7 @@ dependencies = [
[[package]] [[package]]
name = "gotrue" name = "gotrue"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"futures-util", "futures-util",
@ -2612,7 +2613,7 @@ dependencies = [
[[package]] [[package]]
name = "gotrue-entity" name = "gotrue-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -2983,7 +2984,7 @@ dependencies = [
[[package]] [[package]]
name = "infra" name = "infra"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"reqwest", "reqwest",
@ -5109,7 +5110,7 @@ dependencies = [
[[package]] [[package]]
name = "shared-entity" name = "shared-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",

View File

@ -115,7 +115,7 @@ rocksdb = { git = "https://github.com/LucasXu0/rust-rocksdb", rev = "21cf4a23ec1
# Run the script.add_workspace_members: # Run the script.add_workspace_members:
# scripts/tool/update_client_api_rev.sh new_rev_id # scripts/tool/update_client_api_rev.sh new_rev_id
# ⚠️⚠️⚠️️ # ⚠️⚠️⚠️️
client-api = { git = " https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" } client-api = { git = " https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "54dfeb552752eb0e4d887af3a0affaa80863943d" }
# Please use the following script to update collab. # Please use the following script to update collab.
# Working directory: frontend # Working directory: frontend
# #
@ -125,10 +125,10 @@ client-api = { git = " https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "fd
# To switch to the local path, run: # To switch to the local path, run:
# scripts/tool/update_collab_source.sh # scripts/tool/update_collab_source.sh
# ⚠️⚠️⚠️️ # ⚠️⚠️⚠️️
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" } collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" } collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" } collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" } collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" } collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" } collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" } collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }

View File

@ -65,56 +65,32 @@ impl Display for CollabPluginProviderContext {
} }
} }
pub trait WorkspaceCollabIntegrate: Send + Sync {
fn workspace_id(&self) -> Result<String, Error>;
fn device_id(&self) -> Result<String, Error>;
}
pub struct AppFlowyCollabBuilder { pub struct AppFlowyCollabBuilder {
network_reachability: CollabConnectReachability, network_reachability: CollabConnectReachability,
workspace_id: RwLock<Option<String>>,
plugin_provider: RwLock<Arc<dyn CollabCloudPluginProvider>>, plugin_provider: RwLock<Arc<dyn CollabCloudPluginProvider>>,
snapshot_persistence: Mutex<Option<Arc<dyn SnapshotPersistence>>>, snapshot_persistence: Mutex<Option<Arc<dyn SnapshotPersistence>>>,
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
rocksdb_backup: Mutex<Option<Arc<dyn RocksdbBackup>>>, rocksdb_backup: Mutex<Option<Arc<dyn RocksdbBackup>>>,
device_id: String, workspace_integrate: Arc<dyn WorkspaceCollabIntegrate>,
}
pub struct CollabBuilderConfig {
pub sync_enable: bool,
/// If auto_initialize is false, the collab object will not be initialized automatically.
/// You need to call collab.initialize() manually.
///
/// Default is true.
pub auto_initialize: bool,
}
impl Default for CollabBuilderConfig {
fn default() -> Self {
Self {
sync_enable: true,
auto_initialize: true,
}
}
}
impl CollabBuilderConfig {
pub fn sync_enable(mut self, sync_enable: bool) -> Self {
self.sync_enable = sync_enable;
self
}
pub fn auto_initialize(mut self, auto_initialize: bool) -> Self {
self.auto_initialize = auto_initialize;
self
}
} }
impl AppFlowyCollabBuilder { impl AppFlowyCollabBuilder {
pub fn new<T: CollabCloudPluginProvider>(storage_provider: T, device_id: String) -> Self { pub fn new(
storage_provider: impl CollabCloudPluginProvider + 'static,
workspace_integrate: impl WorkspaceCollabIntegrate + 'static,
) -> Self {
Self { Self {
network_reachability: CollabConnectReachability::new(), network_reachability: CollabConnectReachability::new(),
workspace_id: Default::default(),
plugin_provider: RwLock::new(Arc::new(storage_provider)), plugin_provider: RwLock::new(Arc::new(storage_provider)),
snapshot_persistence: Default::default(), snapshot_persistence: Default::default(),
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
rocksdb_backup: Default::default(), rocksdb_backup: Default::default(),
device_id, workspace_integrate: Arc::new(workspace_integrate),
} }
} }
@ -127,10 +103,6 @@ impl AppFlowyCollabBuilder {
*self.rocksdb_backup.lock() = Some(rocksdb_backup); *self.rocksdb_backup.lock() = Some(rocksdb_backup);
} }
pub fn initialize(&self, workspace_id: String) {
*self.workspace_id.write() = Some(workspace_id);
}
pub fn update_network(&self, reachable: bool) { pub fn update_network(&self, reachable: bool) {
if reachable { if reachable {
self self
@ -149,15 +121,14 @@ impl AppFlowyCollabBuilder {
object_id: &str, object_id: &str,
collab_type: CollabType, collab_type: CollabType,
) -> Result<CollabObject, Error> { ) -> Result<CollabObject, Error> {
let workspace_id = self.workspace_id.read().clone().ok_or_else(|| { let device_id = self.workspace_integrate.device_id()?;
anyhow::anyhow!("When using supabase plugin, the workspace_id should not be empty") let workspace_id = self.workspace_integrate.workspace_id()?;
})?;
Ok(CollabObject::new( Ok(CollabObject::new(
uid, uid,
object_id.to_string(), object_id.to_string(),
collab_type, collab_type,
workspace_id, workspace_id,
self.device_id.clone(), device_id,
)) ))
} }
@ -175,8 +146,10 @@ impl AppFlowyCollabBuilder {
/// - `raw_data`: The raw data of the collaboration object, defined by the [CollabDocState] type. /// - `raw_data`: The raw data of the collaboration object, defined by the [CollabDocState] type.
/// - `collab_db`: A weak reference to the [CollabKVDB]. /// - `collab_db`: A weak reference to the [CollabKVDB].
/// ///
#[allow(clippy::too_many_arguments)]
pub async fn build( pub async fn build(
&self, &self,
workspace_id: &str,
uid: i64, uid: i64,
object_id: &str, object_id: &str,
object_type: CollabType, object_type: CollabType,
@ -184,14 +157,13 @@ impl AppFlowyCollabBuilder {
collab_db: Weak<CollabKVDB>, collab_db: Weak<CollabKVDB>,
build_config: CollabBuilderConfig, build_config: CollabBuilderConfig,
) -> Result<Arc<MutexCollab>, Error> { ) -> Result<Arc<MutexCollab>, Error> {
let persistence_config = CollabPersistenceConfig::default();
self.build_with_config( self.build_with_config(
workspace_id,
uid, uid,
object_id, object_id,
object_type, object_type,
collab_db, collab_db,
collab_doc_state, collab_doc_state,
persistence_config,
build_config, build_config,
) )
} }
@ -211,25 +183,34 @@ impl AppFlowyCollabBuilder {
/// - `collab_db`: A weak reference to the [CollabKVDB]. /// - `collab_db`: A weak reference to the [CollabKVDB].
/// ///
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
#[instrument( #[instrument(level = "trace", skip(self, collab_db, collab_doc_state, build_config))]
level = "trace",
skip(self, collab_db, collab_doc_state, persistence_config, build_config)
)]
pub fn build_with_config( pub fn build_with_config(
&self, &self,
workspace_id: &str,
uid: i64, uid: i64,
object_id: &str, object_id: &str,
object_type: CollabType, object_type: CollabType,
collab_db: Weak<CollabKVDB>, collab_db: Weak<CollabKVDB>,
collab_doc_state: DataSource, collab_doc_state: DataSource,
#[allow(unused_variables)] persistence_config: CollabPersistenceConfig,
build_config: CollabBuilderConfig, build_config: CollabBuilderConfig,
) -> Result<Arc<MutexCollab>, Error> { ) -> Result<Arc<MutexCollab>, Error> {
let collab = CollabBuilder::new(uid, object_id) let collab = CollabBuilder::new(uid, object_id)
.with_doc_state(collab_doc_state) .with_doc_state(collab_doc_state)
.with_device_id(self.device_id.clone()) .with_device_id(self.workspace_integrate.device_id()?)
.build()?; .build()?;
// Compare the workspace_id with the currently opened workspace_id. Return an error if they do not match.
// This check is crucial in asynchronous code contexts where the workspace_id might change during operation.
let actual_workspace_id = self.workspace_integrate.workspace_id()?;
if workspace_id != actual_workspace_id {
return Err(anyhow::anyhow!(
"workspace_id not match when build collab. expect workspace_id: {}, actual workspace_id: {}",
workspace_id,
actual_workspace_id
));
}
let persistence_config = CollabPersistenceConfig::default();
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
{ {
collab.lock().add_plugin(Box::new(IndexeddbDiskPlugin::new( collab.lock().add_plugin(Box::new(IndexeddbDiskPlugin::new(
@ -317,3 +298,33 @@ impl AppFlowyCollabBuilder {
Ok(arc_collab) Ok(arc_collab)
} }
} }
pub struct CollabBuilderConfig {
pub sync_enable: bool,
/// If auto_initialize is false, the collab object will not be initialized automatically.
/// You need to call collab.initialize() manually.
///
/// Default is true.
pub auto_initialize: bool,
}
impl Default for CollabBuilderConfig {
fn default() -> Self {
Self {
sync_enable: true,
auto_initialize: true,
}
}
}
impl CollabBuilderConfig {
pub fn sync_enable(mut self, sync_enable: bool) -> Self {
self.sync_enable = sync_enable;
self
}
pub fn auto_initialize(mut self, auto_initialize: bool) -> Self {
self.auto_initialize = auto_initialize;
self
}
}

View File

@ -1,8 +1,8 @@
use crate::collab_builder::{CollabPluginProviderContext, CollabPluginProviderType}; use crate::collab_builder::{CollabPluginProviderContext, CollabPluginProviderType};
use collab::preclude::CollabPlugin; use collab::preclude::CollabPlugin;
use std::sync::Arc;
pub trait CollabCloudPluginProvider: Send + Sync + 'static { #[cfg(target_arch = "wasm32")]
pub trait CollabCloudPluginProvider: 'static {
fn provider_type(&self) -> CollabPluginProviderType; fn provider_type(&self) -> CollabPluginProviderType;
fn get_plugins(&self, context: CollabPluginProviderContext) -> Vec<Box<dyn CollabPlugin>>; fn get_plugins(&self, context: CollabPluginProviderContext) -> Vec<Box<dyn CollabPlugin>>;
@ -10,7 +10,35 @@ pub trait CollabCloudPluginProvider: Send + Sync + 'static {
fn is_sync_enabled(&self) -> bool; fn is_sync_enabled(&self) -> bool;
} }
impl<T> CollabCloudPluginProvider for Arc<T> #[cfg(target_arch = "wasm32")]
impl<T> CollabCloudPluginProvider for std::rc::Rc<T>
where
T: CollabCloudPluginProvider,
{
fn provider_type(&self) -> CollabPluginProviderType {
(**self).provider_type()
}
fn get_plugins(&self, context: CollabPluginProviderContext) -> Vec<Box<dyn CollabPlugin>> {
(**self).get_plugins(context)
}
fn is_sync_enabled(&self) -> bool {
(**self).is_sync_enabled()
}
}
#[cfg(not(target_arch = "wasm32"))]
pub trait CollabCloudPluginProvider: Send + Sync + 'static {
fn provider_type(&self) -> CollabPluginProviderType;
fn get_plugins(&self, context: CollabPluginProviderContext) -> Vec<Box<dyn CollabPlugin>>;
fn is_sync_enabled(&self) -> bool;
}
#[cfg(not(target_arch = "wasm32"))]
impl<T> CollabCloudPluginProvider for std::sync::Arc<T>
where where
T: CollabCloudPluginProvider, T: CollabCloudPluginProvider,
{ {

View File

@ -28,7 +28,7 @@ flowy-search = { workspace = true }
serde.workspace = true serde.workspace = true
serde_json.workspace = true serde_json.workspace = true
protobuf.workspace = true protobuf.workspace = true
tokio = { workspace = true, features = ["full"]} tokio = { workspace = true, features = ["full"] }
futures-util = "0.3.26" futures-util = "0.3.26"
thread-id = "3.3.0" thread-id = "3.3.0"
bytes.workspace = true bytes.workspace = true
@ -53,6 +53,7 @@ tokio-postgres = { version = "0.7.8" }
chrono = "0.4.31" chrono = "0.4.31"
zip = "0.6.6" zip = "0.6.6"
walkdir = "2.5.0" walkdir = "2.5.0"
futures = "0.3.30"
[features] [features]
default = ["supabase_cloud_test"] default = ["supabase_cloud_test"]

View File

@ -143,7 +143,8 @@ impl EventIntegrationTest {
let mutex_folder = self.appflowy_core.folder_manager.get_mutex_folder().clone(); let mutex_folder = self.appflowy_core.folder_manager.get_mutex_folder().clone();
let folder_lock_guard = mutex_folder.read(); let folder_lock_guard = mutex_folder.read();
let folder = folder_lock_guard.as_ref().unwrap(); let folder = folder_lock_guard.as_ref().unwrap();
folder.get_folder_data().clone().unwrap() let workspace_id = self.appflowy_core.user_manager.workspace_id().unwrap();
folder.get_folder_data(&workspace_id).clone().unwrap()
} }
pub async fn get_all_workspace_views(&self) -> Vec<ViewPB> { pub async fn get_all_workspace_views(&self) -> Vec<ViewPB> {

View File

@ -20,7 +20,7 @@ pub async fn get_synced_workspaces(
&sub_id, &sub_id,
UserNotification::DidUpdateUserWorkspaces as i32, UserNotification::DidUpdateUserWorkspaces as i32,
); );
receive_with_timeout(rx, Duration::from_secs(30)) receive_with_timeout(rx, Duration::from_secs(60))
.await .await
.unwrap() .unwrap()
.items .items

View File

@ -1,5 +1,11 @@
use collab::core::collab::DataSource::DocStateV1;
use collab::core::origin::CollabOrigin;
use collab_entity::CollabType;
use collab_folder::Folder;
use event_integration::user_event::user_localhost_af_cloud; use event_integration::user_event::user_localhost_af_cloud;
use event_integration::EventIntegrationTest; use event_integration::EventIntegrationTest;
use std::time::Duration;
use tokio::time::sleep;
use crate::user::af_cloud_test::util::get_synced_workspaces; use crate::user::af_cloud_test::util::get_synced_workspaces;
@ -94,16 +100,143 @@ async fn af_cloud_open_workspace_test() {
user_localhost_af_cloud().await; user_localhost_af_cloud().await;
let test = EventIntegrationTest::new().await; let test = EventIntegrationTest::new().await;
let _ = test.af_cloud_sign_up().await; let _ = test.af_cloud_sign_up().await;
let default_document_name = "Getting started";
let workspace = test.create_workspace("my second workspace").await; test.create_document("A").await;
test.open_workspace(&workspace.workspace_id).await; test.create_document("B").await;
let first_workspace = test.get_current_workspace().await;
let views = test.get_all_workspace_views().await;
assert_eq!(views.len(), 3);
assert_eq!(views[0].name, default_document_name);
assert_eq!(views[1].name, "A");
assert_eq!(views[2].name, "B");
test.create_document("my first document").await; let user_workspace = test.create_workspace("second workspace").await;
test.create_document("my second document").await; test.open_workspace(&user_workspace.workspace_id).await;
let second_workspace = test.get_current_workspace().await;
test.create_document("C").await;
test.create_document("D").await;
let views = test.get_all_workspace_views().await; let views = test.get_all_workspace_views().await;
assert_eq!(views.len(), 3); assert_eq!(views.len(), 3);
// the first view is the default get started view assert_eq!(views[0].name, default_document_name);
assert_eq!(views[1].name, "my first document".to_string()); assert_eq!(views[1].name, "C");
assert_eq!(views[2].name, "my second document".to_string()); assert_eq!(views[2].name, "D");
// simulate open workspace and check if the views are correct
for i in 0..30 {
if i % 2 == 0 {
test.open_workspace(&first_workspace.id).await;
sleep(Duration::from_millis(300)).await;
test
.create_document(&uuid::Uuid::new_v4().to_string())
.await;
} else {
test.open_workspace(&second_workspace.id).await;
sleep(Duration::from_millis(200)).await;
test
.create_document(&uuid::Uuid::new_v4().to_string())
.await;
}
}
test.open_workspace(&first_workspace.id).await;
let views = test.get_all_workspace_views().await;
assert_eq!(views[0].name, default_document_name);
assert_eq!(views[1].name, "A");
assert_eq!(views[2].name, "B");
test.open_workspace(&second_workspace.id).await;
let views = test.get_all_workspace_views().await;
assert_eq!(views[0].name, default_document_name);
assert_eq!(views[1].name, "C");
assert_eq!(views[2].name, "D");
}
#[tokio::test]
async fn af_cloud_different_open_same_workspace_test() {
user_localhost_af_cloud().await;
// Set up the primary client and sign them up to the cloud.
let client_1 = EventIntegrationTest::new().await;
let owner_profile = client_1.af_cloud_sign_up().await;
let shared_workspace_id = client_1.get_current_workspace().await.id.clone();
// Verify that the workspace ID from the profile matches the current session's workspace ID.
assert_eq!(shared_workspace_id, owner_profile.workspace_id);
// Define the number of additional clients
let num_clients = 5;
let mut clients = Vec::new();
// Initialize and sign up additional clients
for _ in 0..num_clients {
let client = EventIntegrationTest::new().await;
let client_profile = client.af_cloud_sign_up().await;
let views = client.get_all_workspace_views().await;
// only the getting started view should be present
assert_eq!(views.len(), 1);
for view in views {
client.delete_view(&view.id).await;
}
client_1
.add_workspace_member(&owner_profile.workspace_id, &client_profile.email)
.await;
clients.push((client, client_profile));
}
// Verify that each client has exactly two workspaces: one from sign-up and another from invitation
for (client, profile) in &clients {
let all_workspaces = get_synced_workspaces(client, profile.id).await;
assert_eq!(all_workspaces.len(), 2);
}
// Simulate each client open different workspace 30 times
let mut handles = vec![];
for client in clients.clone() {
let cloned_shared_workspace_id = shared_workspace_id.clone();
let handle = tokio::spawn(async move {
let (client, profile) = client;
let all_workspaces = get_synced_workspaces(&client, profile.id).await;
for i in 0..30 {
let index = i % 2;
let iter_workspace_id = &all_workspaces[index].workspace_id;
client.open_workspace(iter_workspace_id).await;
if iter_workspace_id == &cloned_shared_workspace_id {
let views = client.get_all_workspace_views().await;
assert_eq!(views.len(), 1);
sleep(Duration::from_millis(300)).await;
} else {
let views = client.get_all_workspace_views().await;
assert!(views.is_empty());
}
}
});
handles.push(handle);
}
futures::future::join_all(handles).await;
// Retrieve and verify the collaborative document state for Client 1's workspace.
let doc_state = client_1
.get_collab_doc_state(&shared_workspace_id, CollabType::Folder)
.await
.unwrap();
let folder = Folder::from_collab_doc_state(
owner_profile.id,
CollabOrigin::Empty,
DocStateV1(doc_state),
&shared_workspace_id,
vec![],
)
.unwrap();
// Retrieve and verify the views associated with the workspace.
let views = folder.get_views_belong_to(&shared_workspace_id);
let folder_workspace_id = folder.get_workspace_id();
assert_eq!(folder_workspace_id, shared_workspace_id);
assert_eq!(views.len(), 1, "only get: {:?}", views); // Expecting two views.
assert_eq!(views[0].name, "Getting started");
} }

View File

@ -9,8 +9,9 @@ use flowy_sqlite::{
}; };
use flowy_user::services::authenticate_user::AuthenticateUser; use flowy_user::services::authenticate_user::AuthenticateUser;
use collab_integrate::collab_builder::WorkspaceCollabIntegrate;
use lib_infra::util::timestamp; use lib_infra::util::timestamp;
use std::sync::Weak; use std::sync::{Arc, Weak};
use tracing::debug; use tracing::debug;
pub struct SnapshotDBImpl(pub Weak<AuthenticateUser>); pub struct SnapshotDBImpl(pub Weak<AuthenticateUser>);
@ -207,3 +208,26 @@ impl CollabSnapshotSql {
Ok(()) Ok(())
} }
} }
pub(crate) struct WorkspaceCollabIntegrateImpl(pub Weak<AuthenticateUser>);
impl WorkspaceCollabIntegrateImpl {
fn upgrade_user(&self) -> Result<Arc<AuthenticateUser>, FlowyError> {
let user = self
.0
.upgrade()
.ok_or(FlowyError::internal().with_context("Unexpected error: UserSession is None"))?;
Ok(user)
}
}
impl WorkspaceCollabIntegrate for WorkspaceCollabIntegrateImpl {
fn workspace_id(&self) -> Result<String, anyhow::Error> {
let workspace_id = self.upgrade_user()?.workspace_id()?;
Ok(workspace_id)
}
fn device_id(&self) -> Result<String, anyhow::Error> {
Ok(self.upgrade_user()?.user_config.device_id.clone())
}
}

View File

@ -27,20 +27,30 @@ impl DatabaseDepsResolver {
} }
struct DatabaseUserImpl(Weak<AuthenticateUser>); struct DatabaseUserImpl(Weak<AuthenticateUser>);
impl DatabaseUser for DatabaseUserImpl { impl DatabaseUserImpl {
fn user_id(&self) -> Result<i64, FlowyError> { fn upgrade_user(&self) -> Result<Arc<AuthenticateUser>, FlowyError> {
self let user = self
.0 .0
.upgrade() .upgrade()
.ok_or(FlowyError::internal().with_context("Unexpected error: UserSession is None"))? .ok_or(FlowyError::internal().with_context("Unexpected error: UserSession is None"))?;
.user_id() Ok(user)
}
}
impl DatabaseUser for DatabaseUserImpl {
fn user_id(&self) -> Result<i64, FlowyError> {
self.upgrade_user()?.user_id()
} }
fn collab_db(&self, uid: i64) -> Result<Weak<CollabKVDB>, FlowyError> { fn collab_db(&self, uid: i64) -> Result<Weak<CollabKVDB>, FlowyError> {
self self.upgrade_user()?.get_collab_db(uid)
.0 }
.upgrade()
.ok_or(FlowyError::internal().with_context("Unexpected error: UserSession is None"))? fn workspace_id(&self) -> Result<String, FlowyError> {
.get_collab_db(uid) self.upgrade_user()?.workspace_id()
}
fn workspace_database_object_id(&self) -> Result<String, FlowyError> {
self.upgrade_user()?.workspace_database_object_id()
} }
} }

View File

@ -18,7 +18,6 @@ use flowy_folder_pub::folder_builder::NestedViewBuilder;
use flowy_search::folder::indexer::FolderIndexManagerImpl; use flowy_search::folder::indexer::FolderIndexManagerImpl;
use flowy_user::services::authenticate_user::AuthenticateUser; use flowy_user::services::authenticate_user::AuthenticateUser;
use lib_dispatch::prelude::ToBytes; use lib_dispatch::prelude::ToBytes;
use lib_infra::async_trait::async_trait;
use lib_infra::future::FutureResult; use lib_infra::future::FutureResult;
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::TryFrom; use std::convert::TryFrom;
@ -76,22 +75,27 @@ struct FolderUserImpl {
authenticate_user: Weak<AuthenticateUser>, authenticate_user: Weak<AuthenticateUser>,
} }
#[async_trait] impl FolderUserImpl {
impl FolderUser for FolderUserImpl { fn upgrade_user(&self) -> Result<Arc<AuthenticateUser>, FlowyError> {
fn user_id(&self) -> Result<i64, FlowyError> { let user = self
self
.authenticate_user .authenticate_user
.upgrade() .upgrade()
.ok_or(FlowyError::internal().with_context("Unexpected error: UserSession is None"))? .ok_or(FlowyError::internal().with_context("Unexpected error: UserSession is None"))?;
.user_id() Ok(user)
}
}
impl FolderUser for FolderUserImpl {
fn user_id(&self) -> Result<i64, FlowyError> {
self.upgrade_user()?.user_id()
}
fn workspace_id(&self) -> Result<String, FlowyError> {
self.upgrade_user()?.workspace_id()
} }
fn collab_db(&self, uid: i64) -> Result<Weak<CollabKVDB>, FlowyError> { fn collab_db(&self, uid: i64) -> Result<Weak<CollabKVDB>, FlowyError> {
self self.upgrade_user()?.get_collab_db(uid)
.authenticate_user
.upgrade()
.ok_or(FlowyError::internal().with_context("Unexpected error: UserSession is None"))?
.get_collab_db(uid)
} }
} }

View File

@ -6,6 +6,7 @@ use parking_lot::RwLock;
use serde_repr::*; use serde_repr::*;
use flowy_error::{FlowyError, FlowyResult}; use flowy_error::{FlowyError, FlowyResult};
use flowy_server::af_cloud::define::ServerUser;
use flowy_server::af_cloud::AppFlowyCloudServer; use flowy_server::af_cloud::AppFlowyCloudServer;
use flowy_server::local_server::{LocalServer, LocalServerDB}; use flowy_server::local_server::{LocalServer, LocalServerDB};
use flowy_server::supabase::SupabaseServer; use flowy_server::supabase::SupabaseServer;
@ -63,6 +64,7 @@ pub struct ServerProvider {
/// The authenticator type of the user. /// The authenticator type of the user.
authenticator: RwLock<Authenticator>, authenticator: RwLock<Authenticator>,
user: Arc<dyn ServerUser>,
pub(crate) uid: Arc<RwLock<Option<i64>>>, pub(crate) uid: Arc<RwLock<Option<i64>>>,
} }
@ -71,7 +73,9 @@ impl ServerProvider {
config: AppFlowyCoreConfig, config: AppFlowyCoreConfig,
server: Server, server: Server,
store_preferences: Weak<StorePreferences>, store_preferences: Weak<StorePreferences>,
server_user: impl ServerUser + 'static,
) -> Self { ) -> Self {
let user = Arc::new(server_user);
let encryption = EncryptionImpl::new(None); let encryption = EncryptionImpl::new(None);
Self { Self {
config, config,
@ -81,6 +85,7 @@ impl ServerProvider {
encryption: RwLock::new(Arc::new(encryption)), encryption: RwLock::new(Arc::new(encryption)),
store_preferences, store_preferences,
uid: Default::default(), uid: Default::default(),
user,
} }
} }
@ -129,6 +134,7 @@ impl ServerProvider {
*self.user_enable_sync.read(), *self.user_enable_sync.read(),
self.config.device_id.clone(), self.config.device_id.clone(),
&self.config.app_version, &self.config.app_version,
self.user.clone(),
)); ));
Ok::<Arc<dyn AppFlowyServer>, FlowyError>(server) Ok::<Arc<dyn AppFlowyServer>, FlowyError>(server)

View File

@ -38,7 +38,6 @@ impl UserStatusCallback for UserStatusCallbackImpl {
) -> Fut<FlowyResult<()>> { ) -> Fut<FlowyResult<()>> {
let user_id = user_id.to_owned(); let user_id = user_id.to_owned();
let user_workspace = user_workspace.clone(); let user_workspace = user_workspace.clone();
let collab_builder = self.collab_builder.clone();
let folder_manager = self.folder_manager.clone(); let folder_manager = self.folder_manager.clone();
let database_manager = self.database_manager.clone(); let database_manager = self.database_manager.clone();
let document_manager = self.document_manager.clone(); let document_manager = self.document_manager.clone();
@ -59,7 +58,6 @@ impl UserStatusCallback for UserStatusCallbackImpl {
} }
to_fut(async move { to_fut(async move {
collab_builder.initialize(user_workspace.id.clone());
folder_manager folder_manager
.initialize( .initialize(
user_id, user_id,
@ -69,16 +67,8 @@ impl UserStatusCallback for UserStatusCallbackImpl {
}, },
) )
.await?; .await?;
database_manager database_manager.initialize(user_id).await?;
.initialize( document_manager.initialize(user_id).await?;
user_id,
user_workspace.id.clone(),
user_workspace.workspace_database_object_id,
)
.await?;
document_manager
.initialize(user_id, user_workspace.id)
.await?;
Ok(()) Ok(())
}) })
} }
@ -104,19 +94,9 @@ impl UserStatusCallback for UserStatusCallbackImpl {
device_id device_id
); );
folder_manager folder_manager.initialize_with_workspace_id(user_id).await?;
.initialize_with_workspace_id(user_id, &user_workspace.id) database_manager.initialize(user_id).await?;
.await?; document_manager.initialize(user_id).await?;
database_manager
.initialize(
user_id,
user_workspace.id.clone(),
user_workspace.workspace_database_object_id,
)
.await?;
document_manager
.initialize(user_id, user_workspace.id)
.await?;
Ok(()) Ok(())
}) })
} }
@ -199,16 +179,12 @@ impl UserStatusCallback for UserStatusCallbackImpl {
.context("FolderManager error")?; .context("FolderManager error")?;
database_manager database_manager
.initialize_with_new_user( .initialize_with_new_user(user_profile.uid)
user_profile.uid,
user_workspace.id.clone(),
user_workspace.workspace_database_object_id,
)
.await .await
.context("DatabaseManager error")?; .context("DatabaseManager error")?;
document_manager document_manager
.initialize_with_new_user(user_profile.uid, user_workspace.id) .initialize_with_new_user(user_profile.uid)
.await .await
.context("DocumentManager error")?; .context("DocumentManager error")?;
Ok(()) Ok(())
@ -223,29 +199,15 @@ impl UserStatusCallback for UserStatusCallbackImpl {
}) })
} }
fn open_workspace(&self, user_id: i64, user_workspace: &UserWorkspace) -> Fut<FlowyResult<()>> { fn open_workspace(&self, user_id: i64, _user_workspace: &UserWorkspace) -> Fut<FlowyResult<()>> {
let user_workspace = user_workspace.clone();
let collab_builder = self.collab_builder.clone();
let folder_manager = self.folder_manager.clone(); let folder_manager = self.folder_manager.clone();
let database_manager = self.database_manager.clone(); let database_manager = self.database_manager.clone();
let document_manager = self.document_manager.clone(); let document_manager = self.document_manager.clone();
to_fut(async move { to_fut(async move {
collab_builder.initialize(user_workspace.id.clone()); folder_manager.initialize_with_workspace_id(user_id).await?;
folder_manager database_manager.initialize(user_id).await?;
.initialize_with_workspace_id(user_id, &user_workspace.id) document_manager.initialize(user_id).await?;
.await?;
database_manager
.initialize(
user_id,
user_workspace.id.clone(),
user_workspace.workspace_database_object_id,
)
.await?;
document_manager
.initialize(user_id, user_workspace.id)
.await?;
Ok(()) Ok(())
}) })
} }

View File

@ -4,7 +4,7 @@ use flowy_search::folder::indexer::FolderIndexManagerImpl;
use flowy_search::services::manager::SearchManager; use flowy_search::services::manager::SearchManager;
use flowy_storage::ObjectStorageService; use flowy_storage::ObjectStorageService;
use semver::Version; use semver::Version;
use std::sync::Arc; use std::sync::{Arc, Weak};
use std::time::Duration; use std::time::Duration;
use sysinfo::System; use sysinfo::System;
use tokio::sync::RwLock; use tokio::sync::RwLock;
@ -13,7 +13,9 @@ use tracing::{debug, error, event, info, instrument};
use collab_integrate::collab_builder::{AppFlowyCollabBuilder, CollabPluginProviderType}; use collab_integrate::collab_builder::{AppFlowyCollabBuilder, CollabPluginProviderType};
use flowy_database2::DatabaseManager; use flowy_database2::DatabaseManager;
use flowy_document::manager::DocumentManager; use flowy_document::manager::DocumentManager;
use flowy_error::{FlowyError, FlowyResult};
use flowy_folder::manager::FolderManager; use flowy_folder::manager::FolderManager;
use flowy_server::af_cloud::define::ServerUser;
use flowy_sqlite::kv::StorePreferences; use flowy_sqlite::kv::StorePreferences;
use flowy_user::services::authenticate_user::AuthenticateUser; use flowy_user::services::authenticate_user::AuthenticateUser;
@ -104,14 +106,29 @@ impl AppFlowyCore {
let task_dispatcher = Arc::new(RwLock::new(task_scheduler)); let task_dispatcher = Arc::new(RwLock::new(task_scheduler));
runtime.spawn(TaskRunner::run(task_dispatcher.clone())); runtime.spawn(TaskRunner::run(task_dispatcher.clone()));
let app_version = Version::parse(&config.app_version).unwrap_or_else(|_| Version::new(0, 5, 4));
let user_config = UserConfig::new(
&config.name,
&config.storage_path,
&config.application_path,
&config.device_id,
app_version,
);
let authenticate_user = Arc::new(AuthenticateUser::new(
user_config.clone(),
store_preference.clone(),
));
let server_type = current_server_type(); let server_type = current_server_type();
debug!("🔥runtime:{}, server:{}", runtime, server_type); debug!("🔥runtime:{}, server:{}", runtime, server_type);
let server_provider = Arc::new(ServerProvider::new( let server_provider = Arc::new(ServerProvider::new(
config.clone(), config.clone(),
server_type, server_type,
Arc::downgrade(&store_preference), Arc::downgrade(&store_preference),
ServerUserImpl(Arc::downgrade(&authenticate_user)),
)); ));
let app_version = Version::parse(&config.app_version).unwrap_or_else(|_| Version::new(0, 5, 4));
event!(tracing::Level::DEBUG, "Init managers",); event!(tracing::Level::DEBUG, "Init managers",);
let ( let (
@ -127,20 +144,7 @@ impl AppFlowyCore {
/// on demand based on the [CollabPluginConfig]. /// on demand based on the [CollabPluginConfig].
let collab_builder = Arc::new(AppFlowyCollabBuilder::new( let collab_builder = Arc::new(AppFlowyCollabBuilder::new(
server_provider.clone(), server_provider.clone(),
config.device_id.clone(), WorkspaceCollabIntegrateImpl(Arc::downgrade(&authenticate_user)),
));
let user_config = UserConfig::new(
&config.name,
&config.storage_path,
&config.application_path,
&config.device_id,
app_version,
);
let authenticate_user = Arc::new(AuthenticateUser::new(
user_config.clone(),
store_preference.clone(),
)); ));
collab_builder collab_builder
@ -260,3 +264,20 @@ impl From<Server> for CollabPluginProviderType {
} }
} }
} }
struct ServerUserImpl(Weak<AuthenticateUser>);
impl ServerUserImpl {
fn upgrade_user(&self) -> Result<Arc<AuthenticateUser>, FlowyError> {
let user = self
.0
.upgrade()
.ok_or(FlowyError::internal().with_context("Unexpected error: UserSession is None"))?;
Ok(user)
}
}
impl ServerUser for ServerUserImpl {
fn workspace_id(&self) -> FlowyResult<String> {
self.upgrade_user()?.workspace_id()
}
}

View File

@ -32,6 +32,8 @@ use crate::services::share::csv::{CSVFormat, CSVImporter, ImportResult};
pub trait DatabaseUser: Send + Sync { pub trait DatabaseUser: Send + Sync {
fn user_id(&self) -> Result<i64, FlowyError>; fn user_id(&self) -> Result<i64, FlowyError>;
fn collab_db(&self, uid: i64) -> Result<Weak<CollabKVDB>, FlowyError>; fn collab_db(&self, uid: i64) -> Result<Weak<CollabKVDB>, FlowyError>;
fn workspace_id(&self) -> Result<String, FlowyError>;
fn workspace_database_object_id(&self) -> Result<String, FlowyError>;
} }
pub struct DatabaseManager { pub struct DatabaseManager {
@ -71,12 +73,7 @@ impl DatabaseManager {
} }
/// When initialize with new workspace, all the resources will be cleared. /// When initialize with new workspace, all the resources will be cleared.
pub async fn initialize( pub async fn initialize(&self, uid: i64) -> FlowyResult<()> {
&self,
uid: i64,
workspace_id: String,
workspace_database_object_id: String,
) -> FlowyResult<()> {
// 1. Clear all existing tasks // 1. Clear all existing tasks
self.task_scheduler.write().await.clear_task(); self.task_scheduler.write().await.clear_task();
// 2. Release all existing editors // 2. Release all existing editors
@ -85,16 +82,21 @@ impl DatabaseManager {
} }
self.editors.lock().await.clear(); self.editors.lock().await.clear();
// 3. Clear the workspace database // 3. Clear the workspace database
if let Some(old_workspace_database) = self.workspace_database.write().await.take() {
old_workspace_database.close();
}
*self.workspace_database.write().await = None; *self.workspace_database.write().await = None;
let collab_db = self.user.collab_db(uid)?; let collab_db = self.user.collab_db(uid)?;
let collab_builder = UserDatabaseCollabServiceImpl { let collab_builder = UserDatabaseCollabServiceImpl {
workspace_id: workspace_id.clone(), user: self.user.clone(),
collab_builder: self.collab_builder.clone(), collab_builder: self.collab_builder.clone(),
cloud_service: self.cloud_service.clone(), cloud_service: self.cloud_service.clone(),
}; };
let config = CollabPersistenceConfig::new().snapshot_per_update(100); let config = CollabPersistenceConfig::new().snapshot_per_update(100);
let workspace_id = self.user.workspace_id()?;
let workspace_database_object_id = self.user.workspace_database_object_id()?;
let mut workspace_database_doc_state = DataSource::Disk; let mut workspace_database_doc_state = DataSource::Disk;
// If the workspace database not exist in disk, try to fetch from remote. // If the workspace database not exist in disk, try to fetch from remote.
if !self.is_collab_exist(uid, &collab_db, &workspace_database_object_id) { if !self.is_collab_exist(uid, &collab_db, &workspace_database_object_id) {
@ -151,15 +153,8 @@ impl DatabaseManager {
skip_all, skip_all,
err err
)] )]
pub async fn initialize_with_new_user( pub async fn initialize_with_new_user(&self, user_id: i64) -> FlowyResult<()> {
&self, self.initialize(user_id).await?;
user_id: i64,
workspace_id: String,
workspace_database_object_id: String,
) -> FlowyResult<()> {
self
.initialize(user_id, workspace_id, workspace_database_object_id)
.await?;
Ok(()) Ok(())
} }
@ -222,7 +217,7 @@ impl DatabaseManager {
.await? .await?
.get_database(database_id) .get_database(database_id)
.await .await
.ok_or_else(FlowyError::collab_not_sync)?; .ok_or_else(|| FlowyError::collab_not_sync().with_context("open database error"))?;
// Subscribe the [BlockEvent] // Subscribe the [BlockEvent]
subscribe_block_event(&database); subscribe_block_event(&database);
@ -445,7 +440,7 @@ fn subscribe_block_event(database: &Arc<MutexDatabase>) {
} }
struct UserDatabaseCollabServiceImpl { struct UserDatabaseCollabServiceImpl {
workspace_id: String, user: Arc<dyn DatabaseUser>,
collab_builder: Arc<AppFlowyCollabBuilder>, collab_builder: Arc<AppFlowyCollabBuilder>,
cloud_service: Arc<dyn DatabaseCloudService>, cloud_service: Arc<dyn DatabaseCloudService>,
} }
@ -456,7 +451,7 @@ impl DatabaseCollabService for UserDatabaseCollabServiceImpl {
object_id: &str, object_id: &str,
object_ty: CollabType, object_ty: CollabType,
) -> CollabFuture<Result<DataSource, DatabaseError>> { ) -> CollabFuture<Result<DataSource, DatabaseError>> {
let workspace_id = self.workspace_id.clone(); let workspace_id = self.user.workspace_id().unwrap();
let object_id = object_id.to_string(); let object_id = object_id.to_string();
let weak_cloud_service = Arc::downgrade(&self.cloud_service); let weak_cloud_service = Arc::downgrade(&self.cloud_service);
Box::pin(async move { Box::pin(async move {
@ -480,9 +475,12 @@ impl DatabaseCollabService for UserDatabaseCollabServiceImpl {
object_ids: Vec<String>, object_ids: Vec<String>,
object_ty: CollabType, object_ty: CollabType,
) -> CollabFuture<Result<CollabDocStateByOid, DatabaseError>> { ) -> CollabFuture<Result<CollabDocStateByOid, DatabaseError>> {
let workspace_id = self.workspace_id.clone(); let cloned_user = self.user.clone();
let weak_cloud_service = Arc::downgrade(&self.cloud_service); let weak_cloud_service = Arc::downgrade(&self.cloud_service);
Box::pin(async move { Box::pin(async move {
let workspace_id = cloned_user
.workspace_id()
.map_err(|err| DatabaseError::Internal(err.into()))?;
match weak_cloud_service.upgrade() { match weak_cloud_service.upgrade() {
None => { None => {
tracing::warn!("Cloud service is dropped"); tracing::warn!("Cloud service is dropped");
@ -505,15 +503,19 @@ impl DatabaseCollabService for UserDatabaseCollabServiceImpl {
object_type: CollabType, object_type: CollabType,
collab_db: Weak<CollabKVDB>, collab_db: Weak<CollabKVDB>,
collab_raw_data: DataSource, collab_raw_data: DataSource,
persistence_config: CollabPersistenceConfig, _persistence_config: CollabPersistenceConfig,
) -> Result<Arc<MutexCollab>, DatabaseError> { ) -> Result<Arc<MutexCollab>, DatabaseError> {
let workspace_id = self
.user
.workspace_id()
.map_err(|err| DatabaseError::Internal(err.into()))?;
let collab = self.collab_builder.build_with_config( let collab = self.collab_builder.build_with_config(
&workspace_id,
uid, uid,
object_id, object_id,
object_type.clone(), object_type.clone(),
collab_db.clone(), collab_db.clone(),
collab_raw_data, collab_raw_data,
persistence_config,
CollabBuilderConfig::default().sync_enable(true), CollabBuilderConfig::default().sync_enable(true),
)?; )?;
Ok(collab) Ok(collab)

View File

@ -20,7 +20,6 @@ use tracing::{error, trace};
use tracing::{event, instrument}; use tracing::{event, instrument};
use collab_integrate::collab_builder::{AppFlowyCollabBuilder, CollabBuilderConfig}; use collab_integrate::collab_builder::{AppFlowyCollabBuilder, CollabBuilderConfig};
use collab_integrate::CollabPersistenceConfig;
use flowy_document_pub::cloud::DocumentCloudService; use flowy_document_pub::cloud::DocumentCloudService;
use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult}; use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult};
use flowy_storage::ObjectStorageService; use flowy_storage::ObjectStorageService;
@ -77,7 +76,7 @@ impl DocumentManager {
} }
} }
pub async fn initialize(&self, _uid: i64, _workspace_id: String) -> FlowyResult<()> { pub async fn initialize(&self, _uid: i64) -> FlowyResult<()> {
self.documents.clear(); self.documents.clear();
Ok(()) Ok(())
} }
@ -88,8 +87,8 @@ impl DocumentManager {
skip_all, skip_all,
err err
)] )]
pub async fn initialize_with_new_user(&self, uid: i64, workspace_id: String) -> FlowyResult<()> { pub async fn initialize_with_new_user(&self, uid: i64) -> FlowyResult<()> {
self.initialize(uid, workspace_id).await?; self.initialize(uid).await?;
Ok(()) Ok(())
} }
@ -382,13 +381,14 @@ impl DocumentManager {
sync_enable: bool, sync_enable: bool,
) -> FlowyResult<Arc<MutexCollab>> { ) -> FlowyResult<Arc<MutexCollab>> {
let db = self.user_service.collab_db(uid)?; let db = self.user_service.collab_db(uid)?;
let workspace_id = self.user_service.workspace_id()?;
let collab = self.collab_builder.build_with_config( let collab = self.collab_builder.build_with_config(
&workspace_id,
uid, uid,
doc_id, doc_id,
CollabType::Document, CollabType::Document,
db, db,
doc_state, doc_state,
CollabPersistenceConfig::default().snapshot_per_update(1000),
CollabBuilderConfig::default().sync_enable(sync_enable), CollabBuilderConfig::default().sync_enable(sync_enable),
)?; )?;
Ok(collab) Ok(collab)

View File

@ -9,11 +9,10 @@ use nanoid::nanoid;
use parking_lot::Once; use parking_lot::Once;
use tempfile::TempDir; use tempfile::TempDir;
use tracing_subscriber::{fmt::Subscriber, util::SubscriberInitExt, EnvFilter}; use tracing_subscriber::{fmt::Subscriber, util::SubscriberInitExt, EnvFilter};
use uuid::Uuid;
use collab_integrate::collab_builder::{ use collab_integrate::collab_builder::{
AppFlowyCollabBuilder, CollabCloudPluginProvider, CollabPluginProviderContext, AppFlowyCollabBuilder, CollabCloudPluginProvider, CollabPluginProviderContext,
CollabPluginProviderType, CollabPluginProviderType, WorkspaceCollabIntegrate,
}; };
use collab_integrate::CollabKVDB; use collab_integrate::CollabKVDB;
use flowy_document::document::MutexDocument; use flowy_document::document::MutexDocument;
@ -35,9 +34,17 @@ impl DocumentTest {
let cloud_service = Arc::new(LocalTestDocumentCloudServiceImpl()); let cloud_service = Arc::new(LocalTestDocumentCloudServiceImpl());
let file_storage = Arc::new(DocumentTestFileStorageService) as Arc<dyn ObjectStorageService>; let file_storage = Arc::new(DocumentTestFileStorageService) as Arc<dyn ObjectStorageService>;
let document_snapshot = Arc::new(DocumentTestSnapshot); let document_snapshot = Arc::new(DocumentTestSnapshot);
let builder = Arc::new(AppFlowyCollabBuilder::new(
DefaultCollabStorageProvider(),
WorkspaceCollabIntegrateImpl {
workspace_id: user.workspace_id.clone(),
},
));
let manager = DocumentManager::new( let manager = DocumentManager::new(
Arc::new(user), Arc::new(user),
default_collab_builder(), builder,
cloud_service, cloud_service,
Arc::downgrade(&file_storage), Arc::downgrade(&file_storage),
document_snapshot, document_snapshot,
@ -55,6 +62,7 @@ impl Deref for DocumentTest {
} }
pub struct FakeUser { pub struct FakeUser {
workspace_id: String,
collab_db: Arc<CollabKVDB>, collab_db: Arc<CollabKVDB>,
} }
@ -65,8 +73,12 @@ impl FakeUser {
let tempdir = TempDir::new().unwrap(); let tempdir = TempDir::new().unwrap();
let path = tempdir.into_path(); let path = tempdir.into_path();
let collab_db = Arc::new(CollabKVDB::open(path).unwrap()); let collab_db = Arc::new(CollabKVDB::open(path).unwrap());
let workspace_id = uuid::Uuid::new_v4().to_string();
Self { collab_db } Self {
collab_db,
workspace_id,
}
} }
} }
@ -76,7 +88,7 @@ impl DocumentUserService for FakeUser {
} }
fn workspace_id(&self) -> Result<String, FlowyError> { fn workspace_id(&self) -> Result<String, FlowyError> {
Ok(Uuid::new_v4().to_string()) Ok(self.workspace_id.clone())
} }
fn collab_db(&self, _uid: i64) -> Result<std::sync::Weak<CollabKVDB>, FlowyError> { fn collab_db(&self, _uid: i64) -> Result<std::sync::Weak<CollabKVDB>, FlowyError> {
@ -100,13 +112,6 @@ pub fn setup_log() {
}); });
} }
pub fn default_collab_builder() -> Arc<AppFlowyCollabBuilder> {
let builder =
AppFlowyCollabBuilder::new(DefaultCollabStorageProvider(), "fake_device_id".to_string());
builder.initialize(uuid::Uuid::new_v4().to_string());
Arc::new(builder)
}
pub async fn create_and_open_empty_document() -> (DocumentTest, Arc<MutexDocument>, String) { pub async fn create_and_open_empty_document() -> (DocumentTest, Arc<MutexDocument>, String) {
let test = DocumentTest::new(); let test = DocumentTest::new();
let doc_id: String = gen_document_id(); let doc_id: String = gen_document_id();
@ -222,3 +227,16 @@ impl DocumentSnapshotService for DocumentTestSnapshot {
todo!() todo!()
} }
} }
struct WorkspaceCollabIntegrateImpl {
workspace_id: String,
}
impl WorkspaceCollabIntegrate for WorkspaceCollabIntegrateImpl {
fn workspace_id(&self) -> Result<String, Error> {
Ok(self.workspace_id.clone())
}
fn device_id(&self) -> Result<String, Error> {
Ok("fake_device_id".to_string())
}
}

View File

@ -11,6 +11,9 @@ pub mod view_operation;
mod manager_init; mod manager_init;
mod manager_observer; mod manager_observer;
#[cfg(debug_assertions)]
pub mod manager_test_util;
pub mod share; pub mod share;
#[cfg(feature = "test_helper")] #[cfg(feature = "test_helper")]
mod test_helper; mod test_helper;

View File

@ -21,34 +21,28 @@ use collab::core::collab::{DataSource, MutexCollab};
use collab_entity::CollabType; use collab_entity::CollabType;
use collab_folder::error::FolderError; use collab_folder::error::FolderError;
use collab_folder::{ use collab_folder::{
Folder, FolderData, FolderNotify, Section, SectionItem, TrashInfo, UserId, View, ViewLayout, Folder, FolderNotify, Section, SectionItem, TrashInfo, UserId, View, ViewLayout, ViewUpdate,
ViewUpdate, Workspace, Workspace,
}; };
use collab_integrate::collab_builder::{AppFlowyCollabBuilder, CollabBuilderConfig}; use collab_integrate::collab_builder::{AppFlowyCollabBuilder, CollabBuilderConfig};
use collab_integrate::{CollabKVDB, CollabPersistenceConfig}; use collab_integrate::CollabKVDB;
use flowy_error::{ErrorCode, FlowyError, FlowyResult}; use flowy_error::{ErrorCode, FlowyError, FlowyResult};
use flowy_folder_pub::cloud::{gen_view_id, FolderCloudService}; use flowy_folder_pub::cloud::{gen_view_id, FolderCloudService};
use flowy_folder_pub::folder_builder::ParentChildViews; use flowy_folder_pub::folder_builder::ParentChildViews;
use flowy_search_pub::entities::FolderIndexManager; use flowy_search_pub::entities::FolderIndexManager;
use lib_infra::conditional_send_sync_trait;
use parking_lot::RwLock; use parking_lot::RwLock;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::ops::Deref; use std::ops::Deref;
use std::sync::{Arc, Weak}; use std::sync::{Arc, Weak};
use tracing::{error, info, instrument}; use tracing::{error, info, instrument};
conditional_send_sync_trait! { pub trait FolderUser: Send + Sync {
"[crate::manager::FolderUser] represents the user for folder.";
FolderUser {
fn user_id(&self) -> Result<i64, FlowyError>; fn user_id(&self) -> Result<i64, FlowyError>;
fn workspace_id(&self) -> Result<String, FlowyError>;
fn collab_db(&self, uid: i64) -> Result<Weak<CollabKVDB>, FlowyError>; fn collab_db(&self, uid: i64) -> Result<Weak<CollabKVDB>, FlowyError>;
}
} }
pub struct FolderManager { pub struct FolderManager {
/// workspace_id represents as the id of the Folder.
pub(crate) workspace_id: RwLock<Option<String>>,
/// MutexFolder is the folder that is used to store the data. /// MutexFolder is the folder that is used to store the data.
pub(crate) mutex_folder: Arc<MutexFolder>, pub(crate) mutex_folder: Arc<MutexFolder>,
pub(crate) collab_builder: Arc<AppFlowyCollabBuilder>, pub(crate) collab_builder: Arc<AppFlowyCollabBuilder>,
@ -73,7 +67,6 @@ impl FolderManager {
collab_builder, collab_builder,
operation_handlers, operation_handlers,
cloud_service, cloud_service,
workspace_id: Default::default(),
folder_indexer, folder_indexer,
}; };
@ -82,25 +75,20 @@ impl FolderManager {
#[instrument(level = "debug", skip(self), err)] #[instrument(level = "debug", skip(self), err)]
pub async fn get_current_workspace(&self) -> FlowyResult<WorkspacePB> { pub async fn get_current_workspace(&self) -> FlowyResult<WorkspacePB> {
let workspace_id = self.user.workspace_id()?;
self.with_folder( self.with_folder(
|| { || {
let uid = self.user.user_id()?; let uid = self.user.user_id()?;
let workspace_id = self
.workspace_id
.read()
.as_ref()
.cloned()
.ok_or_else(|| FlowyError::from(ErrorCode::WorkspaceInitializeError))?;
Err(workspace_data_not_sync_error(uid, &workspace_id)) Err(workspace_data_not_sync_error(uid, &workspace_id))
}, },
|folder| { |folder| {
let workspace_pb_from_workspace = |workspace: Workspace, folder: &Folder| { let workspace_pb_from_workspace = |workspace: Workspace, folder: &Folder| {
let views = get_workspace_public_view_pbs(folder); let views = get_workspace_public_view_pbs(&workspace_id, folder);
let workspace: WorkspacePB = (workspace, views).into(); let workspace: WorkspacePB = (workspace, views).into();
Ok::<WorkspacePB, FlowyError>(workspace) Ok::<WorkspacePB, FlowyError>(workspace)
}; };
match folder.get_current_workspace() { match folder.get_workspace_info(&workspace_id) {
None => Err(FlowyError::record_not_found().with_context("Can not find the workspace")), None => Err(FlowyError::record_not_found().with_context("Can not find the workspace")),
Some(workspace) => workspace_pb_from_workspace(workspace, folder), Some(workspace) => workspace_pb_from_workspace(workspace, folder),
} }
@ -111,28 +99,25 @@ impl FolderManager {
/// Return a list of views of the current workspace. /// Return a list of views of the current workspace.
/// Only the first level of child views are included. /// Only the first level of child views are included.
pub async fn get_current_workspace_public_views(&self) -> FlowyResult<Vec<ViewPB>> { pub async fn get_current_workspace_public_views(&self) -> FlowyResult<Vec<ViewPB>> {
let workspace_id = self let views = self.get_workspace_public_views().await?;
.mutex_folder Ok(views)
.read()
.as_ref()
.map(|folder| folder.get_workspace_id());
if workspace_id.is_some() {
self.get_workspace_public_views().await
} else {
tracing::warn!("Can't get the workspace id from the folder. Return empty list.");
Ok(vec![])
}
} }
pub async fn get_workspace_public_views(&self) -> FlowyResult<Vec<ViewPB>> { pub async fn get_workspace_public_views(&self) -> FlowyResult<Vec<ViewPB>> {
Ok(self.with_folder(Vec::new, get_workspace_public_view_pbs)) let workspace_id = self.user.workspace_id()?;
Ok(self.with_folder(Vec::new, |folder| {
get_workspace_public_view_pbs(&workspace_id, folder)
}))
} }
pub async fn get_workspace_private_views(&self) -> FlowyResult<Vec<ViewPB>> { pub async fn get_workspace_private_views(&self) -> FlowyResult<Vec<ViewPB>> {
Ok(self.with_folder(Vec::new, get_workspace_private_view_pbs)) let workspace_id = self.user.workspace_id()?;
Ok(self.with_folder(Vec::new, |folder| {
get_workspace_private_view_pbs(&workspace_id, folder)
}))
} }
#[instrument(level = "trace", skip_all, err)]
pub(crate) async fn make_folder<T: Into<Option<FolderNotify>>>( pub(crate) async fn make_folder<T: Into<Option<FolderNotify>>>(
&self, &self,
uid: i64, uid: i64,
@ -142,11 +127,6 @@ impl FolderManager {
folder_notifier: T, folder_notifier: T,
) -> Result<Folder, FlowyError> { ) -> Result<Folder, FlowyError> {
let folder_notifier = folder_notifier.into(); let folder_notifier = folder_notifier.into();
// snapshot_config will be deprecated in the future.
let snapshot_config = CollabPersistenceConfig::new()
.enable_snapshot(true)
.snapshot_per_update(50);
// only need the check the workspace id when the doc state is not from the disk. // only need the check the workspace id when the doc state is not from the disk.
let should_check_workspace_id = !matches!(doc_state, DataSource::Disk); let should_check_workspace_id = !matches!(doc_state, DataSource::Disk);
let should_auto_initialize = !should_check_workspace_id; let should_auto_initialize = !should_check_workspace_id;
@ -154,13 +134,14 @@ impl FolderManager {
.sync_enable(true) .sync_enable(true)
.auto_initialize(should_auto_initialize); .auto_initialize(should_auto_initialize);
let object_id = workspace_id;
let collab = self.collab_builder.build_with_config( let collab = self.collab_builder.build_with_config(
uid,
workspace_id, workspace_id,
uid,
object_id,
CollabType::Folder, CollabType::Folder,
collab_db, collab_db,
doc_state, doc_state,
snapshot_config,
config, config,
)?; )?;
let (should_clear, err) = match Folder::open(UserId::from(uid), collab.clone(), folder_notifier) let (should_clear, err) = match Folder::open(UserId::from(uid), collab.clone(), folder_notifier)
@ -172,7 +153,7 @@ impl FolderManager {
let folder_workspace_id = folder.get_workspace_id(); let folder_workspace_id = folder.get_workspace_id();
if folder_workspace_id != workspace_id { if folder_workspace_id != workspace_id {
error!( error!(
"expected workspace id: {}, actual workspace id: {}", "expect workspace_id: {}, actual workspace_id: {}",
workspace_id, folder_workspace_id workspace_id, folder_workspace_id
); );
return Err(FlowyError::workspace_data_not_match()); return Err(FlowyError::workspace_data_not_match());
@ -203,15 +184,14 @@ impl FolderManager {
workspace_id: &str, workspace_id: &str,
collab_db: Weak<CollabKVDB>, collab_db: Weak<CollabKVDB>,
) -> Result<Arc<MutexCollab>, FlowyError> { ) -> Result<Arc<MutexCollab>, FlowyError> {
let object_id = workspace_id;
let collab = self.collab_builder.build_with_config( let collab = self.collab_builder.build_with_config(
uid,
workspace_id, workspace_id,
uid,
object_id,
CollabType::Folder, CollabType::Folder,
collab_db, collab_db,
DataSource::Disk, DataSource::Disk,
CollabPersistenceConfig::new()
.enable_snapshot(true)
.snapshot_per_update(50),
CollabBuilderConfig::default().sync_enable(true), CollabBuilderConfig::default().sync_enable(true),
)?; )?;
Ok(collab) Ok(collab)
@ -220,19 +200,17 @@ impl FolderManager {
/// Initialize the folder with the given workspace id. /// Initialize the folder with the given workspace id.
/// Fetch the folder updates from the cloud service and initialize the folder. /// Fetch the folder updates from the cloud service and initialize the folder.
#[tracing::instrument(skip(self, user_id), err)] #[tracing::instrument(skip(self, user_id), err)]
pub async fn initialize_with_workspace_id( pub async fn initialize_with_workspace_id(&self, user_id: i64) -> FlowyResult<()> {
&self, let workspace_id = self.user.workspace_id()?;
user_id: i64, let object_id = &workspace_id;
workspace_id: &str,
) -> FlowyResult<()> {
let folder_doc_state = self let folder_doc_state = self
.cloud_service .cloud_service
.get_folder_doc_state(workspace_id, user_id, CollabType::Folder, workspace_id) .get_folder_doc_state(&workspace_id, user_id, CollabType::Folder, object_id)
.await?; .await?;
if let Err(err) = self if let Err(err) = self
.initialize( .initialize(
user_id, user_id,
workspace_id, &workspace_id,
FolderInitDataSource::Cloud(folder_doc_state), FolderInitDataSource::Cloud(folder_doc_state),
) )
.await .await
@ -243,7 +221,7 @@ impl FolderManager {
self self
.initialize( .initialize(
user_id, user_id,
workspace_id, &workspace_id,
FolderInitDataSource::LocalDisk { FolderInitDataSource::LocalDisk {
create_if_not_exist: false, create_if_not_exist: false,
}, },
@ -278,17 +256,17 @@ impl FolderManager {
.map_err(FlowyError::from); .map_err(FlowyError::from);
match result { match result {
Ok(folder_updates) => { Ok(folder_doc_state) => {
info!( info!(
"Get folder updates via {}, doc state len: {}", "Get folder updates via {}, doc state len: {}",
self.cloud_service.service_name(), self.cloud_service.service_name(),
folder_updates.len() folder_doc_state.len()
); );
self self
.initialize( .initialize(
user_id, user_id,
workspace_id, workspace_id,
FolderInitDataSource::Cloud(folder_updates), FolderInitDataSource::Cloud(folder_doc_state),
) )
.await?; .await?;
}, },
@ -318,12 +296,8 @@ impl FolderManager {
Ok(new_workspace) Ok(new_workspace)
} }
pub async fn get_workspace(&self, _workspace_id: &str) -> Option<Workspace> {
self.with_folder(|| None, |folder| folder.get_current_workspace())
}
pub async fn get_workspace_setting_pb(&self) -> FlowyResult<WorkspaceSettingPB> { pub async fn get_workspace_setting_pb(&self) -> FlowyResult<WorkspaceSettingPB> {
let workspace_id = self.get_current_workspace_id().await?; let workspace_id = self.user.workspace_id()?;
let latest_view = self.get_current_view().await; let latest_view = self.get_current_view().await;
Ok(WorkspaceSettingPB { Ok(WorkspaceSettingPB {
workspace_id, workspace_id,
@ -349,14 +323,14 @@ impl FolderManager {
} }
pub async fn get_workspace_pb(&self) -> FlowyResult<WorkspacePB> { pub async fn get_workspace_pb(&self) -> FlowyResult<WorkspacePB> {
let workspace_pb = { let workspace_id = self.user.workspace_id()?;
let guard = self.mutex_folder.read(); let guard = self.mutex_folder.read();
let folder = guard let folder = guard
.as_ref() .as_ref()
.ok_or(FlowyError::internal().with_context("folder is not initialized"))?; .ok_or(FlowyError::internal().with_context("folder is not initialized"))?;
let workspace = folder.get_current_workspace().ok_or( let workspace = folder
FlowyError::record_not_found().with_context("Can't find the current workspace id "), .get_workspace_info(&workspace_id)
)?; .ok_or_else(|| FlowyError::record_not_found().with_context("Can not find the workspace"))?;
let views = folder let views = folder
.views .views
@ -364,25 +338,14 @@ impl FolderManager {
.into_iter() .into_iter()
.map(|view| view_pb_without_child_views(view.as_ref().clone())) .map(|view| view_pb_without_child_views(view.as_ref().clone()))
.collect::<Vec<ViewPB>>(); .collect::<Vec<ViewPB>>();
drop(guard);
WorkspacePB { Ok(WorkspacePB {
id: workspace.id, id: workspace.id,
name: workspace.name, name: workspace.name,
views, views,
create_time: workspace.created_at, create_time: workspace.created_at,
} })
};
Ok(workspace_pb)
}
async fn get_current_workspace_id(&self) -> FlowyResult<String> {
self
.mutex_folder
.read()
.as_ref()
.map(|folder| folder.get_workspace_id())
.ok_or(FlowyError::internal().with_context("Unexpected empty workspace id"))
} }
/// This function acquires a lock on the `mutex_folder` and checks its state. /// This function acquires a lock on the `mutex_folder` and checks its state.
@ -404,17 +367,8 @@ impl FolderManager {
} }
} }
pub async fn get_all_workspaces(&self) -> Vec<Workspace> {
self.with_folder(Vec::new, |folder| {
let mut workspaces = vec![];
if let Some(workspace) = folder.get_current_workspace() {
workspaces.push(workspace);
}
workspaces
})
}
pub async fn create_view_with_params(&self, params: CreateViewParams) -> FlowyResult<View> { pub async fn create_view_with_params(&self, params: CreateViewParams) -> FlowyResult<View> {
let workspace_id = self.user.workspace_id()?;
let view_layout: ViewLayout = params.layout.clone().into(); let view_layout: ViewLayout = params.layout.clone().into();
let handler = self.get_handler(&view_layout)?; let handler = self.get_handler(&view_layout)?;
let user_id = self.user.user_id()?; let user_id = self.user.user_id()?;
@ -453,12 +407,10 @@ impl FolderManager {
}, },
); );
if let Ok(workspace_id) = self.get_current_workspace_id().await {
let folder = &self.mutex_folder.read(); let folder = &self.mutex_folder.read();
if let Some(folder) = folder.as_ref() { if let Some(folder) = folder.as_ref() {
notify_did_update_workspace(&workspace_id, folder); notify_did_update_workspace(&workspace_id, folder);
} }
}
Ok(view) Ok(view)
} }
@ -659,6 +611,7 @@ impl FolderManager {
/// ///
#[tracing::instrument(level = "trace", skip(self), err)] #[tracing::instrument(level = "trace", skip(self), err)]
pub async fn move_nested_view(&self, params: MoveNestedViewParams) -> FlowyResult<()> { pub async fn move_nested_view(&self, params: MoveNestedViewParams) -> FlowyResult<()> {
let workspace_id = self.user.workspace_id()?;
let view_id = params.view_id; let view_id = params.view_id;
let new_parent_id = params.new_parent_id; let new_parent_id = params.new_parent_id;
let prev_view_id = params.prev_view_id; let prev_view_id = params.prev_view_id;
@ -681,6 +634,7 @@ impl FolderManager {
}, },
); );
notify_parent_view_did_change( notify_parent_view_did_change(
&workspace_id,
self.mutex_folder.clone(), self.mutex_folder.clone(),
vec![new_parent_id, old_parent_id], vec![new_parent_id, old_parent_id],
); );
@ -693,6 +647,7 @@ impl FolderManager {
/// We need to convert the index to the real index of the view in the parent view. /// We need to convert the index to the real index of the view in the parent view.
#[tracing::instrument(level = "trace", skip(self), err)] #[tracing::instrument(level = "trace", skip(self), err)]
pub async fn move_view(&self, view_id: &str, from: usize, to: usize) -> FlowyResult<()> { pub async fn move_view(&self, view_id: &str, from: usize, to: usize) -> FlowyResult<()> {
let workspace_id = self.user.workspace_id()?;
if let Some((is_workspace, parent_view_id, child_views)) = self.get_view_relation(view_id).await if let Some((is_workspace, parent_view_id, child_views)) = self.get_view_relation(view_id).await
{ {
// The display parent view is the view that is displayed in the UI // The display parent view is the view that is displayed in the UI
@ -729,7 +684,11 @@ impl FolderManager {
folder.move_view(view_id, actual_from_index as u32, actual_to_index as u32); folder.move_view(view_id, actual_from_index as u32, actual_to_index as u32);
}, },
); );
notify_parent_view_did_change(self.mutex_folder.clone(), vec![parent_view_id]); notify_parent_view_did_change(
&workspace_id,
self.mutex_folder.clone(),
vec![parent_view_id],
);
} }
} }
} }
@ -819,12 +778,12 @@ impl FolderManager {
#[tracing::instrument(level = "trace", skip(self), err)] #[tracing::instrument(level = "trace", skip(self), err)]
pub(crate) async fn set_current_view(&self, view_id: &str) -> Result<(), FlowyError> { pub(crate) async fn set_current_view(&self, view_id: &str) -> Result<(), FlowyError> {
let workspace_id = self.with_folder( self.with_folder(
|| Err(FlowyError::record_not_found()), || Err(FlowyError::record_not_found()),
|folder| { |folder| {
folder.set_current_view(view_id); folder.set_current_view(view_id);
folder.add_recent_view_ids(vec![view_id.to_string()]); folder.add_recent_view_ids(vec![view_id.to_string()]);
Ok(folder.get_workspace_id()) Ok(())
}, },
)?; )?;
@ -836,6 +795,7 @@ impl FolderManager {
} }
} }
let workspace_id = self.user.workspace_id()?;
send_workspace_setting_notification(workspace_id, view); send_workspace_setting_notification(workspace_id, view);
Ok(()) Ok(())
} }
@ -992,6 +952,7 @@ impl FolderManager {
} }
pub(crate) async fn import(&self, import_data: ImportParams) -> FlowyResult<View> { pub(crate) async fn import(&self, import_data: ImportParams) -> FlowyResult<View> {
let workspace_id = self.user.workspace_id()?;
if import_data.data.is_none() && import_data.file_path.is_none() { if import_data.data.is_none() && import_data.file_path.is_none() {
return Err(FlowyError::new( return Err(FlowyError::new(
ErrorCode::InvalidParams, ErrorCode::InvalidParams,
@ -1040,7 +1001,11 @@ impl FolderManager {
folder.insert_view(view.clone(), None); folder.insert_view(view.clone(), None);
}, },
); );
notify_parent_view_did_change(self.mutex_folder.clone(), vec![view.parent_view_id.clone()]); notify_parent_view_did_change(
&workspace_id,
self.mutex_folder.clone(),
vec![view.parent_view_id.clone()],
);
Ok(view) Ok(view)
} }
@ -1049,6 +1014,7 @@ impl FolderManager {
where where
F: FnOnce(ViewUpdate) -> Option<View>, F: FnOnce(ViewUpdate) -> Option<View>,
{ {
let workspace_id = self.user.workspace_id()?;
let value = self.with_folder( let value = self.with_folder(
|| None, || None,
|folder| { |folder| {
@ -1070,13 +1036,11 @@ impl FolderManager {
.payload(view_pb) .payload(view_pb)
.send(); .send();
if let Ok(workspace_id) = self.get_current_workspace_id().await {
let folder = &self.mutex_folder.read(); let folder = &self.mutex_folder.read();
if let Some(folder) = folder.as_ref() { if let Some(folder) = folder.as_ref() {
notify_did_update_workspace(&workspace_id, folder); notify_did_update_workspace(&workspace_id, folder);
} }
} }
}
Ok(()) Ok(())
} }
@ -1100,12 +1064,13 @@ impl FolderManager {
/// Otherwise, the parent_view_id is the parent view id of the view. The child_view_ids is the /// Otherwise, the parent_view_id is the parent view id of the view. The child_view_ids is the
/// child view ids of the view. /// child view ids of the view.
async fn get_view_relation(&self, view_id: &str) -> Option<(bool, String, Vec<String>)> { async fn get_view_relation(&self, view_id: &str) -> Option<(bool, String, Vec<String>)> {
let workspace_id = self.user.workspace_id().ok()?;
self.with_folder( self.with_folder(
|| None, || None,
|folder| { |folder| {
let view = folder.views.get_view(view_id)?; let view = folder.views.get_view(view_id)?;
match folder.views.get_view(&view.parent_view_id) { match folder.views.get_view(&view.parent_view_id) {
None => folder.get_current_workspace().map(|workspace| { None => folder.get_workspace_info(&workspace_id).map(|workspace| {
( (
true, true,
workspace.id, workspace.id,
@ -1154,18 +1119,6 @@ impl FolderManager {
Ok(snapshots) Ok(snapshots)
} }
/// Only expose this method for testing
#[cfg(debug_assertions)]
pub fn get_mutex_folder(&self) -> &Arc<MutexFolder> {
&self.mutex_folder
}
/// Only expose this method for testing
#[cfg(debug_assertions)]
pub fn get_cloud_service(&self) -> &Arc<dyn FolderCloudService> {
&self.cloud_service
}
pub fn set_views_visibility(&self, view_ids: Vec<String>, is_public: bool) { pub fn set_views_visibility(&self, view_ids: Vec<String>, is_public: bool) {
self.with_folder( self.with_folder(
|| (), || (),
@ -1239,7 +1192,7 @@ impl FolderManager {
} }
/// Return the views that belong to the workspace. The views are filtered by the trash and all the private views. /// Return the views that belong to the workspace. The views are filtered by the trash and all the private views.
pub(crate) fn get_workspace_public_view_pbs(folder: &Folder) -> Vec<ViewPB> { pub(crate) fn get_workspace_public_view_pbs(workspace_id: &str, folder: &Folder) -> Vec<ViewPB> {
// get the trash ids // get the trash ids
let trash_ids = folder let trash_ids = folder
.get_all_trash_sections() .get_all_trash_sections()
@ -1254,8 +1207,7 @@ pub(crate) fn get_workspace_public_view_pbs(folder: &Folder) -> Vec<ViewPB> {
.map(|view| view.id) .map(|view| view.id)
.collect::<Vec<String>>(); .collect::<Vec<String>>();
let mut views = folder.get_workspace_views(); let mut views = folder.views.get_views_belong_to(workspace_id);
// filter the views that are in the trash and all the private views // filter the views that are in the trash and all the private views
views.retain(|view| !trash_ids.contains(&view.id) && !private_view_ids.contains(&view.id)); views.retain(|view| !trash_ids.contains(&view.id) && !private_view_ids.contains(&view.id));
@ -1289,7 +1241,7 @@ fn get_all_child_view_ids(folder: &Folder, view_id: &str) -> Vec<String> {
} }
/// Get the current private views of the user. /// Get the current private views of the user.
pub(crate) fn get_workspace_private_view_pbs(folder: &Folder) -> Vec<ViewPB> { pub(crate) fn get_workspace_private_view_pbs(workspace_id: &str, folder: &Folder) -> Vec<ViewPB> {
// get the trash ids // get the trash ids
let trash_ids = folder let trash_ids = folder
.get_all_trash_sections() .get_all_trash_sections()
@ -1304,8 +1256,7 @@ pub(crate) fn get_workspace_private_view_pbs(folder: &Folder) -> Vec<ViewPB> {
.map(|view| view.id) .map(|view| view.id)
.collect::<Vec<String>>(); .collect::<Vec<String>>();
let mut views = folder.get_workspace_views(); let mut views = folder.views.get_views_belong_to(workspace_id);
// filter the views that are in the trash and not in the private view ids // filter the views that are in the trash and not in the private view ids
views.retain(|view| !trash_ids.contains(&view.id) && private_view_ids.contains(&view.id)); views.retain(|view| !trash_ids.contains(&view.id) && private_view_ids.contains(&view.id));
@ -1342,8 +1293,6 @@ pub enum FolderInitDataSource {
LocalDisk { create_if_not_exist: bool }, LocalDisk { create_if_not_exist: bool },
/// If there is no data stored on local disk, we will use the data from the server to initialize the folder /// If there is no data stored on local disk, we will use the data from the server to initialize the folder
Cloud(Vec<u8>), Cloud(Vec<u8>),
/// If the user is new, we use the [DefaultFolderBuilder] to create the default folder.
FolderData(FolderData),
} }
impl Display for FolderInitDataSource { impl Display for FolderInitDataSource {
@ -1351,7 +1300,6 @@ impl Display for FolderInitDataSource {
match self { match self {
FolderInitDataSource::LocalDisk { .. } => f.write_fmt(format_args!("LocalDisk")), FolderInitDataSource::LocalDisk { .. } => f.write_fmt(format_args!("LocalDisk")),
FolderInitDataSource::Cloud(_) => f.write_fmt(format_args!("Cloud")), FolderInitDataSource::Cloud(_) => f.write_fmt(format_args!("Cloud")),
FolderInitDataSource::FolderData(_) => f.write_fmt(format_args!("Custom FolderData")),
} }
} }
} }

View File

@ -1,22 +1,14 @@
use collab_entity::CollabType;
use collab_folder::{Folder, FolderNotify, UserId};
use tokio::task::spawn_blocking;
use tracing::{event, Level};
use collab_integrate::CollabKVDB;
use flowy_error::{FlowyError, FlowyResult};
use collab::core::collab::DataSource;
use std::sync::{Arc, Weak};
use crate::manager::{FolderInitDataSource, FolderManager}; use crate::manager::{FolderInitDataSource, FolderManager};
use crate::manager_observer::{ use crate::manager_observer::*;
subscribe_folder_snapshot_state_changed, subscribe_folder_sync_state_changed,
subscribe_folder_trash_changed, subscribe_folder_view_changed,
};
use crate::user_default::DefaultFolderBuilder; use crate::user_default::DefaultFolderBuilder;
use collab::core::collab::DataSource;
use collab_entity::CollabType;
use collab_folder::{Folder, FolderNotify, UserId};
use collab_integrate::CollabKVDB;
use flowy_error::{FlowyError, FlowyResult};
use std::sync::{Arc, Weak};
use tokio::task::spawn_blocking;
use tracing::{event, info, Level};
impl FolderManager { impl FolderManager {
/// Called immediately after the application launched if the user already sign in/sign up. /// Called immediately after the application launched if the user already sign in/sign up.
@ -34,9 +26,13 @@ impl FolderManager {
workspace_id, workspace_id,
initial_data initial_data
); );
*self.workspace_id.write() = Some(workspace_id.to_string());
let workspace_id = workspace_id.to_string();
if let Some(old_folder) = self.mutex_folder.write().take() {
old_folder.close();
info!("remove old folder: {}", old_folder.get_workspace_id());
}
let workspace_id = workspace_id.to_string();
// Get the collab db for the user with given user id. // Get the collab db for the user with given user id.
let collab_db = self.user.collab_db(uid)?; let collab_db = self.user.collab_db(uid)?;
@ -115,22 +111,6 @@ impl FolderManager {
.await? .await?
} }
}, },
FolderInitDataSource::FolderData(folder_data) => {
if folder_data.workspace.id != workspace_id {
return Err(FlowyError::workspace_data_not_match());
}
event!(Level::INFO, "Restore folder with passed-in folder data");
let collab = self
.create_empty_collab(uid, &workspace_id, collab_db)
.await?;
Folder::create(
UserId::from(uid),
collab,
Some(folder_notifier),
folder_data,
)
},
}; };
let folder_state_rx = folder.subscribe_sync_state(); let folder_state_rx = folder.subscribe_sync_state();
@ -154,10 +134,28 @@ impl FolderManager {
*self.mutex_folder.write() = Some(folder); *self.mutex_folder.write() = Some(folder);
let weak_mutex_folder = Arc::downgrade(&self.mutex_folder); let weak_mutex_folder = Arc::downgrade(&self.mutex_folder);
subscribe_folder_sync_state_changed(workspace_id.clone(), folder_state_rx, &weak_mutex_folder); subscribe_folder_sync_state_changed(
subscribe_folder_snapshot_state_changed(workspace_id, &weak_mutex_folder); workspace_id.clone(),
subscribe_folder_trash_changed(section_change_rx, &weak_mutex_folder); folder_state_rx,
subscribe_folder_view_changed(view_rx, &weak_mutex_folder); Arc::downgrade(&self.user),
);
subscribe_folder_snapshot_state_changed(
workspace_id.clone(),
&weak_mutex_folder,
Arc::downgrade(&self.user),
);
subscribe_folder_trash_changed(
workspace_id.clone(),
section_change_rx,
&weak_mutex_folder,
Arc::downgrade(&self.user),
);
subscribe_folder_view_changed(
workspace_id.clone(),
view_rx,
&weak_mutex_folder,
Arc::downgrade(&self.user),
);
Ok(()) Ok(())
} }

View File

@ -1,32 +1,43 @@
use std::collections::HashSet; use crate::entities::{
use std::sync::{Arc, Weak}; view_pb_with_child_views, view_pb_without_child_views, ChildViewUpdatePB, FolderSnapshotStatePB,
FolderSyncStatePB, RepeatedTrashPB, RepeatedViewPB, SectionViewsPB, ViewPB, ViewSectionPB,
};
use crate::manager::{
get_workspace_private_view_pbs, get_workspace_public_view_pbs, FolderUser, MutexFolder,
};
use crate::notification::{send_notification, FolderNotification};
use collab::core::collab_state::SyncState; use collab::core::collab_state::SyncState;
use collab_folder::{ use collab_folder::{
Folder, SectionChange, SectionChangeReceiver, TrashSectionChange, View, ViewChange, Folder, SectionChange, SectionChangeReceiver, TrashSectionChange, View, ViewChange,
ViewChangeReceiver, ViewChangeReceiver,
}; };
use lib_dispatch::prelude::af_spawn;
use std::collections::HashSet;
use std::sync::{Arc, Weak};
use tokio_stream::wrappers::WatchStream; use tokio_stream::wrappers::WatchStream;
use tokio_stream::StreamExt; use tokio_stream::StreamExt;
use tracing::{event, Level}; use tracing::{event, trace, Level};
use lib_dispatch::prelude::af_spawn;
use crate::entities::{
view_pb_with_child_views, view_pb_without_child_views, ChildViewUpdatePB, FolderSnapshotStatePB,
FolderSyncStatePB, RepeatedTrashPB, RepeatedViewPB, SectionViewsPB, ViewPB, ViewSectionPB,
};
use crate::manager::{get_workspace_private_view_pbs, get_workspace_public_view_pbs, MutexFolder};
use crate::notification::{send_notification, FolderNotification};
/// Listen on the [ViewChange] after create/delete/update events happened /// Listen on the [ViewChange] after create/delete/update events happened
pub(crate) fn subscribe_folder_view_changed( pub(crate) fn subscribe_folder_view_changed(
workspace_id: String,
mut rx: ViewChangeReceiver, mut rx: ViewChangeReceiver,
weak_mutex_folder: &Weak<MutexFolder>, weak_mutex_folder: &Weak<MutexFolder>,
user: Weak<dyn FolderUser>,
) { ) {
let weak_mutex_folder = weak_mutex_folder.clone(); let weak_mutex_folder = weak_mutex_folder.clone();
af_spawn(async move { af_spawn(async move {
while let Ok(value) = rx.recv().await { while let Ok(value) = rx.recv().await {
if let Some(user) = user.upgrade() {
if let Ok(actual_workspace_id) = user.workspace_id() {
if actual_workspace_id != workspace_id {
trace!("Did break the loop when the workspace id is not matched");
// break the loop when the workspace id is not matched.
break;
}
}
}
if let Some(folder) = weak_mutex_folder.upgrade() { if let Some(folder) = weak_mutex_folder.upgrade() {
tracing::trace!("Did receive view change: {:?}", value); tracing::trace!("Did receive view change: {:?}", value);
match value { match value {
@ -35,7 +46,7 @@ pub(crate) fn subscribe_folder_view_changed(
view_pb_without_child_views(view.clone()), view_pb_without_child_views(view.clone()),
ChildViewChangeReason::Create, ChildViewChangeReason::Create,
); );
notify_parent_view_did_change(folder.clone(), vec![view.parent_view_id]); notify_parent_view_did_change(&workspace_id, folder.clone(), vec![view.parent_view_id]);
}, },
ViewChange::DidDeleteView { views } => { ViewChange::DidDeleteView { views } => {
for view in views { for view in views {
@ -51,7 +62,11 @@ pub(crate) fn subscribe_folder_view_changed(
view_pb_without_child_views(view.clone()), view_pb_without_child_views(view.clone()),
ChildViewChangeReason::Update, ChildViewChangeReason::Update,
); );
notify_parent_view_did_change(folder.clone(), vec![view.parent_view_id.clone()]); notify_parent_view_did_change(
&workspace_id,
folder.clone(),
vec![view.parent_view_id.clone()],
);
}, },
}; };
} }
@ -62,6 +77,7 @@ pub(crate) fn subscribe_folder_view_changed(
pub(crate) fn subscribe_folder_snapshot_state_changed( pub(crate) fn subscribe_folder_snapshot_state_changed(
workspace_id: String, workspace_id: String,
weak_mutex_folder: &Weak<MutexFolder>, weak_mutex_folder: &Weak<MutexFolder>,
user: Weak<dyn FolderUser>,
) { ) {
let weak_mutex_folder = weak_mutex_folder.clone(); let weak_mutex_folder = weak_mutex_folder.clone();
af_spawn(async move { af_spawn(async move {
@ -72,6 +88,14 @@ pub(crate) fn subscribe_folder_snapshot_state_changed(
.map(|folder| folder.subscribe_snapshot_state()); .map(|folder| folder.subscribe_snapshot_state());
if let Some(mut state_stream) = stream { if let Some(mut state_stream) = stream {
while let Some(snapshot_state) = state_stream.next().await { while let Some(snapshot_state) = state_stream.next().await {
if let Some(user) = user.upgrade() {
if let Ok(actual_workspace_id) = user.workspace_id() {
if actual_workspace_id != workspace_id {
// break the loop when the workspace id is not matched.
break;
}
}
}
if let Some(new_snapshot_id) = snapshot_state.snapshot_id() { if let Some(new_snapshot_id) = snapshot_state.snapshot_id() {
tracing::debug!("Did create folder remote snapshot: {}", new_snapshot_id); tracing::debug!("Did create folder remote snapshot: {}", new_snapshot_id);
send_notification( send_notification(
@ -90,10 +114,19 @@ pub(crate) fn subscribe_folder_snapshot_state_changed(
pub(crate) fn subscribe_folder_sync_state_changed( pub(crate) fn subscribe_folder_sync_state_changed(
workspace_id: String, workspace_id: String,
mut folder_sync_state_rx: WatchStream<SyncState>, mut folder_sync_state_rx: WatchStream<SyncState>,
_weak_mutex_folder: &Weak<MutexFolder>, user: Weak<dyn FolderUser>,
) { ) {
af_spawn(async move { af_spawn(async move {
while let Some(state) = folder_sync_state_rx.next().await { while let Some(state) = folder_sync_state_rx.next().await {
if let Some(user) = user.upgrade() {
if let Ok(actual_workspace_id) = user.workspace_id() {
if actual_workspace_id != workspace_id {
// break the loop when the workspace id is not matched.
break;
}
}
}
send_notification(&workspace_id, FolderNotification::DidUpdateFolderSyncUpdate) send_notification(&workspace_id, FolderNotification::DidUpdateFolderSyncUpdate)
.payload(FolderSyncStatePB::from(state)) .payload(FolderSyncStatePB::from(state))
.send(); .send();
@ -103,12 +136,23 @@ pub(crate) fn subscribe_folder_sync_state_changed(
/// Listen on the [TrashChange]s and notify the frontend some views were changed. /// Listen on the [TrashChange]s and notify the frontend some views were changed.
pub(crate) fn subscribe_folder_trash_changed( pub(crate) fn subscribe_folder_trash_changed(
workspace_id: String,
mut rx: SectionChangeReceiver, mut rx: SectionChangeReceiver,
weak_mutex_folder: &Weak<MutexFolder>, weak_mutex_folder: &Weak<MutexFolder>,
user: Weak<dyn FolderUser>,
) { ) {
let weak_mutex_folder = weak_mutex_folder.clone(); let weak_mutex_folder = weak_mutex_folder.clone();
af_spawn(async move { af_spawn(async move {
while let Ok(value) = rx.recv().await { while let Ok(value) = rx.recv().await {
if let Some(user) = user.upgrade() {
if let Ok(actual_workspace_id) = user.workspace_id() {
if actual_workspace_id != workspace_id {
// break the loop when the workspace id is not matched.
break;
}
}
}
if let Some(folder) = weak_mutex_folder.upgrade() { if let Some(folder) = weak_mutex_folder.upgrade() {
let mut unique_ids = HashSet::new(); let mut unique_ids = HashSet::new();
tracing::trace!("Did receive trash change: {:?}", value); tracing::trace!("Did receive trash change: {:?}", value);
@ -132,7 +176,7 @@ pub(crate) fn subscribe_folder_trash_changed(
} }
let parent_view_ids = unique_ids.into_iter().collect(); let parent_view_ids = unique_ids.into_iter().collect();
notify_parent_view_did_change(folder.clone(), parent_view_ids); notify_parent_view_did_change(&workspace_id, folder.clone(), parent_view_ids);
}, },
} }
} }
@ -143,12 +187,12 @@ pub(crate) fn subscribe_folder_trash_changed(
/// Notify the list of parent view ids that its child views were changed. /// Notify the list of parent view ids that its child views were changed.
#[tracing::instrument(level = "debug", skip(folder, parent_view_ids))] #[tracing::instrument(level = "debug", skip(folder, parent_view_ids))]
pub(crate) fn notify_parent_view_did_change<T: AsRef<str>>( pub(crate) fn notify_parent_view_did_change<T: AsRef<str>>(
workspace_id: &str,
folder: Arc<MutexFolder>, folder: Arc<MutexFolder>,
parent_view_ids: Vec<T>, parent_view_ids: Vec<T>,
) -> Option<()> { ) -> Option<()> {
let folder = folder.read(); let folder = folder.read();
let folder = folder.as_ref()?; let folder = folder.as_ref()?;
let workspace_id = folder.get_workspace_id();
let trash_ids = folder let trash_ids = folder
.get_all_trash_sections() .get_all_trash_sections()
.into_iter() .into_iter()
@ -161,8 +205,8 @@ pub(crate) fn notify_parent_view_did_change<T: AsRef<str>>(
// if the view's parent id equal to workspace id. Then it will fetch the current // if the view's parent id equal to workspace id. Then it will fetch the current
// workspace views. Because the workspace is not a view stored in the views map. // workspace views. Because the workspace is not a view stored in the views map.
if parent_view_id == workspace_id { if parent_view_id == workspace_id {
notify_did_update_workspace(&workspace_id, folder); notify_did_update_workspace(workspace_id, folder);
notify_did_update_section_views(&workspace_id, folder); notify_did_update_section_views(workspace_id, folder);
} else { } else {
// Parent view can contain a list of child views. Currently, only get the first level // Parent view can contain a list of child views. Currently, only get the first level
// child views. // child views.
@ -183,8 +227,8 @@ pub(crate) fn notify_parent_view_did_change<T: AsRef<str>>(
} }
pub(crate) fn notify_did_update_section_views(workspace_id: &str, folder: &Folder) { pub(crate) fn notify_did_update_section_views(workspace_id: &str, folder: &Folder) {
let public_views = get_workspace_public_view_pbs(folder); let public_views = get_workspace_public_view_pbs(workspace_id, folder);
let private_views = get_workspace_private_view_pbs(folder); let private_views = get_workspace_private_view_pbs(workspace_id, folder);
tracing::trace!( tracing::trace!(
"Did update section views: public len = {}, private len = {}", "Did update section views: public len = {}, private len = {}",
public_views.len(), public_views.len(),
@ -210,7 +254,7 @@ pub(crate) fn notify_did_update_section_views(workspace_id: &str, folder: &Folde
} }
pub(crate) fn notify_did_update_workspace(workspace_id: &str, folder: &Folder) { pub(crate) fn notify_did_update_workspace(workspace_id: &str, folder: &Folder) {
let repeated_view: RepeatedViewPB = get_workspace_public_view_pbs(folder).into(); let repeated_view: RepeatedViewPB = get_workspace_public_view_pbs(workspace_id, folder).into();
send_notification(workspace_id, FolderNotification::DidUpdateWorkspaceViews) send_notification(workspace_id, FolderNotification::DidUpdateWorkspaceViews)
.payload(repeated_view) .payload(repeated_view)
.send(); .send();

View File

@ -0,0 +1,32 @@
use crate::manager::{FolderManager, FolderUser, MutexFolder};
use crate::view_operation::FolderOperationHandlers;
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
use flowy_folder_pub::cloud::FolderCloudService;
use flowy_search_pub::entities::FolderIndexManager;
use std::sync::Arc;
impl FolderManager {
pub fn get_mutex_folder(&self) -> Arc<MutexFolder> {
self.mutex_folder.clone()
}
pub fn get_cloud_service(&self) -> Arc<dyn FolderCloudService> {
self.cloud_service.clone()
}
pub fn get_user(&self) -> Arc<dyn FolderUser> {
self.user.clone()
}
pub fn get_indexer(&self) -> Arc<dyn FolderIndexManager> {
self.folder_indexer.clone()
}
pub fn get_collab_builder(&self) -> Arc<AppFlowyCollabBuilder> {
self.collab_builder.clone()
}
pub fn get_operation_handlers(&self) -> FolderOperationHandlers {
self.operation_handlers.clone()
}
}

View File

@ -1,14 +1,11 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use super::notifier::{SearchNotifier, SearchResultChanged, SearchResultReceiverRunner};
use crate::entities::{SearchFilterPB, SearchResultNotificationPB, SearchResultPB};
use flowy_error::FlowyResult; use flowy_error::FlowyResult;
use lib_dispatch::prelude::af_spawn; use lib_dispatch::prelude::af_spawn;
use tokio::{sync::broadcast, task::spawn_blocking}; use tokio::{sync::broadcast, task::spawn_blocking};
use crate::entities::{SearchFilterPB, SearchResultNotificationPB, SearchResultPB};
use super::notifier::{SearchNotifier, SearchResultChanged, SearchResultReceiverRunner};
#[derive(Debug, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum SearchType { pub enum SearchType {
Folder, Folder,

View File

@ -1,4 +1,12 @@
use flowy_error::FlowyResult;
pub const USER_SIGN_IN_URL: &str = "sign_in_url"; pub const USER_SIGN_IN_URL: &str = "sign_in_url";
pub const USER_UUID: &str = "uuid"; pub const USER_UUID: &str = "uuid";
pub const USER_EMAIL: &str = "email"; pub const USER_EMAIL: &str = "email";
pub const USER_DEVICE_ID: &str = "device_id"; pub const USER_DEVICE_ID: &str = "device_id";
/// Represents a user that is currently using the server.
pub trait ServerUser: Send + Sync {
/// different user might return different workspace id.
fn workspace_id(&self) -> FlowyResult<String>;
}

View File

@ -5,19 +5,26 @@ use client_api::error::ErrorCode::RecordNotFound;
use collab::core::collab::DataSource; use collab::core::collab::DataSource;
use collab::entity::EncodedCollab; use collab::entity::EncodedCollab;
use collab_entity::CollabType; use collab_entity::CollabType;
use tracing::error; use std::sync::Arc;
use tracing::{error, instrument};
use flowy_database_pub::cloud::{CollabDocStateByOid, DatabaseCloudService, DatabaseSnapshot}; use flowy_database_pub::cloud::{CollabDocStateByOid, DatabaseCloudService, DatabaseSnapshot};
use lib_infra::future::FutureResult; use lib_infra::future::FutureResult;
use crate::af_cloud::define::ServerUser;
use crate::af_cloud::impls::util::check_request_workspace_id_is_match;
use crate::af_cloud::AFServer; use crate::af_cloud::AFServer;
pub(crate) struct AFCloudDatabaseCloudServiceImpl<T>(pub T); pub(crate) struct AFCloudDatabaseCloudServiceImpl<T> {
pub inner: T,
pub user: Arc<dyn ServerUser>,
}
impl<T> DatabaseCloudService for AFCloudDatabaseCloudServiceImpl<T> impl<T> DatabaseCloudService for AFCloudDatabaseCloudServiceImpl<T>
where where
T: AFServer, T: AFServer,
{ {
#[instrument(level = "debug", skip_all)]
fn get_database_object_doc_state( fn get_database_object_doc_state(
&self, &self,
object_id: &str, object_id: &str,
@ -26,17 +33,25 @@ where
) -> FutureResult<Option<Vec<u8>>, Error> { ) -> FutureResult<Option<Vec<u8>>, Error> {
let workspace_id = workspace_id.to_string(); let workspace_id = workspace_id.to_string();
let object_id = object_id.to_string(); let object_id = object_id.to_string();
let try_get_client = self.0.try_get_client(); let try_get_client = self.inner.try_get_client();
let cloned_user = self.user.clone();
FutureResult::new(async move { FutureResult::new(async move {
let params = QueryCollabParams { let params = QueryCollabParams {
workspace_id, workspace_id: workspace_id.clone(),
inner: QueryCollab { inner: QueryCollab {
object_id, object_id: object_id.clone(),
collab_type, collab_type: collab_type.clone(),
}, },
}; };
match try_get_client?.get_collab(params).await { match try_get_client?.get_collab(params).await {
Ok(data) => Ok(Some(data.encode_collab.doc_state.to_vec())), Ok(data) => {
check_request_workspace_id_is_match(
&workspace_id,
&cloned_user,
format!("get database object: {}:{}", object_id, collab_type),
)?;
Ok(Some(data.encode_collab.doc_state.to_vec()))
},
Err(err) => { Err(err) => {
if err.code == RecordNotFound { if err.code == RecordNotFound {
Ok(None) Ok(None)
@ -48,6 +63,7 @@ where
}) })
} }
#[instrument(level = "debug", skip_all)]
fn batch_get_database_object_doc_state( fn batch_get_database_object_doc_state(
&self, &self,
object_ids: Vec<String>, object_ids: Vec<String>,
@ -55,7 +71,8 @@ where
workspace_id: &str, workspace_id: &str,
) -> FutureResult<CollabDocStateByOid, Error> { ) -> FutureResult<CollabDocStateByOid, Error> {
let workspace_id = workspace_id.to_string(); let workspace_id = workspace_id.to_string();
let try_get_client = self.0.try_get_client(); let try_get_client = self.inner.try_get_client();
let cloned_user = self.user.clone();
FutureResult::new(async move { FutureResult::new(async move {
let client = try_get_client?; let client = try_get_client?;
let params = object_ids let params = object_ids
@ -66,6 +83,11 @@ where
}) })
.collect(); .collect();
let results = client.batch_get_collab(&workspace_id, params).await?; let results = client.batch_get_collab(&workspace_id, params).await?;
check_request_workspace_id_is_match(
&workspace_id,
&cloned_user,
"batch get database object",
)?;
Ok( Ok(
results results
.0 .0

View File

@ -4,30 +4,39 @@ use collab::core::collab::DataSource;
use collab::core::origin::CollabOrigin; use collab::core::origin::CollabOrigin;
use collab_document::document::Document; use collab_document::document::Document;
use collab_entity::CollabType; use collab_entity::CollabType;
use std::sync::Arc;
use tracing::instrument;
use flowy_document_pub::cloud::*; use flowy_document_pub::cloud::*;
use flowy_error::FlowyError; use flowy_error::FlowyError;
use lib_infra::future::FutureResult; use lib_infra::future::FutureResult;
use crate::af_cloud::define::ServerUser;
use crate::af_cloud::impls::util::check_request_workspace_id_is_match;
use crate::af_cloud::AFServer; use crate::af_cloud::AFServer;
pub(crate) struct AFCloudDocumentCloudServiceImpl<T>(pub T); pub(crate) struct AFCloudDocumentCloudServiceImpl<T> {
pub inner: T,
pub user: Arc<dyn ServerUser>,
}
impl<T> DocumentCloudService for AFCloudDocumentCloudServiceImpl<T> impl<T> DocumentCloudService for AFCloudDocumentCloudServiceImpl<T>
where where
T: AFServer, T: AFServer,
{ {
#[instrument(level = "debug", skip_all, fields(document_id = %document_id))]
fn get_document_doc_state( fn get_document_doc_state(
&self, &self,
document_id: &str, document_id: &str,
workspace_id: &str, workspace_id: &str,
) -> FutureResult<Vec<u8>, FlowyError> { ) -> FutureResult<Vec<u8>, FlowyError> {
let workspace_id = workspace_id.to_string(); let workspace_id = workspace_id.to_string();
let try_get_client = self.0.try_get_client(); let try_get_client = self.inner.try_get_client();
let document_id = document_id.to_string(); let document_id = document_id.to_string();
let cloned_user = self.user.clone();
FutureResult::new(async move { FutureResult::new(async move {
let params = QueryCollabParams { let params = QueryCollabParams {
workspace_id, workspace_id: workspace_id.clone(),
inner: QueryCollab { inner: QueryCollab {
object_id: document_id.to_string(), object_id: document_id.to_string(),
collab_type: CollabType::Document, collab_type: CollabType::Document,
@ -40,6 +49,13 @@ where
.encode_collab .encode_collab
.doc_state .doc_state
.to_vec(); .to_vec();
check_request_workspace_id_is_match(
&workspace_id,
&cloned_user,
format!("get document doc state:{}", document_id),
)?;
Ok(doc_state) Ok(doc_state)
}) })
} }
@ -53,17 +69,19 @@ where
FutureResult::new(async move { Ok(vec![]) }) FutureResult::new(async move { Ok(vec![]) })
} }
#[instrument(level = "debug", skip_all)]
fn get_document_data( fn get_document_data(
&self, &self,
document_id: &str, document_id: &str,
workspace_id: &str, workspace_id: &str,
) -> FutureResult<Option<DocumentData>, Error> { ) -> FutureResult<Option<DocumentData>, Error> {
let try_get_client = self.0.try_get_client(); let try_get_client = self.inner.try_get_client();
let document_id = document_id.to_string(); let document_id = document_id.to_string();
let workspace_id = workspace_id.to_string(); let workspace_id = workspace_id.to_string();
let cloned_user = self.user.clone();
FutureResult::new(async move { FutureResult::new(async move {
let params = QueryCollabParams { let params = QueryCollabParams {
workspace_id, workspace_id: workspace_id.clone(),
inner: QueryCollab { inner: QueryCollab {
object_id: document_id.clone(), object_id: document_id.clone(),
collab_type: CollabType::Document, collab_type: CollabType::Document,
@ -76,6 +94,11 @@ where
.encode_collab .encode_collab
.doc_state .doc_state
.to_vec(); .to_vec();
check_request_workspace_id_is_match(
&workspace_id,
&cloned_user,
format!("Get {} document", document_id),
)?;
let document = Document::from_doc_state( let document = Document::from_doc_state(
CollabOrigin::Empty, CollabOrigin::Empty,
DataSource::DocStateV1(doc_state), DataSource::DocStateV1(doc_state),

View File

@ -6,6 +6,8 @@ use collab::core::collab::DataSource;
use collab::core::origin::CollabOrigin; use collab::core::origin::CollabOrigin;
use collab_entity::CollabType; use collab_entity::CollabType;
use collab_folder::RepeatedViewIdentifier; use collab_folder::RepeatedViewIdentifier;
use std::sync::Arc;
use tracing::instrument;
use flowy_error::FlowyError; use flowy_error::FlowyError;
use flowy_folder_pub::cloud::{ use flowy_folder_pub::cloud::{
@ -14,16 +16,21 @@ use flowy_folder_pub::cloud::{
}; };
use lib_infra::future::FutureResult; use lib_infra::future::FutureResult;
use crate::af_cloud::define::ServerUser;
use crate::af_cloud::impls::util::check_request_workspace_id_is_match;
use crate::af_cloud::AFServer; use crate::af_cloud::AFServer;
pub(crate) struct AFCloudFolderCloudServiceImpl<T>(pub T); pub(crate) struct AFCloudFolderCloudServiceImpl<T> {
pub inner: T,
pub user: Arc<dyn ServerUser>,
}
impl<T> FolderCloudService for AFCloudFolderCloudServiceImpl<T> impl<T> FolderCloudService for AFCloudFolderCloudServiceImpl<T>
where where
T: AFServer, T: AFServer,
{ {
fn create_workspace(&self, _uid: i64, name: &str) -> FutureResult<Workspace, Error> { fn create_workspace(&self, _uid: i64, name: &str) -> FutureResult<Workspace, Error> {
let try_get_client = self.0.try_get_client(); let try_get_client = self.inner.try_get_client();
let cloned_name = name.to_string(); let cloned_name = name.to_string();
FutureResult::new(async move { FutureResult::new(async move {
let client = try_get_client?; let client = try_get_client?;
@ -47,7 +54,7 @@ where
fn open_workspace(&self, workspace_id: &str) -> FutureResult<(), Error> { fn open_workspace(&self, workspace_id: &str) -> FutureResult<(), Error> {
let workspace_id = workspace_id.to_string(); let workspace_id = workspace_id.to_string();
let try_get_client = self.0.try_get_client(); let try_get_client = self.inner.try_get_client();
FutureResult::new(async move { FutureResult::new(async move {
let client = try_get_client?; let client = try_get_client?;
let _ = client.open_workspace(&workspace_id).await?; let _ = client.open_workspace(&workspace_id).await?;
@ -56,7 +63,7 @@ where
} }
fn get_all_workspace(&self) -> FutureResult<Vec<WorkspaceRecord>, Error> { fn get_all_workspace(&self) -> FutureResult<Vec<WorkspaceRecord>, Error> {
let try_get_client = self.0.try_get_client(); let try_get_client = self.inner.try_get_client();
FutureResult::new(async move { FutureResult::new(async move {
let client = try_get_client?; let client = try_get_client?;
let records = client let records = client
@ -73,7 +80,7 @@ where
Ok(records) Ok(records)
}) })
} }
#[instrument(level = "debug", skip_all)]
fn get_folder_data( fn get_folder_data(
&self, &self,
workspace_id: &str, workspace_id: &str,
@ -81,7 +88,8 @@ where
) -> FutureResult<Option<FolderData>, Error> { ) -> FutureResult<Option<FolderData>, Error> {
let uid = *uid; let uid = *uid;
let workspace_id = workspace_id.to_string(); let workspace_id = workspace_id.to_string();
let try_get_client = self.0.try_get_client(); let try_get_client = self.inner.try_get_client();
let cloned_user = self.user.clone();
FutureResult::new(async move { FutureResult::new(async move {
let params = QueryCollabParams { let params = QueryCollabParams {
workspace_id: workspace_id.clone(), workspace_id: workspace_id.clone(),
@ -97,6 +105,7 @@ where
.encode_collab .encode_collab
.doc_state .doc_state
.to_vec(); .to_vec();
check_request_workspace_id_is_match(&workspace_id, &cloned_user, "get folder data")?;
let folder = Folder::from_collab_doc_state( let folder = Folder::from_collab_doc_state(
uid, uid,
CollabOrigin::Empty, CollabOrigin::Empty,
@ -104,7 +113,7 @@ where
&workspace_id, &workspace_id,
vec![], vec![],
)?; )?;
Ok(folder.get_folder_data()) Ok(folder.get_folder_data(&workspace_id))
}) })
} }
@ -116,6 +125,7 @@ where
FutureResult::new(async move { Ok(vec![]) }) FutureResult::new(async move { Ok(vec![]) })
} }
#[instrument(level = "debug", skip_all)]
fn get_folder_doc_state( fn get_folder_doc_state(
&self, &self,
workspace_id: &str, workspace_id: &str,
@ -125,10 +135,11 @@ where
) -> FutureResult<Vec<u8>, Error> { ) -> FutureResult<Vec<u8>, Error> {
let object_id = object_id.to_string(); let object_id = object_id.to_string();
let workspace_id = workspace_id.to_string(); let workspace_id = workspace_id.to_string();
let try_get_client = self.0.try_get_client(); let try_get_client = self.inner.try_get_client();
let cloned_user = self.user.clone();
FutureResult::new(async move { FutureResult::new(async move {
let params = QueryCollabParams { let params = QueryCollabParams {
workspace_id, workspace_id: workspace_id.clone(),
inner: QueryCollab { inner: QueryCollab {
object_id, object_id,
collab_type, collab_type,
@ -141,6 +152,7 @@ where
.encode_collab .encode_collab
.doc_state .doc_state
.to_vec(); .to_vec();
check_request_workspace_id_is_match(&workspace_id, &cloned_user, "get folder doc state")?;
Ok(doc_state) Ok(doc_state)
}) })
} }
@ -151,7 +163,7 @@ where
objects: Vec<FolderCollabParams>, objects: Vec<FolderCollabParams>,
) -> FutureResult<(), Error> { ) -> FutureResult<(), Error> {
let workspace_id = workspace_id.to_string(); let workspace_id = workspace_id.to_string();
let try_get_client = self.0.try_get_client(); let try_get_client = self.inner.try_get_client();
FutureResult::new(async move { FutureResult::new(async move {
let params = objects let params = objects
.into_iter() .into_iter()

View File

@ -9,3 +9,4 @@ mod document;
mod file_storage; mod file_storage;
mod folder; mod folder;
mod user; mod user;
mod util;

View File

@ -13,6 +13,7 @@ use client_api::entity::{QueryCollab, QueryCollabParams};
use client_api::{Client, ClientConfiguration}; use client_api::{Client, ClientConfiguration};
use collab_entity::{CollabObject, CollabType}; use collab_entity::{CollabObject, CollabType};
use parking_lot::RwLock; use parking_lot::RwLock;
use tracing::instrument;
use flowy_error::{ErrorCode, FlowyError, FlowyResult}; use flowy_error::{ErrorCode, FlowyError, FlowyResult};
use flowy_user_pub::cloud::{UserCloudService, UserCollabParams, UserUpdate, UserUpdateReceiver}; use flowy_user_pub::cloud::{UserCloudService, UserCollabParams, UserUpdate, UserUpdateReceiver};
@ -24,11 +25,12 @@ use lib_infra::box_any::BoxAny;
use lib_infra::future::FutureResult; use lib_infra::future::FutureResult;
use uuid::Uuid; use uuid::Uuid;
use crate::af_cloud::define::USER_SIGN_IN_URL; use crate::af_cloud::define::{ServerUser, USER_SIGN_IN_URL};
use crate::af_cloud::impls::user::dto::{ use crate::af_cloud::impls::user::dto::{
af_update_from_update_params, from_af_workspace_member, to_af_role, user_profile_from_af_profile, af_update_from_update_params, from_af_workspace_member, to_af_role, user_profile_from_af_profile,
}; };
use crate::af_cloud::impls::user::util::encryption_type_from_profile; use crate::af_cloud::impls::user::util::encryption_type_from_profile;
use crate::af_cloud::impls::util::check_request_workspace_id_is_match;
use crate::af_cloud::{AFCloudClient, AFServer}; use crate::af_cloud::{AFCloudClient, AFServer};
use super::dto::{from_af_workspace_invitation_status, to_workspace_invitation_status}; use super::dto::{from_af_workspace_invitation_status, to_workspace_invitation_status};
@ -36,13 +38,19 @@ use super::dto::{from_af_workspace_invitation_status, to_workspace_invitation_st
pub(crate) struct AFCloudUserAuthServiceImpl<T> { pub(crate) struct AFCloudUserAuthServiceImpl<T> {
server: T, server: T,
user_change_recv: RwLock<Option<tokio::sync::mpsc::Receiver<UserUpdate>>>, user_change_recv: RwLock<Option<tokio::sync::mpsc::Receiver<UserUpdate>>>,
user: Arc<dyn ServerUser>,
} }
impl<T> AFCloudUserAuthServiceImpl<T> { impl<T> AFCloudUserAuthServiceImpl<T> {
pub(crate) fn new(server: T, user_change_recv: tokio::sync::mpsc::Receiver<UserUpdate>) -> Self { pub(crate) fn new(
server: T,
user_change_recv: tokio::sync::mpsc::Receiver<UserUpdate>,
user: Arc<dyn ServerUser>,
) -> Self {
Self { Self {
server, server,
user_change_recv: RwLock::new(Some(user_change_recv)), user_change_recv: RwLock::new(Some(user_change_recv)),
user,
} }
} }
} }
@ -166,16 +174,27 @@ where
}) })
} }
#[instrument(level = "debug", skip_all)]
fn get_user_profile( fn get_user_profile(
&self, &self,
_credential: UserCredentials, _credential: UserCredentials,
) -> FutureResult<UserProfile, FlowyError> { ) -> FutureResult<UserProfile, FlowyError> {
let try_get_client = self.server.try_get_client(); let try_get_client = self.server.try_get_client();
let cloned_user = self.user.clone();
FutureResult::new(async move { FutureResult::new(async move {
let expected_workspace_id = cloned_user.workspace_id()?;
let client = try_get_client?; let client = try_get_client?;
let profile = client.get_profile().await?; let profile = client.get_profile().await?;
let token = client.get_token()?; let token = client.get_token()?;
let profile = user_profile_from_af_profile(token, profile)?; let profile = user_profile_from_af_profile(token, profile)?;
// Discard the response if the user has switched to a new workspace. This avoids updating the
// user profile with potentially outdated information when the workspace ID no longer matches.
check_request_workspace_id_is_match(
&expected_workspace_id,
&cloned_user,
"get user profile",
)?;
Ok(profile) Ok(profile)
}) })
} }
@ -315,6 +334,7 @@ where
}) })
} }
#[instrument(level = "debug", skip_all)]
fn get_user_awareness_doc_state( fn get_user_awareness_doc_state(
&self, &self,
_uid: i64, _uid: i64,
@ -324,16 +344,21 @@ where
let workspace_id = workspace_id.to_string(); let workspace_id = workspace_id.to_string();
let object_id = object_id.to_string(); let object_id = object_id.to_string();
let try_get_client = self.server.try_get_client(); let try_get_client = self.server.try_get_client();
FutureResult::new(async { let cloned_user = self.user.clone();
FutureResult::new(async move {
let params = QueryCollabParams { let params = QueryCollabParams {
workspace_id, workspace_id: workspace_id.clone(),
inner: QueryCollab { inner: QueryCollab {
object_id, object_id,
collab_type: CollabType::UserAwareness, collab_type: CollabType::UserAwareness,
}, },
}; };
let resp = try_get_client?.get_collab(params).await?; let resp = try_get_client?.get_collab(params).await?;
check_request_workspace_id_is_match(
&workspace_id,
&cloned_user,
"get user awareness object",
)?;
Ok(resp.encode_collab.doc_state.to_vec()) Ok(resp.encode_collab.doc_state.to_vec())
}) })
} }

View File

@ -0,0 +1,29 @@
use crate::af_cloud::define::ServerUser;
use flowy_error::{FlowyError, FlowyResult};
use std::sync::Arc;
use tracing::warn;
/// Validates the workspace_id provided in the request.
/// It checks that the workspace_id from the request matches the current user's active workspace_id.
/// This ensures that the operation is being performed in the correct workspace context, enhancing security.
pub fn check_request_workspace_id_is_match(
expected_workspace_id: &str,
user: &Arc<dyn ServerUser>,
action: impl AsRef<str>,
) -> FlowyResult<()> {
let actual_workspace_id = user.workspace_id()?;
if expected_workspace_id != actual_workspace_id {
warn!(
"{}, expect workspace_id: {}, actual workspace_id: {}",
action.as_ref(),
expected_workspace_id,
actual_workspace_id
);
return Err(
FlowyError::internal()
.with_context("Current workspace was changed when processing the request"),
);
}
Ok(())
}

View File

@ -17,6 +17,7 @@ use tokio_stream::wrappers::WatchStream;
use tracing::{error, event, info, warn}; use tracing::{error, event, info, warn};
use uuid::Uuid; use uuid::Uuid;
use crate::af_cloud::define::ServerUser;
use flowy_database_pub::cloud::DatabaseCloudService; use flowy_database_pub::cloud::DatabaseCloudService;
use flowy_document_pub::cloud::DocumentCloudService; use flowy_document_pub::cloud::DocumentCloudService;
use flowy_error::{ErrorCode, FlowyError}; use flowy_error::{ErrorCode, FlowyError};
@ -42,6 +43,7 @@ pub struct AppFlowyCloudServer {
network_reachable: Arc<AtomicBool>, network_reachable: Arc<AtomicBool>,
pub device_id: String, pub device_id: String,
ws_client: Arc<WSClient>, ws_client: Arc<WSClient>,
user: Arc<dyn ServerUser>,
} }
impl AppFlowyCloudServer { impl AppFlowyCloudServer {
@ -50,6 +52,7 @@ impl AppFlowyCloudServer {
enable_sync: bool, enable_sync: bool,
mut device_id: String, mut device_id: String,
client_version: &str, client_version: &str,
user: Arc<dyn ServerUser>,
) -> Self { ) -> Self {
// The device id can't be empty, so we generate a new one if it is. // The device id can't be empty, so we generate a new one if it is.
if device_id.is_empty() { if device_id.is_empty() {
@ -83,6 +86,7 @@ impl AppFlowyCloudServer {
network_reachable, network_reachable,
device_id, device_id,
ws_client, ws_client,
user,
} }
} }
@ -159,28 +163,41 @@ impl AppFlowyServer for AppFlowyCloudServer {
} }
}); });
Arc::new(AFCloudUserAuthServiceImpl::new(server, rx)) Arc::new(AFCloudUserAuthServiceImpl::new(
server,
rx,
self.user.clone(),
))
} }
fn folder_service(&self) -> Arc<dyn FolderCloudService> { fn folder_service(&self) -> Arc<dyn FolderCloudService> {
let server = AFServerImpl { let server = AFServerImpl {
client: self.get_client(), client: self.get_client(),
}; };
Arc::new(AFCloudFolderCloudServiceImpl(server)) Arc::new(AFCloudFolderCloudServiceImpl {
inner: server,
user: self.user.clone(),
})
} }
fn database_service(&self) -> Arc<dyn DatabaseCloudService> { fn database_service(&self) -> Arc<dyn DatabaseCloudService> {
let server = AFServerImpl { let server = AFServerImpl {
client: self.get_client(), client: self.get_client(),
}; };
Arc::new(AFCloudDatabaseCloudServiceImpl(server)) Arc::new(AFCloudDatabaseCloudServiceImpl {
inner: server,
user: self.user.clone(),
})
} }
fn document_service(&self) -> Arc<dyn DocumentCloudService> { fn document_service(&self) -> Arc<dyn DocumentCloudService> {
let server = AFServerImpl { let server = AFServerImpl {
client: self.get_client(), client: self.get_client(),
}; };
Arc::new(AFCloudDocumentCloudServiceImpl(server)) Arc::new(AFCloudDocumentCloudServiceImpl {
inner: server,
user: self.user.clone(),
})
} }
fn subscribe_ws_state(&self) -> Option<WSConnectStateReceiver> { fn subscribe_ws_state(&self) -> Option<WSConnectStateReceiver> {

View File

@ -109,7 +109,7 @@ where
&workspace_id, &workspace_id,
vec![], vec![],
)?; )?;
Ok(folder.get_folder_data()) Ok(folder.get_folder_data(&workspace_id))
}) })
} }

View File

@ -2,8 +2,10 @@ use client_api::ClientConfiguration;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use flowy_error::FlowyResult;
use uuid::Uuid; use uuid::Uuid;
use flowy_server::af_cloud::define::ServerUser;
use flowy_server::af_cloud::AppFlowyCloudServer; use flowy_server::af_cloud::AppFlowyCloudServer;
use flowy_server::supabase::define::{USER_DEVICE_ID, USER_SIGN_IN_URL}; use flowy_server::supabase::define::{USER_DEVICE_ID, USER_SIGN_IN_URL};
use flowy_server_pub::af_cloud_config::AFCloudConfiguration; use flowy_server_pub::af_cloud_config::AFCloudConfiguration;
@ -31,9 +33,17 @@ pub fn af_cloud_server(config: AFCloudConfiguration) -> Arc<AppFlowyCloudServer>
true, true,
fake_device_id, fake_device_id,
"0.5.1", "0.5.1",
Arc::new(FakeServerUserImpl),
)) ))
} }
struct FakeServerUserImpl;
impl ServerUser for FakeServerUserImpl {
fn workspace_id(&self) -> FlowyResult<String> {
todo!()
}
}
pub async fn generate_sign_in_url(user_email: &str, config: &AFCloudConfiguration) -> String { pub async fn generate_sign_in_url(user_email: &str, config: &AFCloudConfiguration) -> String {
let client = client_api::Client::new( let client = client_api::Client::new(
&config.base_url, &config.base_url,

View File

@ -134,7 +134,7 @@ pub async fn print_encryption_folder_snapshot(
)); ));
let folder_data = Folder::open(uid, collab, None) let folder_data = Folder::open(uid, collab, None)
.unwrap() .unwrap()
.get_folder_data() .get_folder_data(folder_id)
.unwrap(); .unwrap();
let json = serde_json::to_value(folder_data).unwrap(); let json = serde_json::to_value(folder_data).unwrap();
println!("{}", serde_json::to_string_pretty(&json).unwrap()); println!("{}", serde_json::to_string_pretty(&json).unwrap());

View File

@ -226,8 +226,9 @@ where
None, None,
) )
.map_err(|err| PersistenceError::InvalidData(err.to_string()))?; .map_err(|err| PersistenceError::InvalidData(err.to_string()))?;
let mut folder_data = old_folder let mut folder_data =
.get_folder_data() old_folder
.get_folder_data(old_workspace_id)
.ok_or(PersistenceError::Internal(anyhow!( .ok_or(PersistenceError::Internal(anyhow!(
"Can't migrate the folder data" "Can't migrate the folder data"
)))?; )))?;

View File

@ -49,7 +49,7 @@ pub async fn sync_supabase_user_data_to_cloud(
) )
.await; .await;
let views = folder.lock().get_current_workspace_views(); let views = folder.lock().get_views_belong_to(&workspace_id);
for view in views { for view in views {
let view_id = view.id.clone(); let view_id = view.id.clone();
if let Err(err) = sync_view( if let Err(err) = sync_view(

View File

@ -53,8 +53,8 @@ impl UserDataMigration for HistoricalEmptyDocumentMigration {
let folder = Folder::open(session.user_id, folder_collab, None) let folder = Folder::open(session.user_id, folder_collab, None)
.map_err(|err| PersistenceError::Internal(err.into()))?; .map_err(|err| PersistenceError::Internal(err.into()))?;
let migration_views = folder.get_workspace_views(); if let Ok(workspace_id) = folder.try_get_workspace_id() {
let migration_views = folder.views.get_views_belong_to(&workspace_id);
// For historical reasons, the first level documents are empty. So migrate them by inserting // For historical reasons, the first level documents are empty. So migrate them by inserting
// the default document data. // the default document data.
for view in migration_views { for view in migration_views {
@ -66,6 +66,7 @@ impl UserDataMigration for HistoricalEmptyDocumentMigration {
); );
} }
} }
}
Ok(()) Ok(())
})?; })?;

View File

@ -16,7 +16,7 @@ use tracing::{error, info};
const SQLITE_VACUUM_042: &str = "sqlite_vacuum_042_version"; const SQLITE_VACUUM_042: &str = "sqlite_vacuum_042_version";
pub struct AuthenticateUser { pub struct AuthenticateUser {
pub(crate) user_config: UserConfig, pub user_config: UserConfig,
pub(crate) database: Arc<UserDB>, pub(crate) database: Arc<UserDB>,
pub(crate) user_paths: UserPaths, pub(crate) user_paths: UserPaths,
store_preferences: Arc<StorePreferences>, store_preferences: Arc<StorePreferences>,
@ -67,6 +67,11 @@ impl AuthenticateUser {
Ok(session.user_workspace.id) Ok(session.user_workspace.id)
} }
pub fn workspace_database_object_id(&self) -> FlowyResult<String> {
let session = self.get_session()?;
Ok(session.user_workspace.workspace_database_object_id.clone())
}
pub fn get_collab_db(&self, uid: i64) -> FlowyResult<Weak<CollabKVDB>> { pub fn get_collab_db(&self, uid: i64) -> FlowyResult<Weak<CollabKVDB>> {
self self
.database .database

View File

@ -509,7 +509,7 @@ where
.map_err(|err| PersistenceError::InvalidData(err.to_string()))?; .map_err(|err| PersistenceError::InvalidData(err.to_string()))?;
let other_folder_data = other_folder let other_folder_data = other_folder
.get_folder_data() .get_folder_data(&other_session.user_workspace.id)
.ok_or(PersistenceError::Internal(anyhow!( .ok_or(PersistenceError::Internal(anyhow!(
"Can't read the folder data" "Can't read the folder data"
)))?; )))?;

View File

@ -518,7 +518,6 @@ impl UserManager {
pub async fn prepare_user(&self, session: &Session) { pub async fn prepare_user(&self, session: &Session) {
let _ = self.authenticate_user.database.close(session.user_id); let _ = self.authenticate_user.database.close(session.user_id);
self.prepare_collab(session);
} }
pub async fn prepare_backup(&self, session: &Session) { pub async fn prepare_backup(&self, session: &Session) {
@ -724,11 +723,6 @@ impl UserManager {
Ok(()) Ok(())
} }
fn prepare_collab(&self, session: &Session) {
let collab_builder = self.collab_builder.upgrade().unwrap();
collab_builder.initialize(session.user_workspace.id.clone());
}
async fn handler_user_update(&self, user_update: UserUpdate) -> FlowyResult<()> { async fn handler_user_update(&self, user_update: UserUpdate) -> FlowyResult<()> {
let session = self.get_session()?; let session = self.get_session()?;
if session.user_id == user_update.uid { if session.user_id == user_update.uid {

View File

@ -136,6 +136,7 @@ impl UserManager {
let weak_builder = self.collab_builder.clone(); let weak_builder = self.collab_builder.clone();
let cloned_is_loading = self.is_loading_awareness.clone(); let cloned_is_loading = self.is_loading_awareness.clone();
let session = session.clone(); let session = session.clone();
let workspace_id = session.user_workspace.id.clone();
tokio::spawn(async move { tokio::spawn(async move {
if cloned_is_loading.load(Ordering::SeqCst) { if cloned_is_loading.load(Ordering::SeqCst) {
return Ok(()); return Ok(());
@ -160,6 +161,7 @@ impl UserManager {
Ok(data) => { Ok(data) => {
trace!("Get user awareness collab from remote: {}", data.len()); trace!("Get user awareness collab from remote: {}", data.len());
let collab = Self::collab_for_user_awareness( let collab = Self::collab_for_user_awareness(
&workspace_id,
&weak_builder, &weak_builder,
session.user_id, session.user_id,
&object_id, &object_id,
@ -173,6 +175,7 @@ impl UserManager {
if err.is_record_not_found() { if err.is_record_not_found() {
info!("User awareness not found, creating new"); info!("User awareness not found, creating new");
let collab = Self::collab_for_user_awareness( let collab = Self::collab_for_user_awareness(
&workspace_id,
&weak_builder, &weak_builder,
session.user_id, session.user_id,
&object_id, &object_id,
@ -206,6 +209,7 @@ impl UserManager {
/// using a collaboration builder. This instance is specifically geared towards handling /// using a collaboration builder. This instance is specifically geared towards handling
/// user awareness. /// user awareness.
async fn collab_for_user_awareness( async fn collab_for_user_awareness(
workspace_id: &str,
collab_builder: &Weak<AppFlowyCollabBuilder>, collab_builder: &Weak<AppFlowyCollabBuilder>,
uid: i64, uid: i64,
object_id: &str, object_id: &str,
@ -218,6 +222,7 @@ impl UserManager {
))?; ))?;
let collab = collab_builder let collab = collab_builder
.build( .build(
workspace_id,
uid, uid,
object_id, object_id,
CollabType::UserAwareness, CollabType::UserAwareness,

View File

@ -136,7 +136,6 @@ impl UserManager {
#[instrument(skip(self), err)] #[instrument(skip(self), err)]
pub async fn open_workspace(&self, workspace_id: &str) -> FlowyResult<()> { pub async fn open_workspace(&self, workspace_id: &str) -> FlowyResult<()> {
info!("open workspace: {}", workspace_id); info!("open workspace: {}", workspace_id);
let uid = self.user_id()?;
let user_workspace = self let user_workspace = self
.cloud_services .cloud_services
.get_user_service()? .get_user_service()?
@ -146,6 +145,8 @@ impl UserManager {
self self
.authenticate_user .authenticate_user
.set_user_workspace(user_workspace.clone())?; .set_user_workspace(user_workspace.clone())?;
let uid = self.user_id()?;
if let Err(err) = self if let Err(err) = self
.user_status_callback .user_status_callback
.read() .read()