mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: integrate appflowy-cloud (#3359)
* feat: draft: code dependency * chore: update ref * feat: signup using client_api * feat: support auto sign_in after sign_up if already confirmed(WIP) * chore: update collab commit id * chore: fix compile errors * chore: user AFServer trait to provide optional service * chore: refactor workspace * chore: disable aws config * chore: return ws connect * chore: update collab rev * chore: fmt and clippy * chore: fix test * chore: update chrono version * chore: add script to update the collab crates commit id * chore: update --------- Co-authored-by: nathan <nathan@appflowy.io>
This commit is contained in:
parent
cecd4f48ab
commit
1c84ee1d53
2
.gitignore
vendored
2
.gitignore
vendored
@ -41,3 +41,5 @@ pubspec.lock
|
||||
# ignore the deb filegit
|
||||
frontend/package
|
||||
frontend/*.deb
|
||||
|
||||
**/Cargo.toml.bak
|
||||
|
491
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
491
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
@ -70,6 +70,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"getrandom 0.2.10",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
@ -107,6 +108,12 @@ dependencies = [
|
||||
"alloc-no-stdlib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
@ -137,33 +144,12 @@ version = "1.0.75"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
||||
|
||||
[[package]]
|
||||
name = "appflowy-integrate"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9bb9a7#9bb9a7e33c17677ec6f553fb8b98f66d6c9b6c2e"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collab",
|
||||
"collab-database",
|
||||
"collab-define",
|
||||
"collab-document",
|
||||
"collab-folder",
|
||||
"collab-persistence",
|
||||
"collab-plugins",
|
||||
"futures",
|
||||
"parking_lot",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "appflowy_tauri"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"flowy-core",
|
||||
"flowy-net",
|
||||
"flowy-notification",
|
||||
"lib-dispatch",
|
||||
"serde",
|
||||
@ -243,6 +229,15 @@ dependencies = [
|
||||
"system-deps 6.1.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atoi"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atomic_refcell"
|
||||
version = "0.1.10"
|
||||
@ -367,7 +362,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b"
|
||||
dependencies = [
|
||||
"borsh-derive",
|
||||
"hashbrown 0.13.2",
|
||||
"hashbrown 0.12.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -433,6 +428,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a246e68bb43f6cd9db24bea052a53e40405417c5fb372e3d1a8a7f770a564ef5"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"once_cell",
|
||||
"regex-automata",
|
||||
"serde",
|
||||
]
|
||||
|
||||
@ -592,18 +589,17 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.26"
|
||||
version = "0.4.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5"
|
||||
checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"time 0.1.45",
|
||||
"wasm-bindgen",
|
||||
"winapi",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -671,6 +667,32 @@ dependencies = [
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "client-api"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=8f8f6a#8f8f6af0f9fa1229d43e0dcbc54c62cc41ccaa3b"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
"collab-sync-protocol",
|
||||
"futures-util",
|
||||
"gotrue-entity",
|
||||
"opener",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"shared_entity",
|
||||
"storage-entity",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-retry",
|
||||
"tokio-stream",
|
||||
"tokio-tungstenite",
|
||||
"tracing",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cmd_lib"
|
||||
version = "1.3.0"
|
||||
@ -730,7 +752,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9bb9a7#9bb9a7e33c17677ec6f553fb8b98f66d6c9b6c2e"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=20eff3#20eff314f7963e30bdbe85c860060269b12197ef"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -749,7 +771,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-database"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9bb9a7#9bb9a7e33c17677ec6f553fb8b98f66d6c9b6c2e"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=20eff3#20eff314f7963e30bdbe85c860060269b12197ef"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -779,7 +801,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-define"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9bb9a7#9bb9a7e33c17677ec6f553fb8b98f66d6c9b6c2e"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=20eff3#20eff314f7963e30bdbe85c860060269b12197ef"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collab",
|
||||
@ -791,7 +813,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-derive"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9bb9a7#9bb9a7e33c17677ec6f553fb8b98f66d6c9b6c2e"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=20eff3#20eff314f7963e30bdbe85c860060269b12197ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -803,7 +825,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-document"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9bb9a7#9bb9a7e33c17677ec6f553fb8b98f66d6c9b6c2e"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=20eff3#20eff314f7963e30bdbe85c860060269b12197ef"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collab",
|
||||
@ -823,7 +845,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-folder"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9bb9a7#9bb9a7e33c17677ec6f553fb8b98f66d6c9b6c2e"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=20eff3#20eff314f7963e30bdbe85c860060269b12197ef"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@ -840,10 +862,30 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "collab-integrate"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"collab",
|
||||
"collab-database",
|
||||
"collab-define",
|
||||
"collab-document",
|
||||
"collab-folder",
|
||||
"collab-persistence",
|
||||
"collab-plugins",
|
||||
"futures",
|
||||
"parking_lot",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "collab-persistence"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9bb9a7#9bb9a7e33c17677ec6f553fb8b98f66d6c9b6c2e"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=20eff3#20eff314f7963e30bdbe85c860060269b12197ef"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bincode",
|
||||
@ -864,7 +906,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-plugins"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9bb9a7#9bb9a7e33c17677ec6f553fb8b98f66d6c9b6c2e"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=20eff3#20eff314f7963e30bdbe85c860060269b12197ef"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -872,7 +914,6 @@ dependencies = [
|
||||
"collab-define",
|
||||
"collab-persistence",
|
||||
"collab-sync-protocol",
|
||||
"collab-ws",
|
||||
"futures-util",
|
||||
"lib0",
|
||||
"parking_lot",
|
||||
@ -893,10 +934,11 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-sync-protocol"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9bb9a7#9bb9a7e33c17677ec6f553fb8b98f66d6c9b6c2e"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=20eff3#20eff314f7963e30bdbe85c860060269b12197ef"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"collab",
|
||||
"collab-define",
|
||||
"md5",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -907,7 +949,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-user"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9bb9a7#9bb9a7e33c17677ec6f553fb8b98f66d6c9b6c2e"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=20eff3#20eff314f7963e30bdbe85c860060269b12197ef"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collab",
|
||||
@ -920,24 +962,6 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "collab-ws"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9bb9a7#9bb9a7e33c17677ec6f553fb8b98f66d6c9b6c2e"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"collab-sync-protocol",
|
||||
"futures-util",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-retry",
|
||||
"tokio-stream",
|
||||
"tokio-tungstenite",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "color_quant"
|
||||
version = "1.1.0"
|
||||
@ -1036,6 +1060,21 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc"
|
||||
version = "3.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe"
|
||||
dependencies = [
|
||||
"crc-catalog",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc-catalog"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.3.2"
|
||||
@ -1079,6 +1118,16 @@ dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-queue"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.16"
|
||||
@ -1315,6 +1364,12 @@ version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
|
||||
|
||||
[[package]]
|
||||
name = "dotenvy"
|
||||
version = "0.15.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
|
||||
|
||||
[[package]]
|
||||
name = "dtoa"
|
||||
version = "1.0.6"
|
||||
@ -1347,6 +1402,9 @@ name = "either"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embed-resource"
|
||||
@ -1418,6 +1476,12 @@ dependencies = [
|
||||
"backtrace",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "2.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
|
||||
|
||||
[[package]]
|
||||
name = "faccess"
|
||||
version = "0.2.4"
|
||||
@ -1522,7 +1586,7 @@ dependencies = [
|
||||
"console",
|
||||
"fancy-regex 0.10.0",
|
||||
"flowy-ast",
|
||||
"itertools",
|
||||
"itertools 0.10.5",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"phf 0.8.0",
|
||||
@ -1556,9 +1620,12 @@ dependencies = [
|
||||
name = "flowy-core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"appflowy-integrate",
|
||||
"anyhow",
|
||||
"bytes",
|
||||
"collab",
|
||||
"collab-define",
|
||||
"collab-integrate",
|
||||
"collab-plugins",
|
||||
"diesel",
|
||||
"flowy-config",
|
||||
"flowy-database-deps",
|
||||
@ -1568,7 +1635,6 @@ dependencies = [
|
||||
"flowy-error",
|
||||
"flowy-folder-deps",
|
||||
"flowy-folder2",
|
||||
"flowy-net",
|
||||
"flowy-server",
|
||||
"flowy-server-config",
|
||||
"flowy-sqlite",
|
||||
@ -1604,7 +1670,6 @@ name = "flowy-database2"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"appflowy-integrate",
|
||||
"async-stream",
|
||||
"async-trait",
|
||||
"bytes",
|
||||
@ -1613,6 +1678,7 @@ dependencies = [
|
||||
"collab",
|
||||
"collab-database",
|
||||
"collab-define",
|
||||
"collab-integrate",
|
||||
"csv",
|
||||
"dashmap",
|
||||
"fancy-regex 0.10.0",
|
||||
@ -1673,11 +1739,11 @@ name = "flowy-document2"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"appflowy-integrate",
|
||||
"bytes",
|
||||
"collab",
|
||||
"collab-define",
|
||||
"collab-document",
|
||||
"collab-integrate",
|
||||
"flowy-codegen",
|
||||
"flowy-derive",
|
||||
"flowy-document-deps",
|
||||
@ -1719,6 +1785,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
"client-api",
|
||||
"collab-database",
|
||||
"collab-document",
|
||||
"flowy-codegen",
|
||||
@ -1751,12 +1818,12 @@ dependencies = [
|
||||
name = "flowy-folder2"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"appflowy-integrate",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"collab",
|
||||
"collab-define",
|
||||
"collab-folder",
|
||||
"collab-integrate",
|
||||
"flowy-codegen",
|
||||
"flowy-derive",
|
||||
"flowy-error",
|
||||
@ -1776,17 +1843,6 @@ dependencies = [
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flowy-net"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"flowy-codegen",
|
||||
"lib-dispatch",
|
||||
"protobuf",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flowy-notification"
|
||||
version = "0.1.0"
|
||||
@ -1808,6 +1864,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"client-api",
|
||||
"collab",
|
||||
"collab-define",
|
||||
"collab-document",
|
||||
@ -1901,7 +1958,6 @@ name = "flowy-user"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"appflowy-integrate",
|
||||
"base64 0.21.2",
|
||||
"bytes",
|
||||
"chrono",
|
||||
@ -1910,6 +1966,7 @@ dependencies = [
|
||||
"collab-define",
|
||||
"collab-document",
|
||||
"collab-folder",
|
||||
"collab-integrate",
|
||||
"collab-user",
|
||||
"diesel",
|
||||
"diesel_derives",
|
||||
@ -2045,6 +2102,17 @@ dependencies = [
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-intrusive"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"lock_api",
|
||||
"parking_lot",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.28"
|
||||
@ -2376,6 +2444,15 @@ dependencies = [
|
||||
"system-deps 6.1.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gotrue-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=8f8f6a#8f8f6af0f9fa1229d43e0dcbc54c62cc41ccaa3b"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gtk"
|
||||
version = "0.15.5"
|
||||
@ -2473,6 +2550,19 @@ name = "hashbrown"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
|
||||
dependencies = [
|
||||
"ahash 0.8.3",
|
||||
"allocator-api2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashlink"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7"
|
||||
dependencies = [
|
||||
"hashbrown 0.14.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
@ -2488,6 +2578,9 @@ name = "heck"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
@ -2681,6 +2774,12 @@ dependencies = [
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "if_chain"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed"
|
||||
|
||||
[[package]]
|
||||
name = "ignore"
|
||||
version = "0.4.20"
|
||||
@ -2785,6 +2884,15 @@ dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.8"
|
||||
@ -3338,6 +3446,15 @@ dependencies = [
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "normpath"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec60c60a693226186f5d6edf073232bfb6464ed97eb22cf3b01c1e8198fd97f5"
|
||||
dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.46.0"
|
||||
@ -3468,6 +3585,17 @@ dependencies = [
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "opener"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c62dcb6174f9cb326eac248f07e955d5d559c272730b6c03e396b443b562788"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"normpath",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.55"
|
||||
@ -3595,6 +3723,12 @@ dependencies = [
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
|
||||
|
||||
[[package]]
|
||||
name = "pathdiff"
|
||||
version = "0.2.1"
|
||||
@ -3863,7 +3997,7 @@ dependencies = [
|
||||
"line-wrap",
|
||||
"quick-xml",
|
||||
"serde",
|
||||
"time 0.3.22",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4665,9 +4799,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.164"
|
||||
version = "1.0.188"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d"
|
||||
checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
@ -4685,9 +4819,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.164"
|
||||
version = "1.0.188"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68"
|
||||
checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -4696,9 +4830,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.99"
|
||||
version = "1.0.107"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3"
|
||||
checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65"
|
||||
dependencies = [
|
||||
"itoa 1.0.6",
|
||||
"ryu",
|
||||
@ -4707,9 +4841,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_repr"
|
||||
version = "0.1.12"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab"
|
||||
checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -4750,7 +4884,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with_macros",
|
||||
"time 0.3.22",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4834,6 +4968,21 @@ dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shared_entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=8f8f6a#8f8f6af0f9fa1229d43e0dcbc54c62cc41ccaa3b"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"opener",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"thiserror",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.1.0"
|
||||
@ -4966,6 +5115,99 @@ version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "sqlformat"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b7b278788e7be4d0d29c0f39497a0eef3fba6bbc8e70d8bf7fde46edeaa9e85"
|
||||
dependencies = [
|
||||
"itertools 0.11.0",
|
||||
"nom 7.1.3",
|
||||
"unicode_categories",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e58421b6bc416714d5115a2ca953718f6c621a51b68e4f4922aea5a4391a721"
|
||||
dependencies = [
|
||||
"sqlx-core",
|
||||
"sqlx-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-core"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd4cef4251aabbae751a3710927945901ee1d97ee96d757f6880ebb9a79bfd53"
|
||||
dependencies = [
|
||||
"ahash 0.8.3",
|
||||
"atoi",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"crc",
|
||||
"crossbeam-queue",
|
||||
"dotenvy",
|
||||
"either",
|
||||
"event-listener",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-intrusive",
|
||||
"futures-io",
|
||||
"futures-util",
|
||||
"hashlink",
|
||||
"hex",
|
||||
"indexmap 2.0.0",
|
||||
"log",
|
||||
"memchr",
|
||||
"once_cell",
|
||||
"paste",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
"sha2",
|
||||
"smallvec",
|
||||
"sqlformat",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-macros"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "208e3165167afd7f3881b16c1ef3f2af69fa75980897aac8874a0696516d12c2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"sqlx-core",
|
||||
"sqlx-macros-core",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-macros-core"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a4a8336d278c62231d87f24e8a7a74898156e34c1c18942857be2acb29c7dfc"
|
||||
dependencies = [
|
||||
"dotenvy",
|
||||
"either",
|
||||
"heck 0.4.1",
|
||||
"hex",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"sqlx-core",
|
||||
"syn 1.0.109",
|
||||
"tempfile",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
@ -4987,6 +5229,20 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "storage-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=8f8f6a#8f8f6af0f9fa1229d43e0dcbc54c62cc41ccaa3b"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"collab-define",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"uuid",
|
||||
"validator",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "string_cache"
|
||||
version = "0.8.7"
|
||||
@ -5280,7 +5536,7 @@ dependencies = [
|
||||
"sha2",
|
||||
"tauri-utils",
|
||||
"thiserror",
|
||||
"time 0.3.22",
|
||||
"time",
|
||||
"uuid",
|
||||
"walkdir",
|
||||
]
|
||||
@ -5445,18 +5701,18 @@ checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c"
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.40"
|
||||
version = "1.0.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
|
||||
checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.40"
|
||||
version = "1.0.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
|
||||
checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -5483,17 +5739,6 @@ dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi 0.10.0+wasi-snapshot-preview1",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.22"
|
||||
@ -5978,6 +6223,12 @@ version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode_categories"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
|
||||
|
||||
[[package]]
|
||||
name = "universal-hash"
|
||||
version = "0.5.1"
|
||||
@ -5996,9 +6247,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.4.0"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb"
|
||||
checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
@ -6014,11 +6265,12 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.3.4"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa2982af2eec27de306107c027578ff7f423d65f7250e40ce0fea8f45248b81"
|
||||
checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d"
|
||||
dependencies = [
|
||||
"getrandom 0.2.10",
|
||||
"serde",
|
||||
"sha1_smol",
|
||||
]
|
||||
|
||||
@ -6035,6 +6287,33 @@ dependencies = [
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"url",
|
||||
"validator_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "validator_derive"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc44ca3088bb3ba384d9aecf40c6a23a676ce23e09bdaca2073d99c207f864af"
|
||||
dependencies = [
|
||||
"if_chain",
|
||||
"lazy_static",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"syn 1.0.109",
|
||||
"validator_types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "validator_types"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "111abfe30072511849c5910134e8baf8dc05de4c0e5903d681cbd5c9c4d611e3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -6112,12 +6391,6 @@ version = "0.9.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
|
@ -23,7 +23,6 @@ tracing = { version = "0.1", features = ["log"] }
|
||||
lib-dispatch = { path = "../../rust-lib/lib-dispatch", features = ["use_serde"] }
|
||||
flowy-core = { path = "../../rust-lib/flowy-core", features = ["rev-sqlite", "ts"] }
|
||||
flowy-notification = { path = "../../rust-lib/flowy-notification", features = ["ts"] }
|
||||
flowy-net = { path = "../../rust-lib/flowy-net" }
|
||||
|
||||
[features]
|
||||
# by default Tauri runs in production mode
|
||||
@ -34,24 +33,33 @@ default = ["custom-protocol"]
|
||||
custom-protocol = ["tauri/custom-protocol"]
|
||||
|
||||
[patch.crates-io]
|
||||
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9bb9a7" }
|
||||
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9bb9a7" }
|
||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9bb9a7" }
|
||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9bb9a7" }
|
||||
appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9bb9a7" }
|
||||
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9bb9a7" }
|
||||
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9bb9a7" }
|
||||
collab-define = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9bb9a7" }
|
||||
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9bb9a7" }
|
||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "8f8f6a" }
|
||||
|
||||
# ⚠️⚠️⚠️
|
||||
# Please using the following command to update the revision id
|
||||
# Current directory: frontend
|
||||
# Run the script:
|
||||
# scripts/tool/update_collab_rev.sh new_rev_id
|
||||
# ⚠️⚠️⚠️️
|
||||
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "20eff3" }
|
||||
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "20eff3" }
|
||||
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "20eff3" }
|
||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "20eff3" }
|
||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "20eff3" }
|
||||
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "20eff3" }
|
||||
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "20eff3" }
|
||||
collab-define = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "20eff3" }
|
||||
collab-sync-protocol = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "20eff3" }
|
||||
|
||||
#collab = { path = "../../../../AppFlowy-Collab/collab" }
|
||||
#collab-folder = { path = "../../../../AppFlowy-Collab/collab-folder" }
|
||||
#collab-document = { path = "../../../../AppFlowy-Collab/collab-document" }
|
||||
#collab-database = { path = "../../../../AppFlowy-Collab/collab-database" }
|
||||
#appflowy-integrate = { path = "../../../../AppFlowy-Collab/appflowy-integrate" }
|
||||
#collab-plugins = { path = "../../../../AppFlowy-Collab/collab-plugins" }
|
||||
#collab-persistence = { path = "../../../../AppFlowy-Collab/collab-persistence" }
|
||||
#collab-user = { path = "../../../../AppFlowy-Collab/collab-user" }
|
||||
#collab-define = { path = "../../../../AppFlowy-Collab/collab-define" }
|
||||
#collab-sync-protocol = { path = "../../../../AppFlowy-Collab/collab-sync-protocol" }
|
||||
|
||||
|
||||
|
||||
|
1661
frontend/rust-lib/Cargo.lock
generated
1661
frontend/rust-lib/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,6 @@
|
||||
members = [
|
||||
"lib-dispatch",
|
||||
"lib-log",
|
||||
"flowy-net",
|
||||
"flowy-core",
|
||||
"dart-ffi",
|
||||
"flowy-user",
|
||||
@ -23,8 +22,33 @@ members = [
|
||||
"flowy-config",
|
||||
"flowy-encrypt",
|
||||
"flowy-storage",
|
||||
"collab-integrate",
|
||||
]
|
||||
|
||||
[workspace.dependencies]
|
||||
lib-dispatch = { workspace = true, path = "lib-dispatch" }
|
||||
lib-log = { workspace = true, path = "lib-log" }
|
||||
flowy-core = { workspace = true, path = "flowy-core" }
|
||||
dart-ffi = { workspace = true, path = "dart-ffi" }
|
||||
flowy-user = { workspace = true, path = "flowy-user" }
|
||||
flowy-user-deps = { workspace = true, path = "flowy-user-deps" }
|
||||
flowy-sqlite = { workspace = true, path = "flowy-sqlite" }
|
||||
flowy-folder2 = { workspace = true, path = "flowy-folder2" }
|
||||
flowy-folder-deps = { workspace = true, path = "flowy-folder-deps" }
|
||||
flowy-notification = { workspace = true, path = "flowy-notification" }
|
||||
flowy-document2 = { workspace = true, path = "flowy-document2" }
|
||||
flowy-document-deps = { workspace = true, path = "flowy-document-deps" }
|
||||
flowy-error = { workspace = true, path = "flowy-error" }
|
||||
flowy-database2 = { workspace = true, path = "flowy-database2" }
|
||||
flowy-database-deps = { workspace = true, path = "flowy-database-deps" }
|
||||
flowy-task = { workspace = true, path = "flowy-task" }
|
||||
flowy-server = { workspace = true, path = "flowy-server" }
|
||||
flowy-server-config = { workspace = true, path = "flowy-server-config" }
|
||||
flowy-config = { workspace = true, path = "flowy-config" }
|
||||
flowy-encrypt = { workspace = true, path = "flowy-encrypt" }
|
||||
flowy-storage = { workspace = true, path = "flowy-storage" }
|
||||
collab-integrate = { workspace = true, path = "collab-integrate" }
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 0
|
||||
lto = false
|
||||
@ -49,20 +73,30 @@ lto = false
|
||||
incremental = false
|
||||
|
||||
[patch.crates-io]
|
||||
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9bb9a7" }
|
||||
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9bb9a7" }
|
||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9bb9a7" }
|
||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9bb9a7" }
|
||||
appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9bb9a7" }
|
||||
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9bb9a7" }
|
||||
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9bb9a7" }
|
||||
collab-define = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9bb9a7" }
|
||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "8f8f6a" }
|
||||
|
||||
# ⚠️⚠️⚠️
|
||||
# Please using the following command to update the revision id
|
||||
# Current directory: frontend
|
||||
# Run the script:
|
||||
# scripts/tool/update_collab_rev.sh new_rev_id
|
||||
# ⚠️⚠️⚠️️
|
||||
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "20eff3" }
|
||||
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "20eff3" }
|
||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "20eff3" }
|
||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "20eff3" }
|
||||
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "20eff3" }
|
||||
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "20eff3" }
|
||||
collab-define = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "20eff3" }
|
||||
collab-sync-protocol = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "20eff3" }
|
||||
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "20eff3" }
|
||||
|
||||
#collab = { path = "../AppFlowy-Collab/collab" }
|
||||
#collab-folder = { path = "../AppFlowy-Collab/collab-folder" }
|
||||
#collab-database= { path = "../AppFlowy-Collab/collab-database" }
|
||||
#collab-document = { path = "../AppFlowy-Collab/collab-document" }
|
||||
#collab-plugins = { path = "../AppFlowy-Collab/collab-plugins" }
|
||||
#appflowy-integrate = { path = "../AppFlowy-Collab/appflowy-integrate" }
|
||||
#collab-persistence = { path = "../AppFlowy-Collab/collab-persistence" }
|
||||
#collab-user = { path = "../AppFlowy-Collab/collab-user" }
|
||||
#collab-define = { path = "../AppFlowy-Collab/collab-define" }
|
||||
#collab-sync-protocol = { path = "../AppFlowy-Collab/collab-sync-protocol" }
|
||||
|
28
frontend/rust-lib/collab-integrate/Cargo.toml
Normal file
28
frontend/rust-lib/collab-integrate/Cargo.toml
Normal file
@ -0,0 +1,28 @@
|
||||
[package]
|
||||
name = "collab-integrate"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
collab = { version = "0.1.0" }
|
||||
collab-persistence = { version = "0.1.0", features = ["rocksdb_persistence"] }
|
||||
collab-folder = { version = "0.1.0" }
|
||||
collab-database = { version = "0.1.0" }
|
||||
collab-plugins = { version = "0.1.0" }
|
||||
collab-document = { version = "0.1.0" }
|
||||
collab-define = { version = "0.1.0" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
anyhow = "1.0"
|
||||
tracing = "0.1"
|
||||
parking_lot = "0.12.1"
|
||||
futures = "0.3"
|
||||
async-trait = "0.1.73"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
supabase_integrate = ["collab-plugins/postgres_storage_plugin", "collab-plugins/rocksdb_plugin"]
|
||||
appflowy_cloud_integrate = ["collab-plugins/sync_plugin", "collab-plugins/rocksdb_plugin"]
|
||||
snapshot_plugin = ["collab-plugins/snapshot_plugin"]
|
261
frontend/rust-lib/collab-integrate/src/collab_builder.rs
Normal file
261
frontend/rust-lib/collab-integrate/src/collab_builder.rs
Normal file
@ -0,0 +1,261 @@
|
||||
use std::fmt::Debug;
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
use anyhow::Error;
|
||||
use async_trait::async_trait;
|
||||
use collab::core::collab::{CollabRawData, MutexCollab};
|
||||
use collab::preclude::{CollabBuilder, CollabPlugin};
|
||||
use collab_define::{CollabObject, CollabType};
|
||||
use collab_persistence::kv::rocks_kv::RocksCollabDB;
|
||||
use collab_plugins::cloud_storage::network_state::{CollabNetworkReachability, CollabNetworkState};
|
||||
use collab_plugins::local_storage::rocksdb::RocksdbDiskPlugin;
|
||||
use collab_plugins::local_storage::CollabPersistenceConfig;
|
||||
use collab_plugins::snapshot::{CollabSnapshotPlugin, SnapshotPersistence};
|
||||
use futures::executor::block_on;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum CollabSource {
|
||||
Local,
|
||||
AFCloud,
|
||||
Supabase,
|
||||
}
|
||||
|
||||
pub enum CollabPluginContext {
|
||||
Local,
|
||||
AppFlowyCloud {
|
||||
uid: i64,
|
||||
collab_object: CollabObject,
|
||||
local_collab: Weak<MutexCollab>,
|
||||
},
|
||||
Supabase {
|
||||
uid: i64,
|
||||
collab_object: CollabObject,
|
||||
local_collab: Weak<MutexCollab>,
|
||||
local_collab_db: Weak<RocksCollabDB>,
|
||||
},
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait CollabStorageProvider: Send + Sync + 'static {
|
||||
fn storage_source(&self) -> CollabSource;
|
||||
|
||||
async fn get_plugins(
|
||||
&self,
|
||||
context: CollabPluginContext,
|
||||
) -> Vec<Arc<dyn collab::core::collab_plugin::CollabPlugin>>;
|
||||
|
||||
fn is_sync_enabled(&self) -> bool;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T> CollabStorageProvider for Arc<T>
|
||||
where
|
||||
T: CollabStorageProvider,
|
||||
{
|
||||
fn storage_source(&self) -> CollabSource {
|
||||
(**self).storage_source()
|
||||
}
|
||||
|
||||
async fn get_plugins(&self, context: CollabPluginContext) -> Vec<Arc<dyn CollabPlugin>> {
|
||||
(**self).get_plugins(context).await
|
||||
}
|
||||
|
||||
fn is_sync_enabled(&self) -> bool {
|
||||
(**self).is_sync_enabled()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AppFlowyCollabBuilder {
|
||||
network_reachability: CollabNetworkReachability,
|
||||
workspace_id: RwLock<Option<String>>,
|
||||
cloud_storage: RwLock<Arc<dyn CollabStorageProvider>>,
|
||||
snapshot_persistence: Mutex<Option<Arc<dyn SnapshotPersistence>>>,
|
||||
device_id: Mutex<String>,
|
||||
}
|
||||
|
||||
impl AppFlowyCollabBuilder {
|
||||
pub fn new<T: CollabStorageProvider>(storage_provider: T) -> Self {
|
||||
Self {
|
||||
network_reachability: CollabNetworkReachability::new(),
|
||||
workspace_id: Default::default(),
|
||||
cloud_storage: RwLock::new(Arc::new(storage_provider)),
|
||||
snapshot_persistence: Default::default(),
|
||||
device_id: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_snapshot_persistence(&self, snapshot_persistence: Arc<dyn SnapshotPersistence>) {
|
||||
*self.snapshot_persistence.lock() = Some(snapshot_persistence);
|
||||
}
|
||||
|
||||
pub fn initialize(&self, workspace_id: String) {
|
||||
*self.workspace_id.write() = Some(workspace_id);
|
||||
}
|
||||
|
||||
pub fn set_sync_device(&self, device_id: String) {
|
||||
*self.device_id.lock() = device_id;
|
||||
}
|
||||
|
||||
pub fn update_network(&self, reachable: bool) {
|
||||
if reachable {
|
||||
self
|
||||
.network_reachability
|
||||
.set_state(CollabNetworkState::Connected)
|
||||
} else {
|
||||
self
|
||||
.network_reachability
|
||||
.set_state(CollabNetworkState::Disconnected)
|
||||
}
|
||||
}
|
||||
|
||||
fn collab_object(
|
||||
&self,
|
||||
uid: i64,
|
||||
object_id: &str,
|
||||
collab_type: CollabType,
|
||||
) -> Result<CollabObject, Error> {
|
||||
let workspace_id = self.workspace_id.read().clone().ok_or_else(|| {
|
||||
anyhow::anyhow!("When using supabase plugin, the workspace_id should not be empty")
|
||||
})?;
|
||||
Ok(CollabObject::new(
|
||||
uid,
|
||||
object_id.to_string(),
|
||||
collab_type,
|
||||
workspace_id,
|
||||
self.device_id.lock().clone(),
|
||||
))
|
||||
}
|
||||
|
||||
/// Creates a new collaboration builder with the default configuration.
|
||||
///
|
||||
/// This function will initiate the creation of a [MutexCollab] object if it does not already exist.
|
||||
/// To check for the existence of the object prior to creation, you should utilize a transaction
|
||||
/// returned by the [read_txn] method of the [RocksCollabDB]. Then, invoke the [is_exist] method
|
||||
/// to confirm the object's presence.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `uid`: The user ID associated with the collaboration.
|
||||
/// - `object_id`: A string reference representing the ID of the object.
|
||||
/// - `object_type`: The type of the collaboration, defined by the [CollabType] enum.
|
||||
/// - `raw_data`: The raw data of the collaboration object, defined by the [CollabRawData] type.
|
||||
/// - `collab_db`: A weak reference to the [RocksCollabDB].
|
||||
///
|
||||
pub fn build(
|
||||
&self,
|
||||
uid: i64,
|
||||
object_id: &str,
|
||||
object_type: CollabType,
|
||||
raw_data: CollabRawData,
|
||||
collab_db: Weak<RocksCollabDB>,
|
||||
) -> Result<Arc<MutexCollab>, Error> {
|
||||
self.build_with_config(
|
||||
uid,
|
||||
object_id,
|
||||
object_type,
|
||||
collab_db,
|
||||
raw_data,
|
||||
&CollabPersistenceConfig::default(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates a new collaboration builder with the custom configuration.
|
||||
///
|
||||
/// This function will initiate the creation of a [MutexCollab] object if it does not already exist.
|
||||
/// To check for the existence of the object prior to creation, you should utilize a transaction
|
||||
/// returned by the [read_txn] method of the [RocksCollabDB]. Then, invoke the [is_exist] method
|
||||
/// to confirm the object's presence.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `uid`: The user ID associated with the collaboration.
|
||||
/// - `object_id`: A string reference representing the ID of the object.
|
||||
/// - `object_type`: The type of the collaboration, defined by the [CollabType] enum.
|
||||
/// - `raw_data`: The raw data of the collaboration object, defined by the [CollabRawData] type.
|
||||
/// - `collab_db`: A weak reference to the [RocksCollabDB].
|
||||
///
|
||||
pub fn build_with_config(
|
||||
&self,
|
||||
uid: i64,
|
||||
object_id: &str,
|
||||
object_type: CollabType,
|
||||
collab_db: Weak<RocksCollabDB>,
|
||||
collab_raw_data: CollabRawData,
|
||||
config: &CollabPersistenceConfig,
|
||||
) -> Result<Arc<MutexCollab>, Error> {
|
||||
let collab = Arc::new(
|
||||
CollabBuilder::new(uid, object_id)
|
||||
.with_raw_data(collab_raw_data)
|
||||
.with_plugin(RocksdbDiskPlugin::new_with_config(
|
||||
uid,
|
||||
collab_db.clone(),
|
||||
config.clone(),
|
||||
))
|
||||
.with_device_id(self.device_id.lock().clone())
|
||||
.build()?,
|
||||
);
|
||||
{
|
||||
let cloud_storage = self.cloud_storage.read();
|
||||
let cloud_storage_type = cloud_storage.storage_source();
|
||||
let collab_object = self.collab_object(uid, object_id, object_type)?;
|
||||
match cloud_storage_type {
|
||||
CollabSource::AFCloud => {
|
||||
#[cfg(feature = "appflowy_cloud_integrate")]
|
||||
{
|
||||
//
|
||||
}
|
||||
},
|
||||
CollabSource::Supabase => {
|
||||
#[cfg(feature = "supabase_integrate")]
|
||||
{
|
||||
let local_collab = Arc::downgrade(&collab);
|
||||
let local_collab_db = collab_db.clone();
|
||||
let plugins = block_on(cloud_storage.get_plugins(CollabPluginContext::Supabase {
|
||||
uid,
|
||||
collab_object: collab_object.clone(),
|
||||
local_collab,
|
||||
local_collab_db,
|
||||
}));
|
||||
for plugin in plugins {
|
||||
collab.lock().add_plugin(plugin);
|
||||
}
|
||||
}
|
||||
},
|
||||
CollabSource::Local => {},
|
||||
}
|
||||
|
||||
if let Some(snapshot_persistence) = self.snapshot_persistence.lock().as_ref() {
|
||||
if config.enable_snapshot {
|
||||
let snapshot_plugin = CollabSnapshotPlugin::new(
|
||||
uid,
|
||||
collab_object,
|
||||
snapshot_persistence.clone(),
|
||||
collab_db,
|
||||
config.snapshot_per_update,
|
||||
);
|
||||
// tracing::trace!("add snapshot plugin: {}", object_id);
|
||||
collab.lock().add_plugin(Arc::new(snapshot_plugin));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
block_on(collab.async_initialize());
|
||||
Ok(collab)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DefaultCollabStorageProvider();
|
||||
|
||||
#[async_trait]
|
||||
impl CollabStorageProvider for DefaultCollabStorageProvider {
|
||||
fn storage_source(&self) -> CollabSource {
|
||||
CollabSource::Local
|
||||
}
|
||||
|
||||
async fn get_plugins(&self, _context: CollabPluginContext) -> Vec<Arc<dyn CollabPlugin>> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn is_sync_enabled(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
70
frontend/rust-lib/collab-integrate/src/config.rs
Normal file
70
frontend/rust-lib/collab-integrate/src/config.rs
Normal file
@ -0,0 +1,70 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub enum CollabDBPluginProvider {
|
||||
AWS,
|
||||
Supabase,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
|
||||
pub struct CollabPluginConfig {
|
||||
/// Only one of the following two fields should be set.
|
||||
aws_config: Option<AWSDynamoDBConfig>,
|
||||
}
|
||||
|
||||
impl CollabPluginConfig {
|
||||
pub fn from_env() -> Self {
|
||||
let aws_config = AWSDynamoDBConfig::from_env();
|
||||
Self { aws_config }
|
||||
}
|
||||
|
||||
pub fn aws_config(&self) -> Option<&AWSDynamoDBConfig> {
|
||||
self.aws_config.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl CollabPluginConfig {}
|
||||
|
||||
impl FromStr for CollabPluginConfig {
|
||||
type Err = serde_json::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
serde_json::from_str(s)
|
||||
}
|
||||
}
|
||||
|
||||
pub const AWS_ACCESS_KEY_ID: &str = "AWS_ACCESS_KEY_ID";
|
||||
pub const AWS_SECRET_ACCESS_KEY: &str = "AWS_SECRET_ACCESS_KEY";
|
||||
pub const AWS_REGION: &str = "AWS_REGION";
|
||||
|
||||
// To enable this test, you should set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY in your environment variables.
|
||||
// or create the ~/.aws/credentials file following the instructions in https://docs.aws.amazon.com/sdk-for-rust/latest/dg/credentials.html
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct AWSDynamoDBConfig {
|
||||
pub access_key_id: String,
|
||||
pub secret_access_key: String,
|
||||
// Region list: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.RegionsAndAvailabilityZones.html
|
||||
pub region: String,
|
||||
pub enable: bool,
|
||||
}
|
||||
|
||||
impl AWSDynamoDBConfig {
|
||||
pub fn from_env() -> Option<Self> {
|
||||
let access_key_id = std::env::var(AWS_ACCESS_KEY_ID).ok()?;
|
||||
let secret_access_key = std::env::var(AWS_SECRET_ACCESS_KEY).ok()?;
|
||||
let region = std::env::var(AWS_REGION).unwrap_or_else(|_| "us-east-1".to_string());
|
||||
Some(Self {
|
||||
access_key_id,
|
||||
secret_access_key,
|
||||
region,
|
||||
enable: true,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write_env(&self) {
|
||||
std::env::set_var(AWS_ACCESS_KEY_ID, &self.access_key_id);
|
||||
std::env::set_var(AWS_SECRET_ACCESS_KEY, &self.secret_access_key);
|
||||
std::env::set_var(AWS_REGION, &self.region);
|
||||
}
|
||||
}
|
26
frontend/rust-lib/collab-integrate/src/lib.rs
Normal file
26
frontend/rust-lib/collab-integrate/src/lib.rs
Normal file
@ -0,0 +1,26 @@
|
||||
pub use collab::core::collab::MutexCollab;
|
||||
pub use collab::preclude::Snapshot;
|
||||
pub use collab_persistence::doc::YrsDocAction;
|
||||
pub use collab_persistence::error::PersistenceError;
|
||||
#[cfg(any(
|
||||
feature = "appflowy_cloud_integrate",
|
||||
feature = "supabase_integrate",
|
||||
feature = "rocksdb_plugin"
|
||||
))]
|
||||
pub use collab_persistence::kv::rocks_kv::RocksCollabDB;
|
||||
pub use collab_persistence::snapshot::CollabSnapshot;
|
||||
#[cfg(feature = "supabase_integrate")]
|
||||
pub use collab_plugins::cloud_storage::*;
|
||||
#[cfg(any(
|
||||
feature = "appflowy_cloud_integrate",
|
||||
feature = "supabase_integrate",
|
||||
feature = "rocksdb_plugin"
|
||||
))]
|
||||
pub use collab_plugins::local_storage::CollabPersistenceConfig;
|
||||
#[cfg(feature = "snapshot_plugin")]
|
||||
pub use collab_plugins::snapshot::{
|
||||
calculate_snapshot_diff, try_encode_snapshot, SnapshotPersistence,
|
||||
};
|
||||
|
||||
pub mod collab_builder;
|
||||
pub mod config;
|
@ -24,15 +24,15 @@ crossbeam-utils = "0.8.15"
|
||||
lazy_static = "1.4.0"
|
||||
parking_lot = "0.12.1"
|
||||
tracing = { version = "0.1", features = ["log"] }
|
||||
appflowy-integrate = {version = "0.1.0" }
|
||||
|
||||
lib-dispatch = { path = "../lib-dispatch" }
|
||||
flowy-core = { path = "../flowy-core" }
|
||||
flowy-notification = { path = "../flowy-notification" }
|
||||
flowy-net = { path = "../flowy-net" }
|
||||
# workspace
|
||||
lib-dispatch = { workspace = true }
|
||||
flowy-core = { workspace = true }
|
||||
flowy-notification = { workspace = true }
|
||||
flowy-server = { workspace = true }
|
||||
flowy-server-config = { workspace = true }
|
||||
collab-integrate = { workspace = true }
|
||||
flowy-derive = { path = "../../../shared-lib/flowy-derive" }
|
||||
flowy-server = { path = "../flowy-server" }
|
||||
flowy-server-config = { path = "../flowy-server-config" }
|
||||
|
||||
[features]
|
||||
default = ["dart", "rev-sqlite"]
|
||||
|
@ -6,12 +6,14 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
flowy-sqlite = { path = "../flowy-sqlite" }
|
||||
# workspace
|
||||
flowy-sqlite = { workspace = true }
|
||||
lib-dispatch = { workspace = true }
|
||||
flowy-error = { workspace = true }
|
||||
|
||||
flowy-derive = { path = "../../../shared-lib/flowy-derive" }
|
||||
lib-dispatch = { path = "../lib-dispatch" }
|
||||
protobuf = {version = "2.28.0"}
|
||||
bytes = { version = "1.4" }
|
||||
flowy-error = { path = "../flowy-error" }
|
||||
strum_macros = "0.21"
|
||||
|
||||
[build-dependencies]
|
||||
|
@ -6,28 +6,29 @@ edition = "2018"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
lib-dispatch = { path = "../lib-dispatch" }
|
||||
lib-log = { path = "../lib-log" }
|
||||
flowy-user = { path = "../flowy-user" }
|
||||
flowy-user-deps = { path = "../flowy-user-deps" }
|
||||
flowy-net = { path = "../flowy-net" }
|
||||
flowy-folder2 = { path = "../flowy-folder2" }
|
||||
flowy-folder-deps = { path = "../flowy-folder-deps" }
|
||||
flowy-database2 = { path = "../flowy-database2" }
|
||||
flowy-database-deps = { path = "../flowy-database-deps" }
|
||||
flowy-sqlite = { path = "../flowy-sqlite" }
|
||||
flowy-document2 = { path = "../flowy-document2" }
|
||||
flowy-document-deps = { path = "../flowy-document-deps" }
|
||||
flowy-error = { path = "../flowy-error" }
|
||||
flowy-task = { path = "../flowy-task" }
|
||||
flowy-server = { path = "../flowy-server" }
|
||||
flowy-server-config = { path = "../flowy-server-config" }
|
||||
flowy-config = { path = "../flowy-config" }
|
||||
appflowy-integrate = { version = "0.1.0", features = ["postgres_storage_plugin", "snapshot_plugin"] }
|
||||
lib-dispatch = { workspace = true }
|
||||
lib-log = { workspace = true }
|
||||
flowy-user = { workspace = true }
|
||||
flowy-user-deps = { workspace = true }
|
||||
flowy-folder2 = { workspace = true }
|
||||
flowy-folder-deps = { workspace = true }
|
||||
flowy-database2 = { workspace = true }
|
||||
flowy-database-deps = { workspace = true }
|
||||
flowy-sqlite = { workspace = true }
|
||||
flowy-document2 = { workspace = true }
|
||||
flowy-document-deps = { workspace = true }
|
||||
flowy-error = { workspace = true }
|
||||
flowy-task = { workspace = true }
|
||||
flowy-server = { workspace = true }
|
||||
flowy-server-config = { workspace = true }
|
||||
flowy-config = { workspace = true }
|
||||
collab-integrate = { workspace = true, features = ["supabase_integrate", "appflowy_cloud_integrate", "snapshot_plugin"] }
|
||||
collab-define = { version = "0.1.0" }
|
||||
collab-plugins = { version = "0.1.0", features = ["sync_plugin"] }
|
||||
collab = { version = "0.1.0" }
|
||||
diesel = { version = "1.4.8", features = ["sqlite"] }
|
||||
uuid = { version = "1.3.3", features = ["v4"] }
|
||||
flowy-storage = { path = "../flowy-storage" }
|
||||
flowy-storage = { workspace = true }
|
||||
|
||||
tracing = { version = "0.1", features = ["log"] }
|
||||
futures-core = { version = "0.3", default-features = false }
|
||||
@ -35,6 +36,7 @@ bytes = "1.4"
|
||||
tokio = { version = "1.26", features = ["full"] }
|
||||
console-subscriber = { version = "0.1.8", optional = true }
|
||||
parking_lot = "0.12.1"
|
||||
anyhow = "1.0.75"
|
||||
|
||||
lib-infra = { path = "../../../shared-lib/lib-infra" }
|
||||
serde = "1.0"
|
||||
@ -49,7 +51,6 @@ native_sync = []
|
||||
use_bunyan = ["lib-log/use_bunyan"]
|
||||
dart = [
|
||||
"flowy-user/dart",
|
||||
"flowy-net/dart",
|
||||
"flowy-folder2/dart",
|
||||
"flowy-database2/dart",
|
||||
"flowy-document2/dart",
|
||||
@ -57,7 +58,6 @@ dart = [
|
||||
]
|
||||
ts = [
|
||||
"flowy-user/ts",
|
||||
"flowy-net/ts",
|
||||
"flowy-folder2/ts",
|
||||
"flowy-database2/ts",
|
||||
"flowy-document2/ts",
|
||||
@ -67,3 +67,4 @@ rev-sqlite = [
|
||||
"flowy-user/rev-sqlite",
|
||||
]
|
||||
openssl_vendored = ["flowy-sqlite/openssl_vendored"]
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
use std::sync::Weak;
|
||||
|
||||
use appflowy_integrate::{
|
||||
calculate_snapshot_diff, CollabSnapshot, PersistenceError, SnapshotPersistence,
|
||||
};
|
||||
use diesel::SqliteConnection;
|
||||
|
||||
use collab_integrate::{
|
||||
calculate_snapshot_diff, CollabSnapshot, PersistenceError, SnapshotPersistence,
|
||||
};
|
||||
use flowy_error::FlowyError;
|
||||
use flowy_sqlite::{
|
||||
insert_or_ignore_into,
|
||||
|
@ -1,9 +1,9 @@
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
use appflowy_integrate::collab_builder::AppFlowyCollabBuilder;
|
||||
use appflowy_integrate::RocksCollabDB;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
|
||||
use collab_integrate::RocksCollabDB;
|
||||
use flowy_database2::{DatabaseManager, DatabaseUser};
|
||||
use flowy_database_deps::cloud::DatabaseCloudService;
|
||||
use flowy_error::FlowyError;
|
||||
|
@ -1,8 +1,7 @@
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
use appflowy_integrate::collab_builder::AppFlowyCollabBuilder;
|
||||
use appflowy_integrate::RocksCollabDB;
|
||||
|
||||
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
|
||||
use collab_integrate::RocksCollabDB;
|
||||
use flowy_database2::DatabaseManager;
|
||||
use flowy_document2::manager::{DocumentManager, DocumentUser};
|
||||
use flowy_document_deps::cloud::DocumentCloudService;
|
||||
|
@ -2,11 +2,11 @@ use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
use appflowy_integrate::collab_builder::AppFlowyCollabBuilder;
|
||||
use appflowy_integrate::RocksCollabDB;
|
||||
use bytes::Bytes;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
|
||||
use collab_integrate::RocksCollabDB;
|
||||
use flowy_database2::entities::DatabaseLayoutPB;
|
||||
use flowy_database2::services::share::csv::CSVFormat;
|
||||
use flowy_database2::template::{make_default_board, make_default_calendar, make_default_grid};
|
||||
|
@ -1 +1,2 @@
|
||||
pub(crate) mod server;
|
||||
mod trait_impls;
|
||||
|
@ -2,18 +2,11 @@ use std::collections::HashMap;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
use appflowy_integrate::collab_builder::{CollabStorageProvider, CollabStorageType};
|
||||
use appflowy_integrate::{RemoteCollabStorage, YrsDocAction};
|
||||
use bytes::Bytes;
|
||||
use collab_define::{CollabObject, CollabType};
|
||||
use parking_lot::RwLock;
|
||||
use serde_repr::*;
|
||||
|
||||
use flowy_database_deps::cloud::*;
|
||||
use flowy_document2::deps::DocumentData;
|
||||
use flowy_document_deps::cloud::{DocumentCloudService, DocumentSnapshot};
|
||||
use collab_integrate::YrsDocAction;
|
||||
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
||||
use flowy_folder_deps::cloud::*;
|
||||
use flowy_server::af_cloud::configuration::appflowy_cloud_server_configuration;
|
||||
use flowy_server::af_cloud::AFCloudServer;
|
||||
use flowy_server::local_server::{LocalServer, LocalServerDB};
|
||||
@ -21,22 +14,19 @@ use flowy_server::supabase::SupabaseServer;
|
||||
use flowy_server::{AppFlowyEncryption, AppFlowyServer, EncryptionImpl};
|
||||
use flowy_server_config::supabase_config::SupabaseConfiguration;
|
||||
use flowy_sqlite::kv::StorePreferences;
|
||||
use flowy_storage::{FileStorageService, StorageObject};
|
||||
use flowy_user::event_map::UserCloudServiceProvider;
|
||||
use flowy_user::services::database::{
|
||||
get_user_profile, get_user_workspace, open_collab_db, open_user_db,
|
||||
};
|
||||
use flowy_user_deps::cloud::UserCloudService;
|
||||
use flowy_user_deps::entities::*;
|
||||
use lib_infra::future::FutureResult;
|
||||
|
||||
use crate::AppFlowyCoreConfig;
|
||||
|
||||
const SERVER_PROVIDER_TYPE_KEY: &str = "server_provider_type";
|
||||
pub(crate) const SERVER_PROVIDER_TYPE_KEY: &str = "server_provider_type";
|
||||
|
||||
#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize_repr, Deserialize_repr)]
|
||||
#[repr(u8)]
|
||||
pub enum ServerProviderType {
|
||||
pub enum ServerType {
|
||||
/// Local server provider.
|
||||
/// Offline mode, no user authentication and the data is stored locally.
|
||||
Local = 0,
|
||||
@ -45,48 +35,48 @@ pub enum ServerProviderType {
|
||||
/// progress.
|
||||
AppFlowyCloud = 1,
|
||||
/// Supabase server provider.
|
||||
/// It uses supabase's postgresql database to store data and user authentication.
|
||||
/// It uses supabase postgresql database to store data and user authentication.
|
||||
Supabase = 2,
|
||||
}
|
||||
|
||||
impl Display for ServerProviderType {
|
||||
impl Display for ServerType {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ServerProviderType::Local => write!(f, "Local"),
|
||||
ServerProviderType::AppFlowyCloud => write!(f, "AppFlowyCloud"),
|
||||
ServerProviderType::Supabase => write!(f, "Supabase"),
|
||||
ServerType::Local => write!(f, "Local"),
|
||||
ServerType::AppFlowyCloud => write!(f, "AppFlowyCloud"),
|
||||
ServerType::Supabase => write!(f, "Supabase"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The [AppFlowyServerProvider] provides list of [AppFlowyServer] base on the [AuthType]. Using
|
||||
/// the auth type, the [AppFlowyServerProvider] will create a new [AppFlowyServer] if it doesn't
|
||||
/// The [ServerProvider] provides list of [AppFlowyServer] base on the [AuthType]. Using
|
||||
/// the auth type, the [ServerProvider] will create a new [AppFlowyServer] if it doesn't
|
||||
/// exist.
|
||||
/// Each server implements the [AppFlowyServer] trait, which provides the [UserCloudService], etc.
|
||||
pub struct AppFlowyServerProvider {
|
||||
pub struct ServerProvider {
|
||||
config: AppFlowyCoreConfig,
|
||||
provider_type: RwLock<ServerProviderType>,
|
||||
providers: RwLock<HashMap<ServerProviderType, Arc<dyn AppFlowyServer>>>,
|
||||
encryption: RwLock<Arc<dyn AppFlowyEncryption>>,
|
||||
store_preferences: Weak<StorePreferences>,
|
||||
cache_user_service: RwLock<HashMap<ServerProviderType, Arc<dyn UserCloudService>>>,
|
||||
server_type: RwLock<ServerType>,
|
||||
providers: RwLock<HashMap<ServerType, Arc<dyn AppFlowyServer>>>,
|
||||
pub(crate) encryption: RwLock<Arc<dyn AppFlowyEncryption>>,
|
||||
pub(crate) store_preferences: Weak<StorePreferences>,
|
||||
pub(crate) cache_user_service: RwLock<HashMap<ServerType, Arc<dyn UserCloudService>>>,
|
||||
|
||||
device_id: Arc<RwLock<String>>,
|
||||
enable_sync: RwLock<bool>,
|
||||
uid: Arc<RwLock<Option<i64>>>,
|
||||
pub(crate) device_id: Arc<RwLock<String>>,
|
||||
pub(crate) enable_sync: RwLock<bool>,
|
||||
pub(crate) uid: Arc<RwLock<Option<i64>>>,
|
||||
}
|
||||
|
||||
impl AppFlowyServerProvider {
|
||||
impl ServerProvider {
|
||||
pub fn new(
|
||||
config: AppFlowyCoreConfig,
|
||||
provider_type: ServerProviderType,
|
||||
provider_type: ServerType,
|
||||
store_preferences: Weak<StorePreferences>,
|
||||
) -> Self {
|
||||
let encryption = EncryptionImpl::new(None);
|
||||
Self {
|
||||
config,
|
||||
provider_type: RwLock::new(provider_type),
|
||||
device_id: Default::default(),
|
||||
server_type: RwLock::new(provider_type),
|
||||
device_id: Arc::new(RwLock::new(uuid::Uuid::new_v4().to_string())),
|
||||
providers: RwLock::new(HashMap::new()),
|
||||
enable_sync: RwLock::new(true),
|
||||
encryption: RwLock::new(Arc::new(encryption)),
|
||||
@ -96,42 +86,51 @@ impl AppFlowyServerProvider {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn provider_type(&self) -> ServerProviderType {
|
||||
self.provider_type.read().clone()
|
||||
pub fn get_server_type(&self) -> ServerType {
|
||||
self.server_type.read().clone()
|
||||
}
|
||||
|
||||
pub fn set_server_type(&self, server_type: ServerType) {
|
||||
*self.server_type.write() = server_type;
|
||||
}
|
||||
|
||||
/// Returns a [AppFlowyServer] trait implementation base on the provider_type.
|
||||
fn get_provider(
|
||||
pub(crate) fn get_server(
|
||||
&self,
|
||||
provider_type: &ServerProviderType,
|
||||
server_type: &ServerType,
|
||||
) -> FlowyResult<Arc<dyn AppFlowyServer>> {
|
||||
if let Some(provider) = self.providers.read().get(provider_type) {
|
||||
if let Some(provider) = self.providers.read().get(server_type) {
|
||||
return Ok(provider.clone());
|
||||
}
|
||||
|
||||
let server = match provider_type {
|
||||
ServerProviderType::Local => {
|
||||
let server = match server_type {
|
||||
ServerType::Local => {
|
||||
let local_db = Arc::new(LocalServerDBImpl {
|
||||
storage_path: self.config.storage_path.clone(),
|
||||
});
|
||||
let server = Arc::new(LocalServer::new(local_db));
|
||||
|
||||
Ok::<Arc<dyn AppFlowyServer>, FlowyError>(server)
|
||||
},
|
||||
ServerProviderType::AppFlowyCloud => {
|
||||
ServerType::AppFlowyCloud => {
|
||||
let config = appflowy_cloud_server_configuration().map_err(|e| {
|
||||
FlowyError::new(
|
||||
ErrorCode::InvalidAuthConfig,
|
||||
format!(
|
||||
"Missing self host config: {:?}. Error: {:?}",
|
||||
provider_type, e
|
||||
server_type, e
|
||||
),
|
||||
)
|
||||
})?;
|
||||
let server = Arc::new(AFCloudServer::new(config));
|
||||
tracing::trace!("🔑AppFlowy cloud config: {:?}", config);
|
||||
let server = Arc::new(AFCloudServer::new(
|
||||
config,
|
||||
*self.enable_sync.read(),
|
||||
self.device_id.clone(),
|
||||
));
|
||||
|
||||
Ok::<Arc<dyn AppFlowyServer>, FlowyError>(server)
|
||||
},
|
||||
ServerProviderType::Supabase => {
|
||||
ServerType::Supabase => {
|
||||
let config = match SupabaseConfiguration::from_env() {
|
||||
Ok(config) => config,
|
||||
Err(e) => {
|
||||
@ -155,287 +154,30 @@ impl AppFlowyServerProvider {
|
||||
self
|
||||
.providers
|
||||
.write()
|
||||
.insert(provider_type.clone(), server.clone());
|
||||
.insert(server_type.clone(), server.clone());
|
||||
Ok(server)
|
||||
}
|
||||
}
|
||||
|
||||
impl FileStorageService for AppFlowyServerProvider {
|
||||
fn create_object(&self, object: StorageObject) -> FutureResult<String, FlowyError> {
|
||||
let server = self.get_provider(&self.provider_type.read());
|
||||
FutureResult::new(async move {
|
||||
let storage = server?.file_storage().ok_or(FlowyError::internal())?;
|
||||
storage.create_object(object).await
|
||||
})
|
||||
}
|
||||
|
||||
fn delete_object_by_url(&self, object_url: String) -> FutureResult<(), FlowyError> {
|
||||
let server = self.get_provider(&self.provider_type.read());
|
||||
FutureResult::new(async move {
|
||||
let storage = server?.file_storage().ok_or(FlowyError::internal())?;
|
||||
storage.delete_object_by_url(object_url).await
|
||||
})
|
||||
}
|
||||
|
||||
fn get_object_by_url(&self, object_url: String) -> FutureResult<Bytes, FlowyError> {
|
||||
let server = self.get_provider(&self.provider_type.read());
|
||||
FutureResult::new(async move {
|
||||
let storage = server?.file_storage().ok_or(FlowyError::internal())?;
|
||||
storage.get_object_by_url(object_url).await
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl UserCloudServiceProvider for AppFlowyServerProvider {
|
||||
fn set_enable_sync(&self, uid: i64, enable_sync: bool) {
|
||||
match self.get_provider(&self.provider_type.read()) {
|
||||
Ok(server) => {
|
||||
server.set_enable_sync(uid, enable_sync);
|
||||
*self.enable_sync.write() = enable_sync;
|
||||
*self.uid.write() = Some(uid);
|
||||
},
|
||||
Err(e) => tracing::error!("🔴Failed to enable sync: {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_encrypt_secret(&self, secret: String) {
|
||||
tracing::info!("🔑Set encrypt secret");
|
||||
self.encryption.write().set_secret(secret);
|
||||
}
|
||||
|
||||
/// When user login, the provider type is set by the [AuthType] and save to disk for next use.
|
||||
///
|
||||
/// Each [AuthType] has a corresponding [ServerProviderType]. The [ServerProviderType] is used
|
||||
/// to create a new [AppFlowyServer] if it doesn't exist. Once the [ServerProviderType] is set,
|
||||
/// it will be used when user open the app again.
|
||||
///
|
||||
fn set_auth_type(&self, auth_type: AuthType) {
|
||||
let provider_type: ServerProviderType = auth_type.into();
|
||||
*self.provider_type.write() = provider_type.clone();
|
||||
|
||||
match self.store_preferences.upgrade() {
|
||||
None => tracing::error!("🔴Failed to update server provider type: store preferences is drop"),
|
||||
Some(store_preferences) => {
|
||||
match store_preferences.set_object(SERVER_PROVIDER_TYPE_KEY, provider_type.clone()) {
|
||||
Ok(_) => tracing::trace!("Update server provider type to: {:?}", provider_type),
|
||||
Err(e) => {
|
||||
tracing::error!("🔴Failed to update server provider type: {:?}", e);
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn set_device_id(&self, device_id: &str) {
|
||||
*self.device_id.write() = device_id.to_string();
|
||||
}
|
||||
|
||||
/// Returns the [UserCloudService] base on the current [ServerProviderType].
|
||||
/// Creates a new [AppFlowyServer] if it doesn't exist.
|
||||
fn get_user_service(&self) -> Result<Arc<dyn UserCloudService>, FlowyError> {
|
||||
if let Some(user_service) = self
|
||||
.cache_user_service
|
||||
.read()
|
||||
.get(&self.provider_type.read())
|
||||
{
|
||||
return Ok(user_service.clone());
|
||||
}
|
||||
|
||||
let provider_type = self.provider_type.read().clone();
|
||||
let user_service = self.get_provider(&provider_type)?.user_service();
|
||||
self
|
||||
.cache_user_service
|
||||
.write()
|
||||
.insert(provider_type, user_service.clone());
|
||||
Ok(user_service)
|
||||
}
|
||||
|
||||
fn service_name(&self) -> String {
|
||||
self.provider_type.read().to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl FolderCloudService for AppFlowyServerProvider {
|
||||
fn create_workspace(&self, uid: i64, name: &str) -> FutureResult<Workspace, Error> {
|
||||
let server = self.get_provider(&self.provider_type.read());
|
||||
let name = name.to_string();
|
||||
FutureResult::new(async move { server?.folder_service().create_workspace(uid, &name).await })
|
||||
}
|
||||
|
||||
fn get_folder_data(&self, workspace_id: &str) -> FutureResult<Option<FolderData>, Error> {
|
||||
let server = self.get_provider(&self.provider_type.read());
|
||||
let workspace_id = workspace_id.to_string();
|
||||
FutureResult::new(async move {
|
||||
server?
|
||||
.folder_service()
|
||||
.get_folder_data(&workspace_id)
|
||||
.await
|
||||
})
|
||||
}
|
||||
|
||||
fn get_folder_snapshots(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
limit: usize,
|
||||
) -> FutureResult<Vec<FolderSnapshot>, Error> {
|
||||
let workspace_id = workspace_id.to_string();
|
||||
let server = self.get_provider(&self.provider_type.read());
|
||||
FutureResult::new(async move {
|
||||
server?
|
||||
.folder_service()
|
||||
.get_folder_snapshots(&workspace_id, limit)
|
||||
.await
|
||||
})
|
||||
}
|
||||
|
||||
fn get_folder_updates(&self, workspace_id: &str, uid: i64) -> FutureResult<Vec<Vec<u8>>, Error> {
|
||||
let workspace_id = workspace_id.to_string();
|
||||
let server = self.get_provider(&self.provider_type.read());
|
||||
FutureResult::new(async move {
|
||||
server?
|
||||
.folder_service()
|
||||
.get_folder_updates(&workspace_id, uid)
|
||||
.await
|
||||
})
|
||||
}
|
||||
|
||||
fn service_name(&self) -> String {
|
||||
self
|
||||
.get_provider(&self.provider_type.read())
|
||||
.map(|provider| provider.folder_service().service_name())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
impl DatabaseCloudService for AppFlowyServerProvider {
|
||||
fn get_collab_update(
|
||||
&self,
|
||||
object_id: &str,
|
||||
object_ty: CollabType,
|
||||
) -> FutureResult<CollabObjectUpdate, Error> {
|
||||
let server = self.get_provider(&self.provider_type.read());
|
||||
let database_id = object_id.to_string();
|
||||
FutureResult::new(async move {
|
||||
server?
|
||||
.database_service()
|
||||
.get_collab_update(&database_id, object_ty)
|
||||
.await
|
||||
})
|
||||
}
|
||||
|
||||
fn batch_get_collab_updates(
|
||||
&self,
|
||||
object_ids: Vec<String>,
|
||||
object_ty: CollabType,
|
||||
) -> FutureResult<CollabObjectUpdateByOid, Error> {
|
||||
let server = self.get_provider(&self.provider_type.read());
|
||||
FutureResult::new(async move {
|
||||
server?
|
||||
.database_service()
|
||||
.batch_get_collab_updates(object_ids, object_ty)
|
||||
.await
|
||||
})
|
||||
}
|
||||
|
||||
fn get_collab_snapshots(
|
||||
&self,
|
||||
object_id: &str,
|
||||
limit: usize,
|
||||
) -> FutureResult<Vec<DatabaseSnapshot>, Error> {
|
||||
let server = self.get_provider(&self.provider_type.read());
|
||||
let database_id = object_id.to_string();
|
||||
FutureResult::new(async move {
|
||||
server?
|
||||
.database_service()
|
||||
.get_collab_snapshots(&database_id, limit)
|
||||
.await
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl DocumentCloudService for AppFlowyServerProvider {
|
||||
fn get_document_updates(&self, document_id: &str) -> FutureResult<Vec<Vec<u8>>, Error> {
|
||||
let server = self.get_provider(&self.provider_type.read());
|
||||
let document_id = document_id.to_string();
|
||||
FutureResult::new(async move {
|
||||
server?
|
||||
.document_service()
|
||||
.get_document_updates(&document_id)
|
||||
.await
|
||||
})
|
||||
}
|
||||
|
||||
fn get_document_snapshots(
|
||||
&self,
|
||||
document_id: &str,
|
||||
limit: usize,
|
||||
) -> FutureResult<Vec<DocumentSnapshot>, Error> {
|
||||
let server = self.get_provider(&self.provider_type.read());
|
||||
let document_id = document_id.to_string();
|
||||
FutureResult::new(async move {
|
||||
server?
|
||||
.document_service()
|
||||
.get_document_snapshots(&document_id, limit)
|
||||
.await
|
||||
})
|
||||
}
|
||||
|
||||
fn get_document_data(&self, document_id: &str) -> FutureResult<Option<DocumentData>, Error> {
|
||||
let server = self.get_provider(&self.provider_type.read());
|
||||
let document_id = document_id.to_string();
|
||||
FutureResult::new(async move {
|
||||
server?
|
||||
.document_service()
|
||||
.get_document_data(&document_id)
|
||||
.await
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl CollabStorageProvider for AppFlowyServerProvider {
|
||||
fn storage_type(&self) -> CollabStorageType {
|
||||
self.provider_type().into()
|
||||
}
|
||||
|
||||
fn get_storage(
|
||||
&self,
|
||||
collab_object: &CollabObject,
|
||||
storage_type: &CollabStorageType,
|
||||
) -> Option<Arc<dyn RemoteCollabStorage>> {
|
||||
match storage_type {
|
||||
CollabStorageType::Local => None,
|
||||
CollabStorageType::AWS => None,
|
||||
CollabStorageType::Supabase => self
|
||||
.get_provider(&ServerProviderType::Supabase)
|
||||
.ok()
|
||||
.and_then(|provider| provider.collab_storage(collab_object)),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_sync_enabled(&self) -> bool {
|
||||
*self.enable_sync.read()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AuthType> for ServerProviderType {
|
||||
impl From<AuthType> for ServerType {
|
||||
fn from(auth_provider: AuthType) -> Self {
|
||||
match auth_provider {
|
||||
AuthType::Local => ServerProviderType::Local,
|
||||
AuthType::SelfHosted => ServerProviderType::AppFlowyCloud,
|
||||
AuthType::Supabase => ServerProviderType::Supabase,
|
||||
AuthType::Local => ServerType::Local,
|
||||
AuthType::SelfHosted => ServerType::AppFlowyCloud,
|
||||
AuthType::Supabase => ServerType::Supabase,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&AuthType> for ServerProviderType {
|
||||
impl From<&AuthType> for ServerType {
|
||||
fn from(auth_provider: &AuthType) -> Self {
|
||||
Self::from(auth_provider.clone())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_server_provider(store_preferences: &Arc<StorePreferences>) -> ServerProviderType {
|
||||
match store_preferences.get_object::<ServerProviderType>(SERVER_PROVIDER_TYPE_KEY) {
|
||||
None => ServerProviderType::Local,
|
||||
pub fn current_server_provider(store_preferences: &Arc<StorePreferences>) -> ServerType {
|
||||
match store_preferences.get_object::<ServerType>(SERVER_PROVIDER_TYPE_KEY) {
|
||||
None => ServerType::Local,
|
||||
Some(provider_type) => provider_type,
|
||||
}
|
||||
}
|
||||
|
321
frontend/rust-lib/flowy-core/src/integrate/trait_impls.rs
Normal file
321
frontend/rust-lib/flowy-core/src/integrate/trait_impls.rs
Normal file
@ -0,0 +1,321 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Error;
|
||||
use bytes::Bytes;
|
||||
use collab::core::origin::{CollabClient, CollabOrigin};
|
||||
use collab::preclude::CollabPlugin;
|
||||
use collab_define::CollabType;
|
||||
use collab_plugins::sync_plugin::{SyncObject, SyncPlugin};
|
||||
|
||||
use collab_integrate::collab_builder::{CollabPluginContext, CollabSource, CollabStorageProvider};
|
||||
use collab_integrate::postgres::SupabaseDBPlugin;
|
||||
use flowy_database_deps::cloud::{
|
||||
CollabObjectUpdate, CollabObjectUpdateByOid, DatabaseCloudService, DatabaseSnapshot,
|
||||
};
|
||||
use flowy_document2::deps::DocumentData;
|
||||
use flowy_document_deps::cloud::{DocumentCloudService, DocumentSnapshot};
|
||||
use flowy_error::FlowyError;
|
||||
use flowy_folder_deps::cloud::{FolderCloudService, FolderData, FolderSnapshot, Workspace};
|
||||
use flowy_storage::{FileStorageService, StorageObject};
|
||||
use flowy_user::event_map::UserCloudServiceProvider;
|
||||
use flowy_user_deps::cloud::UserCloudService;
|
||||
use flowy_user_deps::entities::AuthType;
|
||||
use lib_infra::async_trait::async_trait;
|
||||
use lib_infra::future::FutureResult;
|
||||
|
||||
use crate::integrate::server::{ServerProvider, ServerType, SERVER_PROVIDER_TYPE_KEY};
|
||||
|
||||
impl FileStorageService for ServerProvider {
|
||||
fn create_object(&self, object: StorageObject) -> FutureResult<String, FlowyError> {
|
||||
let server = self.get_server(&self.get_server_type());
|
||||
FutureResult::new(async move {
|
||||
let storage = server?.file_storage().ok_or(FlowyError::internal())?;
|
||||
storage.create_object(object).await
|
||||
})
|
||||
}
|
||||
|
||||
fn delete_object_by_url(&self, object_url: String) -> FutureResult<(), FlowyError> {
|
||||
let server = self.get_server(&self.get_server_type());
|
||||
FutureResult::new(async move {
|
||||
let storage = server?.file_storage().ok_or(FlowyError::internal())?;
|
||||
storage.delete_object_by_url(object_url).await
|
||||
})
|
||||
}
|
||||
|
||||
fn get_object_by_url(&self, object_url: String) -> FutureResult<Bytes, FlowyError> {
|
||||
let server = self.get_server(&self.get_server_type());
|
||||
FutureResult::new(async move {
|
||||
let storage = server?.file_storage().ok_or(FlowyError::internal())?;
|
||||
storage.get_object_by_url(object_url).await
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl UserCloudServiceProvider for ServerProvider {
|
||||
fn set_enable_sync(&self, uid: i64, enable_sync: bool) {
|
||||
match self.get_server(&self.get_server_type()) {
|
||||
Ok(server) => {
|
||||
server.set_enable_sync(uid, enable_sync);
|
||||
*self.enable_sync.write() = enable_sync;
|
||||
*self.uid.write() = Some(uid);
|
||||
},
|
||||
Err(e) => tracing::error!("🔴Failed to enable sync: {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_encrypt_secret(&self, secret: String) {
|
||||
tracing::info!("🔑Set encrypt secret");
|
||||
self.encryption.write().set_secret(secret);
|
||||
}
|
||||
|
||||
/// When user login, the provider type is set by the [AuthType] and save to disk for next use.
|
||||
///
|
||||
/// Each [AuthType] has a corresponding [ServerType]. The [ServerType] is used
|
||||
/// to create a new [AppFlowyServer] if it doesn't exist. Once the [ServerType] is set,
|
||||
/// it will be used when user open the app again.
|
||||
///
|
||||
fn set_auth_type(&self, auth_type: AuthType) {
|
||||
let server_type: ServerType = auth_type.into();
|
||||
self.set_server_type(server_type.clone());
|
||||
|
||||
match self.store_preferences.upgrade() {
|
||||
None => tracing::error!("🔴Failed to update server provider type: store preferences is drop"),
|
||||
Some(store_preferences) => {
|
||||
match store_preferences.set_object(SERVER_PROVIDER_TYPE_KEY, server_type.clone()) {
|
||||
Ok(_) => tracing::trace!("Update server provider type to: {:?}", server_type),
|
||||
Err(e) => {
|
||||
tracing::error!("🔴Failed to update server provider type: {:?}", e);
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn set_device_id(&self, device_id: &str) {
|
||||
if device_id.is_empty() {
|
||||
tracing::error!("🔴Device id is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
*self.device_id.write() = device_id.to_string();
|
||||
}
|
||||
|
||||
/// Returns the [UserCloudService] base on the current [ServerType].
|
||||
/// Creates a new [AppFlowyServer] if it doesn't exist.
|
||||
fn get_user_service(&self) -> Result<Arc<dyn UserCloudService>, FlowyError> {
|
||||
if let Some(user_service) = self.cache_user_service.read().get(&self.get_server_type()) {
|
||||
return Ok(user_service.clone());
|
||||
}
|
||||
|
||||
let server_type = self.get_server_type();
|
||||
let user_service = self.get_server(&server_type)?.user_service();
|
||||
self
|
||||
.cache_user_service
|
||||
.write()
|
||||
.insert(server_type, user_service.clone());
|
||||
Ok(user_service)
|
||||
}
|
||||
|
||||
fn service_name(&self) -> String {
|
||||
self.get_server_type().to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl FolderCloudService for ServerProvider {
|
||||
fn create_workspace(&self, uid: i64, name: &str) -> FutureResult<Workspace, Error> {
|
||||
let server = self.get_server(&self.get_server_type());
|
||||
let name = name.to_string();
|
||||
FutureResult::new(async move { server?.folder_service().create_workspace(uid, &name).await })
|
||||
}
|
||||
|
||||
fn get_folder_data(&self, workspace_id: &str) -> FutureResult<Option<FolderData>, Error> {
|
||||
let server = self.get_server(&self.get_server_type());
|
||||
let workspace_id = workspace_id.to_string();
|
||||
FutureResult::new(async move {
|
||||
server?
|
||||
.folder_service()
|
||||
.get_folder_data(&workspace_id)
|
||||
.await
|
||||
})
|
||||
}
|
||||
|
||||
fn get_folder_snapshots(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
limit: usize,
|
||||
) -> FutureResult<Vec<FolderSnapshot>, Error> {
|
||||
let workspace_id = workspace_id.to_string();
|
||||
let server = self.get_server(&self.get_server_type());
|
||||
FutureResult::new(async move {
|
||||
server?
|
||||
.folder_service()
|
||||
.get_folder_snapshots(&workspace_id, limit)
|
||||
.await
|
||||
})
|
||||
}
|
||||
|
||||
fn get_folder_updates(&self, workspace_id: &str, uid: i64) -> FutureResult<Vec<Vec<u8>>, Error> {
|
||||
let workspace_id = workspace_id.to_string();
|
||||
let server = self.get_server(&self.get_server_type());
|
||||
FutureResult::new(async move {
|
||||
server?
|
||||
.folder_service()
|
||||
.get_folder_updates(&workspace_id, uid)
|
||||
.await
|
||||
})
|
||||
}
|
||||
|
||||
fn service_name(&self) -> String {
|
||||
self
|
||||
.get_server(&self.get_server_type())
|
||||
.map(|provider| provider.folder_service().service_name())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
impl DatabaseCloudService for ServerProvider {
|
||||
fn get_collab_update(
|
||||
&self,
|
||||
object_id: &str,
|
||||
object_ty: CollabType,
|
||||
) -> FutureResult<CollabObjectUpdate, Error> {
|
||||
let server = self.get_server(&self.get_server_type());
|
||||
let database_id = object_id.to_string();
|
||||
FutureResult::new(async move {
|
||||
server?
|
||||
.database_service()
|
||||
.get_collab_update(&database_id, object_ty)
|
||||
.await
|
||||
})
|
||||
}
|
||||
|
||||
fn batch_get_collab_updates(
|
||||
&self,
|
||||
object_ids: Vec<String>,
|
||||
object_ty: CollabType,
|
||||
) -> FutureResult<CollabObjectUpdateByOid, Error> {
|
||||
let server = self.get_server(&self.get_server_type());
|
||||
FutureResult::new(async move {
|
||||
server?
|
||||
.database_service()
|
||||
.batch_get_collab_updates(object_ids, object_ty)
|
||||
.await
|
||||
})
|
||||
}
|
||||
|
||||
fn get_collab_snapshots(
|
||||
&self,
|
||||
object_id: &str,
|
||||
limit: usize,
|
||||
) -> FutureResult<Vec<DatabaseSnapshot>, Error> {
|
||||
let server = self.get_server(&self.get_server_type());
|
||||
let database_id = object_id.to_string();
|
||||
FutureResult::new(async move {
|
||||
server?
|
||||
.database_service()
|
||||
.get_collab_snapshots(&database_id, limit)
|
||||
.await
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl DocumentCloudService for ServerProvider {
|
||||
fn get_document_updates(&self, document_id: &str) -> FutureResult<Vec<Vec<u8>>, Error> {
|
||||
let server = self.get_server(&self.get_server_type());
|
||||
let document_id = document_id.to_string();
|
||||
FutureResult::new(async move {
|
||||
server?
|
||||
.document_service()
|
||||
.get_document_updates(&document_id)
|
||||
.await
|
||||
})
|
||||
}
|
||||
|
||||
fn get_document_snapshots(
|
||||
&self,
|
||||
document_id: &str,
|
||||
limit: usize,
|
||||
) -> FutureResult<Vec<DocumentSnapshot>, Error> {
|
||||
let server = self.get_server(&self.get_server_type());
|
||||
let document_id = document_id.to_string();
|
||||
FutureResult::new(async move {
|
||||
server?
|
||||
.document_service()
|
||||
.get_document_snapshots(&document_id, limit)
|
||||
.await
|
||||
})
|
||||
}
|
||||
|
||||
fn get_document_data(&self, document_id: &str) -> FutureResult<Option<DocumentData>, Error> {
|
||||
let server = self.get_server(&self.get_server_type());
|
||||
let document_id = document_id.to_string();
|
||||
FutureResult::new(async move {
|
||||
server?
|
||||
.document_service()
|
||||
.get_document_data(&document_id)
|
||||
.await
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl CollabStorageProvider for ServerProvider {
|
||||
fn storage_source(&self) -> CollabSource {
|
||||
self.get_server_type().into()
|
||||
}
|
||||
|
||||
async fn get_plugins(&self, context: CollabPluginContext) -> Vec<Arc<dyn CollabPlugin>> {
|
||||
let mut plugins: Vec<Arc<dyn CollabPlugin>> = vec![];
|
||||
match context {
|
||||
CollabPluginContext::Local => {},
|
||||
CollabPluginContext::AppFlowyCloud {
|
||||
uid: _,
|
||||
collab_object,
|
||||
local_collab,
|
||||
} => {
|
||||
if let Ok(server) = self.get_server(&ServerType::AppFlowyCloud) {
|
||||
match server.collab_ws_channel(&collab_object.object_id).await {
|
||||
Ok(Some(channel)) => {
|
||||
let origin = CollabOrigin::Client(CollabClient::new(
|
||||
collab_object.uid,
|
||||
collab_object.device_id.clone(),
|
||||
));
|
||||
let sync_object = SyncObject::from(collab_object);
|
||||
let (sink, stream) = (channel.sink(), channel.stream());
|
||||
let sync_plugin = SyncPlugin::new(origin, sync_object, local_collab, sink, stream);
|
||||
plugins.push(Arc::new(sync_plugin));
|
||||
},
|
||||
Ok(None) => {},
|
||||
Err(err) => tracing::error!("🔴Failed to get collab ws channel: {:?}", err),
|
||||
}
|
||||
}
|
||||
},
|
||||
CollabPluginContext::Supabase {
|
||||
uid,
|
||||
collab_object,
|
||||
local_collab,
|
||||
local_collab_db,
|
||||
} => {
|
||||
if let Some(remote_collab_storage) = self
|
||||
.get_server(&ServerType::Supabase)
|
||||
.ok()
|
||||
.and_then(|provider| provider.collab_storage(&collab_object))
|
||||
{
|
||||
plugins.push(Arc::new(SupabaseDBPlugin::new(
|
||||
uid,
|
||||
collab_object,
|
||||
local_collab,
|
||||
1,
|
||||
remote_collab_storage,
|
||||
local_collab_db,
|
||||
)));
|
||||
}
|
||||
},
|
||||
}
|
||||
plugins
|
||||
}
|
||||
|
||||
fn is_sync_enabled(&self) -> bool {
|
||||
*self.enable_sync.read()
|
||||
}
|
||||
}
|
@ -10,9 +10,9 @@ use std::{
|
||||
},
|
||||
};
|
||||
|
||||
use appflowy_integrate::collab_builder::{AppFlowyCollabBuilder, CollabStorageType};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use collab_integrate::collab_builder::{AppFlowyCollabBuilder, CollabSource};
|
||||
use flowy_database2::DatabaseManager;
|
||||
use flowy_document2::manager::DocumentManager;
|
||||
use flowy_error::FlowyResult;
|
||||
@ -31,9 +31,7 @@ use module::make_plugins;
|
||||
pub use module::*;
|
||||
|
||||
use crate::deps_resolve::*;
|
||||
use crate::integrate::server::{
|
||||
current_server_provider, AppFlowyServerProvider, ServerProviderType,
|
||||
};
|
||||
use crate::integrate::server::{current_server_provider, ServerProvider, ServerType};
|
||||
|
||||
mod deps_resolve;
|
||||
mod integrate;
|
||||
@ -121,7 +119,7 @@ pub struct AppFlowyCore {
|
||||
pub folder_manager: Arc<FolderManager>,
|
||||
pub database_manager: Arc<DatabaseManager>,
|
||||
pub event_dispatcher: Arc<AFPluginDispatcher>,
|
||||
pub server_provider: Arc<AppFlowyServerProvider>,
|
||||
pub server_provider: Arc<ServerProvider>,
|
||||
pub task_dispatcher: Arc<RwLock<TaskDispatcher>>,
|
||||
pub store_preference: Arc<StorePreferences>,
|
||||
}
|
||||
@ -147,7 +145,7 @@ impl AppFlowyCore {
|
||||
runtime.spawn(TaskRunner::run(task_dispatcher.clone()));
|
||||
|
||||
let provider_type = current_server_provider(&store_preference);
|
||||
let server_provider = Arc::new(AppFlowyServerProvider::new(
|
||||
let server_provider = Arc::new(ServerProvider::new(
|
||||
config.clone(),
|
||||
provider_type,
|
||||
Arc::downgrade(&store_preference),
|
||||
@ -282,7 +280,7 @@ struct UserStatusCallbackImpl {
|
||||
folder_manager: Arc<FolderManager>,
|
||||
database_manager: Arc<DatabaseManager>,
|
||||
document_manager: Arc<DocumentManager>,
|
||||
server_provider: Arc<AppFlowyServerProvider>,
|
||||
server_provider: Arc<ServerProvider>,
|
||||
#[allow(dead_code)]
|
||||
config: AppFlowyCoreConfig,
|
||||
}
|
||||
@ -298,7 +296,8 @@ impl UserStatusCallback for UserStatusCallbackImpl {
|
||||
_device_id: &str,
|
||||
) -> Fut<FlowyResult<()>> {
|
||||
let user_workspace = user_workspace.clone();
|
||||
let collab_builder = self.collab_builder.clone();
|
||||
self.collab_builder.initialize(user_workspace.id.clone());
|
||||
|
||||
let folder_manager = self.folder_manager.clone();
|
||||
let database_manager = self.database_manager.clone();
|
||||
let document_manager = self.document_manager.clone();
|
||||
@ -315,7 +314,6 @@ impl UserStatusCallback for UserStatusCallbackImpl {
|
||||
}
|
||||
|
||||
to_fut(async move {
|
||||
collab_builder.initialize(user_workspace.id.clone());
|
||||
folder_manager
|
||||
.initialize(
|
||||
user_id,
|
||||
@ -419,13 +417,13 @@ impl UserStatusCallback for UserStatusCallbackImpl {
|
||||
|
||||
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();
|
||||
self.collab_builder.initialize(user_workspace.id.clone());
|
||||
|
||||
let folder_manager = self.folder_manager.clone();
|
||||
let database_manager = self.database_manager.clone();
|
||||
let document_manager = self.document_manager.clone();
|
||||
|
||||
to_fut(async move {
|
||||
collab_builder.initialize(user_workspace.id.clone());
|
||||
folder_manager
|
||||
.initialize_with_workspace_id(user_id, &user_workspace.id)
|
||||
.await?;
|
||||
@ -449,12 +447,12 @@ impl UserStatusCallback for UserStatusCallbackImpl {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ServerProviderType> for CollabStorageType {
|
||||
fn from(server_provider: ServerProviderType) -> Self {
|
||||
impl From<ServerType> for CollabSource {
|
||||
fn from(server_provider: ServerType) -> Self {
|
||||
match server_provider {
|
||||
ServerProviderType::Local => CollabStorageType::Local,
|
||||
ServerProviderType::AppFlowyCloud => CollabStorageType::Local,
|
||||
ServerProviderType::Supabase => CollabStorageType::Supabase,
|
||||
ServerType::Local => CollabSource::Local,
|
||||
ServerType::AppFlowyCloud => CollabSource::Local,
|
||||
ServerType::Supabase => CollabSource::Supabase,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,14 +18,12 @@ pub fn make_plugins(
|
||||
.unwrap();
|
||||
let user_plugin = flowy_user::event_map::init(user_session);
|
||||
let folder_plugin = flowy_folder2::event_map::init(folder_manager);
|
||||
let network_plugin = flowy_net::event_map::init();
|
||||
let database_plugin = flowy_database2::event_map::init(database_manager);
|
||||
let document_plugin2 = flowy_document2::event_map::init(document_manager2);
|
||||
let config_plugin = flowy_config::event_map::init(store_preferences);
|
||||
vec![
|
||||
user_plugin,
|
||||
folder_plugin,
|
||||
network_plugin,
|
||||
database_plugin,
|
||||
document_plugin2,
|
||||
config_plugin,
|
||||
|
@ -7,6 +7,6 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
lib-infra = { path = "../../../shared-lib/lib-infra" }
|
||||
flowy-error = { path = "../flowy-error" }
|
||||
flowy-error = { workspace = true }
|
||||
collab-define = { version = "0.1.0" }
|
||||
anyhow = "1.0.71"
|
@ -9,24 +9,24 @@ edition = "2021"
|
||||
collab = { version = "0.1.0" }
|
||||
collab-database = { version = "0.1.0" }
|
||||
collab-define = { version = "0.1.0" }
|
||||
appflowy-integrate = {version = "0.1.0" }
|
||||
flowy-database-deps = { path = "../flowy-database-deps" }
|
||||
collab-integrate = { workspace = true }
|
||||
flowy-database-deps = { workspace = true }
|
||||
|
||||
flowy-derive = { path = "../../../shared-lib/flowy-derive" }
|
||||
flowy-notification = { path = "../flowy-notification" }
|
||||
flowy-notification = { workspace = true }
|
||||
parking_lot = "0.12.1"
|
||||
protobuf = {version = "2.28.0"}
|
||||
flowy-error = { path = "../flowy-error", features = ["impl_from_dispatch_error", "impl_from_collab"]}
|
||||
lib-dispatch = { path = "../lib-dispatch" }
|
||||
flowy-error = { workspace = true, features = ["impl_from_dispatch_error", "impl_from_collab"]}
|
||||
lib-dispatch = { workspace = true }
|
||||
tokio = { version = "1.26", features = ["sync"] }
|
||||
flowy-task= { path = "../flowy-task" }
|
||||
flowy-task= { workspace = true }
|
||||
bytes = { version = "1.4" }
|
||||
tracing = { version = "0.1", features = ["log"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = {version = "1.0"}
|
||||
serde_repr = "0.1"
|
||||
lib-infra = { path = "../../../shared-lib/lib-infra" }
|
||||
chrono = { version = "0.4.22", default-features = false, features = ["clock"] }
|
||||
chrono = { version = "0.4.27", default-features = false, features = ["clock"] }
|
||||
rust_decimal = "1.28.1"
|
||||
rusty-money = {version = "0.4.1", features = ["iso"]}
|
||||
lazy_static = "1.4.0"
|
||||
|
@ -1,8 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
use appflowy_integrate::collab_builder::AppFlowyCollabBuilder;
|
||||
use appflowy_integrate::{CollabPersistenceConfig, RocksCollabDB};
|
||||
use collab::core::collab::{CollabRawData, MutexCollab};
|
||||
use collab_database::blocks::BlockEvent;
|
||||
use collab_database::database::{DatabaseData, YrsDocAction};
|
||||
@ -15,6 +13,8 @@ use collab_database::views::{CreateDatabaseParams, CreateViewParams, DatabaseLay
|
||||
use collab_define::CollabType;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
|
||||
use collab_integrate::{CollabPersistenceConfig, RocksCollabDB};
|
||||
use flowy_database_deps::cloud::DatabaseCloudService;
|
||||
use flowy_error::{internal_error, FlowyError, FlowyResult};
|
||||
use flowy_task::TaskDispatcher;
|
||||
|
@ -1464,7 +1464,7 @@ impl DatabaseViewData for DatabaseViewDataImpl {
|
||||
field_id: &str,
|
||||
visibility: Option<FieldVisibility>,
|
||||
) {
|
||||
let field_settings_map = self.get_field_settings(view_id, &vec![field_id.to_string()]);
|
||||
let field_settings_map = self.get_field_settings(view_id, &[field_id.to_string()]);
|
||||
|
||||
let new_field_settings = if let Some(field_settings) = field_settings_map.get(field_id) {
|
||||
let mut field_settings = field_settings.to_owned();
|
||||
|
@ -910,10 +910,7 @@ impl DatabaseViewEditor {
|
||||
.send();
|
||||
}
|
||||
|
||||
pub async fn v_get_field_settings(
|
||||
&self,
|
||||
field_ids: &Vec<String>,
|
||||
) -> HashMap<String, FieldSettings> {
|
||||
pub async fn v_get_field_settings(&self, field_ids: &[String]) -> HashMap<String, FieldSettings> {
|
||||
self.delegate.get_field_settings(&self.view_id, field_ids)
|
||||
}
|
||||
|
||||
|
@ -292,7 +292,7 @@ mod tests {
|
||||
let native_timestamp = 1647251762;
|
||||
let native = NaiveDateTime::from_timestamp_opt(native_timestamp, 0).unwrap();
|
||||
|
||||
let utc = chrono::DateTime::<chrono::Utc>::from_utc(native, chrono::Utc);
|
||||
let utc = chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(native, chrono::Utc);
|
||||
// utc_timestamp doesn't carry timezone
|
||||
let utc_timestamp = utc.timestamp();
|
||||
assert_eq!(native_timestamp, utc_timestamp);
|
||||
@ -304,7 +304,8 @@ mod tests {
|
||||
|
||||
// Mon Mar 14 2022 17:56:02 GMT+0800 (China Standard Time)
|
||||
let gmt_8_offset = FixedOffset::east_opt(8 * 3600).unwrap();
|
||||
let china_local = chrono::DateTime::<chrono::Local>::from_utc(native, gmt_8_offset);
|
||||
let china_local =
|
||||
chrono::DateTime::<chrono::Local>::from_naive_utc_and_offset(native, gmt_8_offset);
|
||||
let china_local_time = format!(
|
||||
"{}",
|
||||
china_local.format_with_items(StrftimeItems::new(&format))
|
||||
|
@ -22,23 +22,13 @@ use crate::services::sort::SortCondition;
|
||||
/// The [DateTypeOption] is used by [FieldType::Date], [FieldType::LastEditedTime], and [FieldType::CreatedTime].
|
||||
/// So, storing the field type is necessary to distinguish the field type.
|
||||
/// Most of the cases, each [FieldType] has its own [TypeOption] implementation.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
|
||||
pub struct DateTypeOption {
|
||||
pub date_format: DateFormat,
|
||||
pub time_format: TimeFormat,
|
||||
pub timezone_id: String,
|
||||
}
|
||||
|
||||
impl Default for DateTypeOption {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
date_format: Default::default(),
|
||||
time_format: Default::default(),
|
||||
timezone_id: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeOption for DateTypeOption {
|
||||
type CellData = DateCellData;
|
||||
type CellChangeset = DateCellChangeset;
|
||||
@ -113,7 +103,7 @@ impl DateTypeOption {
|
||||
if let Some(timestamp) = timestamp {
|
||||
let naive = chrono::NaiveDateTime::from_timestamp_opt(*timestamp, 0).unwrap();
|
||||
let offset = self.get_timezone_offset(naive);
|
||||
let date_time = DateTime::<Local>::from_utc(naive, offset);
|
||||
let date_time = DateTime::<Local>::from_naive_utc_and_offset(naive, offset);
|
||||
|
||||
let fmt = self.date_format.format_str();
|
||||
let date = format!("{}", date_time.format(fmt));
|
||||
|
@ -108,7 +108,7 @@ impl TimestampTypeOption {
|
||||
if let Some(timestamp) = timestamp {
|
||||
let naive = chrono::NaiveDateTime::from_timestamp_opt(*timestamp, 0).unwrap();
|
||||
let offset = Local::now().offset().fix();
|
||||
let date_time = DateTime::<Local>::from_utc(naive, offset);
|
||||
let date_time = DateTime::<Local>::from_naive_utc_and_offset(naive, offset);
|
||||
|
||||
let fmt = self.date_format.format_str();
|
||||
let date = format!("{}", date_time.format(fmt));
|
||||
|
@ -262,16 +262,12 @@ pub fn default_type_option_data_from_type(field_type: &FieldType) -> TypeOptionD
|
||||
match field_type {
|
||||
FieldType::RichText => RichTextTypeOption::default().into(),
|
||||
FieldType::Number => NumberTypeOption::default().into(),
|
||||
FieldType::DateTime => DateTypeOption {
|
||||
..Default::default()
|
||||
}
|
||||
.into(),
|
||||
FieldType::DateTime => DateTypeOption::default().into(),
|
||||
FieldType::LastEditedTime | FieldType::CreatedTime => TimestampTypeOption {
|
||||
field_type: field_type.clone(),
|
||||
date_format: DateFormat::Friendly,
|
||||
time_format: TimeFormat::TwelveHour,
|
||||
include_time: true,
|
||||
..Default::default()
|
||||
}
|
||||
.into(),
|
||||
FieldType::SingleSelect => SingleSelectTypeOption::default().into(),
|
||||
|
@ -443,7 +443,7 @@ fn date_time_from_timestamp(timestamp: Option<i64>, timezone_id: &str) -> DateTi
|
||||
Err(_) => *Local::now().offset(),
|
||||
};
|
||||
|
||||
DateTime::<Local>::from_utc(naive, offset)
|
||||
DateTime::<Local>::from_naive_utc_and_offset(naive, offset)
|
||||
},
|
||||
None => DateTime::default(),
|
||||
}
|
||||
|
@ -7,6 +7,6 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
lib-infra = { path = "../../../shared-lib/lib-infra" }
|
||||
flowy-error = { path = "../flowy-error" }
|
||||
flowy-error = { workspace = true }
|
||||
collab-document = { version = "0.1.0" }
|
||||
anyhow = "1.0.71"
|
@ -9,14 +9,14 @@ edition = "2021"
|
||||
collab = { version = "0.1.0" }
|
||||
collab-document = { version = "0.1.0" }
|
||||
collab-define = { version = "0.1.0" }
|
||||
appflowy-integrate = {version = "0.1.0" }
|
||||
flowy-document-deps = { path = "../flowy-document-deps" }
|
||||
flowy-storage = { path = "../flowy-storage" }
|
||||
collab-integrate = { workspace = true }
|
||||
flowy-document-deps = { workspace = true }
|
||||
flowy-storage = { workspace = true }
|
||||
|
||||
flowy-derive = { path = "../../../shared-lib/flowy-derive" }
|
||||
flowy-notification = { path = "../flowy-notification" }
|
||||
flowy-notification = { workspace = true }
|
||||
flowy-error = { path = "../flowy-error", features = ["impl_from_serde", "impl_from_sqlite", "impl_from_dispatch_error", "impl_from_collab"] }
|
||||
lib-dispatch = { path = "../lib-dispatch" }
|
||||
lib-dispatch = { workspace = true }
|
||||
lib-infra = { path = "../../../shared-lib/lib-infra" }
|
||||
|
||||
protobuf = {version = "2.28.0"}
|
||||
|
@ -1,8 +1,6 @@
|
||||
use std::sync::Weak;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use appflowy_integrate::collab_builder::AppFlowyCollabBuilder;
|
||||
use appflowy_integrate::RocksCollabDB;
|
||||
use collab::core::collab::MutexCollab;
|
||||
use collab_define::CollabType;
|
||||
use collab_document::blocks::DocumentData;
|
||||
@ -11,6 +9,8 @@ use collab_document::document_data::default_document_data;
|
||||
use collab_document::YrsDocAction;
|
||||
use parking_lot::RwLock;
|
||||
|
||||
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
|
||||
use collab_integrate::RocksCollabDB;
|
||||
use flowy_document_deps::cloud::DocumentCloudService;
|
||||
use flowy_error::{internal_error, FlowyError, FlowyResult};
|
||||
use flowy_storage::FileStorageService;
|
||||
|
@ -2,8 +2,6 @@ use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Error;
|
||||
use appflowy_integrate::collab_builder::{AppFlowyCollabBuilder, DefaultCollabStorageProvider};
|
||||
use appflowy_integrate::RocksCollabDB;
|
||||
use bytes::Bytes;
|
||||
use collab_document::blocks::DocumentData;
|
||||
use collab_document::document_data::default_document_data;
|
||||
@ -12,6 +10,8 @@ use parking_lot::Once;
|
||||
use tempfile::TempDir;
|
||||
use tracing_subscriber::{fmt::Subscriber, util::SubscriberInitExt, EnvFilter};
|
||||
|
||||
use collab_integrate::collab_builder::{AppFlowyCollabBuilder, DefaultCollabStorageProvider};
|
||||
use collab_integrate::RocksCollabDB;
|
||||
use flowy_document2::document::MutexDocument;
|
||||
use flowy_document2::manager::{DocumentManager, DocumentUser};
|
||||
use flowy_document_deps::cloud::*;
|
||||
@ -57,18 +57,15 @@ impl FakeUser {
|
||||
}
|
||||
|
||||
impl DocumentUser for FakeUser {
|
||||
fn user_id(&self) -> Result<i64, flowy_error::FlowyError> {
|
||||
fn user_id(&self) -> Result<i64, FlowyError> {
|
||||
Ok(1)
|
||||
}
|
||||
|
||||
fn token(&self) -> Result<Option<String>, flowy_error::FlowyError> {
|
||||
fn token(&self) -> Result<Option<String>, FlowyError> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn collab_db(
|
||||
&self,
|
||||
_uid: i64,
|
||||
) -> Result<std::sync::Weak<RocksCollabDB>, flowy_error::FlowyError> {
|
||||
fn collab_db(&self, _uid: i64) -> Result<std::sync::Weak<RocksCollabDB>, FlowyError> {
|
||||
Ok(Arc::downgrade(&self.collab_db))
|
||||
}
|
||||
}
|
||||
@ -92,6 +89,7 @@ pub fn db() -> Arc<RocksCollabDB> {
|
||||
pub fn default_collab_builder() -> Arc<AppFlowyCollabBuilder> {
|
||||
let builder = AppFlowyCollabBuilder::new(DefaultCollabStorageProvider());
|
||||
builder.set_sync_device(uuid::Uuid::new_v4().to_string());
|
||||
builder.initialize(uuid::Uuid::new_v4().to_string());
|
||||
Arc::new(builder)
|
||||
}
|
||||
|
||||
|
@ -12,18 +12,19 @@ bytes = "1.4"
|
||||
anyhow = "1.0"
|
||||
thiserror = "1.0"
|
||||
|
||||
lib-dispatch = { path = "../lib-dispatch", optional = true }
|
||||
lib-dispatch = { workspace = true, optional = true }
|
||||
serde_json = {version = "1.0", optional = true}
|
||||
serde_repr = { version = "0.1" }
|
||||
serde = "1.0"
|
||||
reqwest = { version = "0.11.14", optional = true, features = ["native-tls-vendored"] }
|
||||
flowy-sqlite = { path = "../flowy-sqlite", optional = true}
|
||||
flowy-sqlite = { workspace = true, optional = true}
|
||||
r2d2 = { version = "0.8", optional = true}
|
||||
url = { version = "2.2", optional = true }
|
||||
collab-database = { version = "0.1.0", optional = true }
|
||||
collab-document = { version = "0.1.0", optional = true }
|
||||
tokio-postgres = { version = "0.7.8", optional = true }
|
||||
tokio = { version = "1.0", optional = true }
|
||||
client-api = { version = "0.1.0", optional = true }
|
||||
|
||||
[features]
|
||||
impl_from_dispatch_error = ["lib-dispatch"]
|
||||
@ -34,6 +35,7 @@ impl_from_collab = ["collab-database", "collab-document", "impl_from_reqwest"]
|
||||
impl_from_postgres = ["tokio-postgres"]
|
||||
impl_from_tokio= ["tokio"]
|
||||
impl_from_url= ["url"]
|
||||
impl_from_appflowy_cloud = ["client-api"]
|
||||
dart = ["flowy-codegen/dart"]
|
||||
ts = ["flowy-codegen/ts"]
|
||||
|
||||
|
@ -238,8 +238,18 @@ pub enum ErrorCode {
|
||||
|
||||
#[error("Parse url failed")]
|
||||
InvalidURL = 78,
|
||||
|
||||
#[error("Require Email Confirmation, Sign in after email confirmation")]
|
||||
AwaitingEmailConfirmation = 79,
|
||||
|
||||
#[error("Text id is empty")]
|
||||
TextIdIsEmpty = 79,
|
||||
TextIdIsEmpty = 80,
|
||||
|
||||
#[error("Record already exists")]
|
||||
RecordAlreadyExists = 81,
|
||||
|
||||
#[error("Missing payload")]
|
||||
MissingPayload = 82,
|
||||
}
|
||||
|
||||
impl ErrorCode {
|
||||
|
27
frontend/rust-lib/flowy-error/src/impl_from/cloud.rs
Normal file
27
frontend/rust-lib/flowy-error/src/impl_from/cloud.rs
Normal file
@ -0,0 +1,27 @@
|
||||
use client_api::error::AppError;
|
||||
|
||||
use crate::{ErrorCode, FlowyError};
|
||||
|
||||
impl From<AppError> for FlowyError {
|
||||
fn from(error: AppError) -> Self {
|
||||
let code = match error.code {
|
||||
client_api::error::ErrorCode::Ok => ErrorCode::Internal,
|
||||
client_api::error::ErrorCode::Unhandled => ErrorCode::Internal,
|
||||
client_api::error::ErrorCode::RecordNotFound => ErrorCode::RecordNotFound,
|
||||
client_api::error::ErrorCode::RecordAlreadyExists => ErrorCode::RecordAlreadyExists,
|
||||
client_api::error::ErrorCode::InvalidEmail => ErrorCode::EmailFormatInvalid,
|
||||
client_api::error::ErrorCode::InvalidPassword => ErrorCode::PasswordFormatInvalid,
|
||||
client_api::error::ErrorCode::OAuthError => ErrorCode::UserUnauthorized,
|
||||
client_api::error::ErrorCode::MissingPayload => ErrorCode::MissingPayload,
|
||||
client_api::error::ErrorCode::StorageError => ErrorCode::Internal,
|
||||
client_api::error::ErrorCode::OpenError => ErrorCode::Internal,
|
||||
client_api::error::ErrorCode::InvalidUrl => ErrorCode::InvalidURL,
|
||||
client_api::error::ErrorCode::InvalidRequestParams => ErrorCode::InvalidParams,
|
||||
client_api::error::ErrorCode::UrlMissingParameter => ErrorCode::InvalidParams,
|
||||
client_api::error::ErrorCode::InvalidOAuthProvider => ErrorCode::InvalidAuthConfig,
|
||||
client_api::error::ErrorCode::NotLoggedIn => ErrorCode::UserUnauthorized,
|
||||
};
|
||||
|
||||
FlowyError::new(code, error.message)
|
||||
}
|
||||
}
|
@ -22,5 +22,7 @@ mod postgres;
|
||||
#[cfg(feature = "impl_from_tokio")]
|
||||
mod tokio;
|
||||
|
||||
#[cfg(feature = "impl_from_appflowy_cloud")]
|
||||
mod cloud;
|
||||
#[cfg(feature = "impl_from_url")]
|
||||
mod url;
|
||||
|
@ -7,7 +7,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
lib-infra = { path = "../../../shared-lib/lib-infra" }
|
||||
flowy-error = { path = "../flowy-error" }
|
||||
flowy-error = { workspace = true }
|
||||
collab-folder = { version = "0.1.0" }
|
||||
uuid = { version = "1.3.3", features = ["v4"] }
|
||||
anyhow = "1.0.71"
|
@ -9,29 +9,29 @@ edition = "2021"
|
||||
collab = { version = "0.1.0" }
|
||||
collab-folder = { version = "0.1.0" }
|
||||
collab-define = { version = "0.1.0" }
|
||||
appflowy-integrate = {version = "0.1.0" }
|
||||
flowy-folder-deps = { path = "../flowy-folder-deps" }
|
||||
collab-integrate = { workspace = true }
|
||||
flowy-folder-deps = { workspace = true }
|
||||
|
||||
flowy-derive = { path = "../../../shared-lib/flowy-derive" }
|
||||
flowy-notification = { path = "../flowy-notification" }
|
||||
flowy-notification = { workspace = true }
|
||||
parking_lot = "0.12.1"
|
||||
unicode-segmentation = "1.10"
|
||||
tracing = { version = "0.1", features = ["log"] }
|
||||
flowy-error = { path = "../flowy-error", features = ["impl_from_dispatch_error"]}
|
||||
lib-dispatch = { path = "../lib-dispatch" }
|
||||
lib-dispatch = { workspace = true }
|
||||
bytes = { version = "1.4" }
|
||||
lib-infra = { path = "../../../shared-lib/lib-infra" }
|
||||
tokio = { version = "1.26", features = ["full"] }
|
||||
nanoid = "0.4.0"
|
||||
lazy_static = "1.4.0"
|
||||
chrono = { version = "0.4.22", default-features = false, features = ["clock"] }
|
||||
chrono = { version = "0.4.27", default-features = false, features = ["clock"] }
|
||||
strum_macros = "0.21"
|
||||
protobuf = {version = "2.28.0"}
|
||||
uuid = { version = "1.3.3", features = ["v4"] }
|
||||
tokio-stream = { version = "0.1.14", features = ["sync"] }
|
||||
|
||||
[dev-dependencies]
|
||||
flowy-folder2 = { path = "../flowy-folder2"}
|
||||
flowy-folder2 = { workspace = true }
|
||||
flowy-test = { path = "../flowy-test", default-features = false }
|
||||
|
||||
[build-dependencies]
|
||||
|
@ -2,8 +2,6 @@ use std::collections::HashSet;
|
||||
use std::ops::Deref;
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
use appflowy_integrate::collab_builder::AppFlowyCollabBuilder;
|
||||
use appflowy_integrate::{CollabPersistenceConfig, RocksCollabDB, YrsDocAction};
|
||||
use collab::core::collab::{CollabRawData, MutexCollab};
|
||||
use collab::core::collab_state::SyncState;
|
||||
use collab_define::CollabType;
|
||||
@ -16,6 +14,8 @@ use tokio_stream::wrappers::WatchStream;
|
||||
use tokio_stream::StreamExt;
|
||||
use tracing::{event, Level};
|
||||
|
||||
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
|
||||
use collab_integrate::{CollabPersistenceConfig, RocksCollabDB, YrsDocAction};
|
||||
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
||||
use flowy_folder_deps::cloud::{gen_view_id, FolderCloudService};
|
||||
|
||||
|
@ -1,25 +0,0 @@
|
||||
[package]
|
||||
name = "flowy-net"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
lib-dispatch = { path = "../lib-dispatch" }
|
||||
protobuf = {version = "2.28.0"}
|
||||
bytes = { version = "1.4" }
|
||||
tracing = { version = "0.1"}
|
||||
|
||||
[features]
|
||||
http_server = []
|
||||
dart = [
|
||||
"flowy-codegen/dart",
|
||||
]
|
||||
|
||||
ts = [
|
||||
"flowy-codegen/ts",
|
||||
]
|
||||
|
||||
[build-dependencies]
|
||||
flowy-codegen = { path = "../../../shared-lib/flowy-codegen"}
|
@ -1,3 +0,0 @@
|
||||
# Check out the FlowyConfig (located in flowy_toml.rs) for more details.
|
||||
proto_input = ["src/event_map.rs", "src/entities"]
|
||||
event_files = ["src/event_map.rs"]
|
@ -1,10 +0,0 @@
|
||||
fn main() {
|
||||
// let crate_name = env!("CARGO_PKG_NAME");
|
||||
// flowy_codegen::protobuf_file::gen(crate_name);
|
||||
//
|
||||
// #[cfg(feature = "dart")]
|
||||
// flowy_codegen::dart_event::gen(crate_name);
|
||||
//
|
||||
// #[cfg(feature = "ts")]
|
||||
// flowy_codegen::ts_event::gen(crate_name);
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
mod network_state;
|
||||
pub use network_state::*;
|
@ -1,29 +0,0 @@
|
||||
// use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||
//
|
||||
// #[derive(ProtoBuf_Enum, Debug, Clone, Eq, PartialEq, Default)]
|
||||
// pub enum NetworkTypePB {
|
||||
// #[default]
|
||||
// Unknown = 0,
|
||||
// Wifi = 1,
|
||||
// Cell = 2,
|
||||
// Ethernet = 3,
|
||||
// Bluetooth = 4,
|
||||
// VPN = 5,
|
||||
// }
|
||||
//
|
||||
// impl NetworkTypePB {
|
||||
// pub fn is_connect(&self) -> bool {
|
||||
// match self {
|
||||
// NetworkTypePB::Unknown | NetworkTypePB::Bluetooth => false,
|
||||
// NetworkTypePB::Wifi | NetworkTypePB::Cell | NetworkTypePB::Ethernet | NetworkTypePB::VPN => {
|
||||
// true
|
||||
// },
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #[derive(ProtoBuf, Debug, Default, Clone)]
|
||||
// pub struct NetworkStatePB {
|
||||
// #[pb(index = 1)]
|
||||
// pub ty: NetworkTypePB,
|
||||
// }
|
@ -1,5 +0,0 @@
|
||||
use lib_dispatch::prelude::*;
|
||||
|
||||
pub fn init() -> AFPlugin {
|
||||
AFPlugin::new().name("Flowy-Network")
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
// pub async fn update_network_ty(_data: AFPluginData<NetworkStatePB>) -> Result<(), FlowyError> {
|
||||
// Ok(())
|
||||
// }
|
@ -1,3 +0,0 @@
|
||||
pub mod entities;
|
||||
pub mod event_map;
|
||||
mod handlers;
|
@ -13,7 +13,7 @@ bytes = { version = "1.4" }
|
||||
serde = "1.0"
|
||||
|
||||
flowy-derive = { path = "../../../shared-lib/flowy-derive" }
|
||||
lib-dispatch = { path = "../lib-dispatch" }
|
||||
lib-dispatch = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
flowy-codegen = { path = "../../../shared-lib/flowy-codegen" }
|
||||
|
@ -6,5 +6,5 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
flowy-error = { path = "../flowy-error" }
|
||||
flowy-error = { workspace = true }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
@ -23,25 +23,26 @@ bytes = { version = "1.0.1", features = ["serde"] }
|
||||
tokio-retry = "0.3"
|
||||
anyhow = "1.0"
|
||||
uuid = { version = "1.3.3", features = ["v4"] }
|
||||
chrono = { version = "0.4.22", default-features = false, features = ["clock"] }
|
||||
chrono = { version = "0.4.27", default-features = false, features = ["clock"] }
|
||||
collab = { version = "0.1.0" }
|
||||
collab-plugins = { version = "0.1.0" }
|
||||
collab-plugins = { version = "0.1.0", features = ["sync_plugin"] }
|
||||
collab-document = { version = "0.1.0" }
|
||||
collab-define = { version = "0.1.0" }
|
||||
hex = "0.4.3"
|
||||
postgrest = "1.0"
|
||||
lib-infra = { path = "../../../shared-lib/lib-infra" }
|
||||
flowy-user-deps = { path = "../flowy-user-deps" }
|
||||
flowy-folder-deps = { path = "../flowy-folder-deps" }
|
||||
flowy-database-deps = { path = "../flowy-database-deps" }
|
||||
flowy-document-deps = { path = "../flowy-document-deps" }
|
||||
flowy-error = { path = "../flowy-error", features = ["impl_from_postgres", "impl_from_serde", "impl_from_reqwest", "impl_from_url"] }
|
||||
flowy-server-config = { path = "../flowy-server-config" }
|
||||
flowy-encrypt = { path = "../flowy-encrypt" }
|
||||
flowy-storage = { path = "../flowy-storage" }
|
||||
flowy-user-deps = { workspace = true }
|
||||
flowy-folder-deps = { workspace = true }
|
||||
flowy-database-deps = { workspace = true }
|
||||
flowy-document-deps = { workspace = true }
|
||||
flowy-error = { workspace = true, features = ["impl_from_postgres", "impl_from_serde", "impl_from_reqwest", "impl_from_url", "impl_from_appflowy_cloud"] }
|
||||
flowy-server-config = { workspace = true }
|
||||
flowy-encrypt = { workspace = true }
|
||||
flowy-storage = { workspace = true }
|
||||
mime_guess = "2.0"
|
||||
url = "2.4"
|
||||
tokio-util = "0.7"
|
||||
client-api = { version = "0.1.0" }
|
||||
|
||||
[dev-dependencies]
|
||||
uuid = { version = "1.3.3", features = ["v4"] }
|
||||
|
@ -20,7 +20,7 @@ pub fn appflowy_cloud_server_configuration() -> Result<AFCloudConfiguration, con
|
||||
settings.merge(config::File::from_str(base, FileFormat::Yaml).required(true))?;
|
||||
|
||||
let environment: Environment = std::env::var("APP_ENVIRONMENT")
|
||||
.unwrap_or_else(|_| "local".into())
|
||||
.unwrap_or_else(|_| "local".to_owned())
|
||||
.try_into()
|
||||
.expect("Failed to parse APP_ENVIRONMENT.");
|
||||
|
||||
@ -43,42 +43,6 @@ impl AFCloudConfiguration {
|
||||
format!("{}://{}:{}", self.http_scheme, self.host, self.port)
|
||||
}
|
||||
|
||||
pub fn sign_up_url(&self) -> String {
|
||||
format!("{}/api/register", self.base_url())
|
||||
}
|
||||
|
||||
pub fn sign_in_url(&self) -> String {
|
||||
format!("{}/api/auth", self.base_url())
|
||||
}
|
||||
|
||||
pub fn sign_out_url(&self) -> String {
|
||||
format!("{}/api/auth", self.base_url())
|
||||
}
|
||||
|
||||
pub fn user_profile_url(&self) -> String {
|
||||
format!("{}/api/user", self.base_url())
|
||||
}
|
||||
|
||||
pub fn workspace_url(&self) -> String {
|
||||
format!("{}/api/workspace", self.base_url())
|
||||
}
|
||||
|
||||
pub fn app_url(&self) -> String {
|
||||
format!("{}/api/app", self.base_url())
|
||||
}
|
||||
|
||||
pub fn view_url(&self) -> String {
|
||||
format!("{}/api/view", self.base_url())
|
||||
}
|
||||
|
||||
pub fn doc_url(&self) -> String {
|
||||
format!("{}/api/doc", self.base_url())
|
||||
}
|
||||
|
||||
pub fn trash_url(&self) -> String {
|
||||
format!("{}/api/trash", self.base_url())
|
||||
}
|
||||
|
||||
pub fn ws_addr(&self) -> String {
|
||||
format!("{}://{}:{}/ws", self.ws_scheme, self.host, self.port)
|
||||
}
|
||||
|
@ -6,9 +6,14 @@ use flowy_database_deps::cloud::{
|
||||
};
|
||||
use lib_infra::future::FutureResult;
|
||||
|
||||
pub(crate) struct AFCloudDatabaseCloudServiceImpl();
|
||||
use crate::af_cloud::AFServer;
|
||||
|
||||
impl DatabaseCloudService for AFCloudDatabaseCloudServiceImpl {
|
||||
pub(crate) struct AFCloudDatabaseCloudServiceImpl<T>(pub T);
|
||||
|
||||
impl<T> DatabaseCloudService for AFCloudDatabaseCloudServiceImpl<T>
|
||||
where
|
||||
T: AFServer,
|
||||
{
|
||||
fn get_collab_update(
|
||||
&self,
|
||||
_object_id: &str,
|
||||
|
@ -3,9 +3,14 @@ use anyhow::Error;
|
||||
use flowy_document_deps::cloud::*;
|
||||
use lib_infra::future::FutureResult;
|
||||
|
||||
pub(crate) struct AFCloudDocumentCloudServiceImpl();
|
||||
use crate::af_cloud::AFServer;
|
||||
|
||||
impl DocumentCloudService for AFCloudDocumentCloudServiceImpl {
|
||||
pub(crate) struct AFCloudDocumentCloudServiceImpl<T>(pub T);
|
||||
|
||||
impl<T> DocumentCloudService for AFCloudDocumentCloudServiceImpl<T>
|
||||
where
|
||||
T: AFServer,
|
||||
{
|
||||
fn get_document_updates(&self, _document_id: &str) -> FutureResult<Vec<Vec<u8>>, Error> {
|
||||
FutureResult::new(async move { Ok(vec![]) })
|
||||
}
|
||||
|
@ -1,24 +1,18 @@
|
||||
use anyhow::Error;
|
||||
|
||||
use flowy_folder_deps::cloud::{
|
||||
gen_workspace_id, FolderCloudService, FolderData, FolderSnapshot, Workspace,
|
||||
};
|
||||
use flowy_folder_deps::cloud::{FolderCloudService, FolderData, FolderSnapshot, Workspace};
|
||||
use lib_infra::future::FutureResult;
|
||||
use lib_infra::util::timestamp;
|
||||
|
||||
pub(crate) struct AFCloudFolderCloudServiceImpl();
|
||||
use crate::af_cloud::AFServer;
|
||||
|
||||
impl FolderCloudService for AFCloudFolderCloudServiceImpl {
|
||||
fn create_workspace(&self, _uid: i64, name: &str) -> FutureResult<Workspace, Error> {
|
||||
let name = name.to_string();
|
||||
FutureResult::new(async move {
|
||||
Ok(Workspace {
|
||||
id: gen_workspace_id().to_string(),
|
||||
name: name.to_string(),
|
||||
child_views: Default::default(),
|
||||
created_at: timestamp(),
|
||||
})
|
||||
})
|
||||
pub(crate) struct AFCloudFolderCloudServiceImpl<T>(pub T);
|
||||
|
||||
impl<T> FolderCloudService for AFCloudFolderCloudServiceImpl<T>
|
||||
where
|
||||
T: AFServer,
|
||||
{
|
||||
fn create_workspace(&self, _uid: i64, _name: &str) -> FutureResult<Workspace, Error> {
|
||||
FutureResult::new(async move { todo!() })
|
||||
}
|
||||
|
||||
fn get_folder_data(&self, _workspace_id: &str) -> FutureResult<Option<FolderData>, Error> {
|
||||
|
@ -1,96 +1,60 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Error;
|
||||
use collab_define::CollabObject;
|
||||
|
||||
use flowy_error::{ErrorCode, FlowyError};
|
||||
use flowy_error::FlowyError;
|
||||
use flowy_user_deps::cloud::UserCloudService;
|
||||
use flowy_user_deps::entities::*;
|
||||
use lib_infra::box_any::BoxAny;
|
||||
use lib_infra::future::FutureResult;
|
||||
|
||||
use crate::af_cloud::configuration::{AFCloudConfiguration, HEADER_TOKEN};
|
||||
use crate::request::HttpRequestBuilder;
|
||||
use crate::af_cloud::{AFCloudClient, AFServer};
|
||||
|
||||
pub(crate) struct AFCloudUserAuthServiceImpl {
|
||||
config: AFCloudConfiguration,
|
||||
pub(crate) struct AFCloudUserAuthServiceImpl<T> {
|
||||
server: T,
|
||||
}
|
||||
|
||||
impl AFCloudUserAuthServiceImpl {
|
||||
pub(crate) fn new(config: AFCloudConfiguration) -> Self {
|
||||
Self { config }
|
||||
impl<T> AFCloudUserAuthServiceImpl<T> {
|
||||
pub(crate) fn new(server: T) -> Self {
|
||||
Self { server }
|
||||
}
|
||||
}
|
||||
|
||||
impl UserCloudService for AFCloudUserAuthServiceImpl {
|
||||
impl<T> UserCloudService for AFCloudUserAuthServiceImpl<T>
|
||||
where
|
||||
T: AFServer,
|
||||
{
|
||||
fn sign_up(&self, params: BoxAny) -> FutureResult<SignUpResponse, Error> {
|
||||
let url = self.config.sign_up_url();
|
||||
let try_get_client = self.server.try_get_client();
|
||||
FutureResult::new(async move {
|
||||
let params = params.unbox_or_error::<SignUpParams>()?;
|
||||
let resp = user_sign_up_request(params, &url).await?;
|
||||
let resp = user_sign_up_request(try_get_client?, params).await?;
|
||||
Ok(resp)
|
||||
})
|
||||
}
|
||||
|
||||
fn sign_in(&self, params: BoxAny) -> FutureResult<SignInResponse, Error> {
|
||||
let url = self.config.sign_in_url();
|
||||
FutureResult::new(async move {
|
||||
let params = params.unbox_or_error::<SignInParams>()?;
|
||||
let resp = user_sign_in_request(params, &url).await?;
|
||||
Ok(resp)
|
||||
})
|
||||
fn sign_in(&self, _params: BoxAny) -> FutureResult<SignInResponse, Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn sign_out(&self, token: Option<String>) -> FutureResult<(), Error> {
|
||||
match token {
|
||||
None => FutureResult::new(async {
|
||||
Err(FlowyError::new(ErrorCode::InvalidParams, "Token should not be empty").into())
|
||||
}),
|
||||
Some(token) => {
|
||||
let token = token;
|
||||
let url = self.config.sign_out_url();
|
||||
FutureResult::new(async move {
|
||||
let _ = user_sign_out_request(&token, &url).await;
|
||||
Ok(())
|
||||
})
|
||||
},
|
||||
}
|
||||
fn sign_out(&self, _token: Option<String>) -> FutureResult<(), Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn update_user(
|
||||
&self,
|
||||
credential: UserCredentials,
|
||||
params: UpdateUserProfileParams,
|
||||
_credential: UserCredentials,
|
||||
_params: UpdateUserProfileParams,
|
||||
) -> FutureResult<(), Error> {
|
||||
match credential.token {
|
||||
None => FutureResult::new(async {
|
||||
Err(FlowyError::new(ErrorCode::InvalidParams, "Token should not be empty").into())
|
||||
}),
|
||||
Some(token) => {
|
||||
let token = token;
|
||||
let url = self.config.user_profile_url();
|
||||
FutureResult::new(async move {
|
||||
update_user_profile_request(&token, params, &url).await?;
|
||||
Ok(())
|
||||
})
|
||||
},
|
||||
}
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_user_profile(
|
||||
&self,
|
||||
credential: UserCredentials,
|
||||
_credential: UserCredentials,
|
||||
) -> FutureResult<Option<UserProfile>, Error> {
|
||||
let url = self.config.user_profile_url();
|
||||
FutureResult::new(async move {
|
||||
match credential.token {
|
||||
None => {
|
||||
Err(FlowyError::new(ErrorCode::UnexpectedEmpty, "Token should not be empty").into())
|
||||
},
|
||||
Some(token) => {
|
||||
let profile = get_user_profile_request(&token, &url).await?;
|
||||
Ok(Some(profile))
|
||||
},
|
||||
}
|
||||
})
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_user_workspaces(
|
||||
@ -145,53 +109,30 @@ impl UserCloudService for AFCloudUserAuthServiceImpl {
|
||||
}
|
||||
|
||||
pub async fn user_sign_up_request(
|
||||
client: Arc<AFCloudClient>,
|
||||
params: SignUpParams,
|
||||
url: &str,
|
||||
) -> Result<SignUpResponse, FlowyError> {
|
||||
let response = request_builder().post(url).json(params)?.response().await?;
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
pub async fn user_sign_in_request(
|
||||
params: SignInParams,
|
||||
url: &str,
|
||||
) -> Result<SignInResponse, FlowyError> {
|
||||
let response = request_builder().post(url).json(params)?.response().await?;
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
pub async fn user_sign_out_request(token: &str, url: &str) -> Result<(), FlowyError> {
|
||||
request_builder()
|
||||
.delete(url)
|
||||
.header(HEADER_TOKEN, token)
|
||||
.send()
|
||||
client
|
||||
.read()
|
||||
.await
|
||||
.sign_up(¶ms.email, ¶ms.password)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_user_profile_request(token: &str, url: &str) -> Result<UserProfile, FlowyError> {
|
||||
let user_profile = request_builder()
|
||||
.get(url)
|
||||
.header(HEADER_TOKEN, token)
|
||||
.response()
|
||||
.await?;
|
||||
Ok(user_profile)
|
||||
}
|
||||
|
||||
pub async fn update_user_profile_request(
|
||||
token: &str,
|
||||
params: UpdateUserProfileParams,
|
||||
url: &str,
|
||||
) -> Result<(), FlowyError> {
|
||||
request_builder()
|
||||
.patch(url)
|
||||
.header(HEADER_TOKEN, token)
|
||||
.json(params)?
|
||||
.send()
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn request_builder() -> HttpRequestBuilder {
|
||||
HttpRequestBuilder::new()
|
||||
todo!()
|
||||
// tracing::info!("User signed up: {:?}", user);
|
||||
// match user.confirmed_at {
|
||||
// Some(_) => {
|
||||
// // User is already confirmed, help her/him to sign in
|
||||
// let token = client.sign_in_password(¶ms.email, ¶ms.password).await?;
|
||||
//
|
||||
// // TODO:
|
||||
// // Query workspace list
|
||||
// // Query user profile
|
||||
//
|
||||
// todo!()
|
||||
// },
|
||||
// None => Err(FlowyError::new(
|
||||
// ErrorCode::AwaitingEmailConfirmation,
|
||||
// "Awaiting email confirmation".to_string(),
|
||||
// )),
|
||||
// }
|
||||
}
|
||||
|
@ -1,13 +1,19 @@
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use collab_define::CollabObject;
|
||||
use collab_plugins::cloud_storage::RemoteCollabStorage;
|
||||
use anyhow::Error;
|
||||
use client_api::notify::{TokenState, TokenStateReceiver};
|
||||
use client_api::ws::{BusinessID, WSClient, WSClientConfig, WebSocketChannel};
|
||||
use client_api::Client;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use flowy_database_deps::cloud::DatabaseCloudService;
|
||||
use flowy_document_deps::cloud::DocumentCloudService;
|
||||
use flowy_error::{ErrorCode, FlowyError};
|
||||
use flowy_folder_deps::cloud::FolderCloudService;
|
||||
use flowy_storage::FileStorageService;
|
||||
use flowy_user_deps::cloud::UserCloudService;
|
||||
use lib_infra::future::FutureResult;
|
||||
|
||||
use crate::af_cloud::configuration::AFCloudConfiguration;
|
||||
use crate::af_cloud::impls::{
|
||||
@ -16,38 +22,178 @@ use crate::af_cloud::impls::{
|
||||
};
|
||||
use crate::AppFlowyServer;
|
||||
|
||||
pub(crate) type AFCloudClient = RwLock<client_api::Client>;
|
||||
|
||||
pub struct AFCloudServer {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) config: AFCloudConfiguration,
|
||||
pub(crate) client: Arc<AFCloudClient>,
|
||||
enable_sync: AtomicBool,
|
||||
#[allow(dead_code)]
|
||||
device_id: Arc<parking_lot::RwLock<String>>,
|
||||
ws_client: Arc<RwLock<WSClient>>,
|
||||
}
|
||||
|
||||
impl AFCloudServer {
|
||||
pub fn new(config: AFCloudConfiguration) -> Self {
|
||||
Self { config }
|
||||
pub fn new(
|
||||
config: AFCloudConfiguration,
|
||||
enable_sync: bool,
|
||||
device_id: Arc<parking_lot::RwLock<String>>,
|
||||
) -> Self {
|
||||
let http_client = reqwest::Client::new();
|
||||
let api_client = client_api::Client::from(http_client, &config.base_url(), &config.ws_addr());
|
||||
let token_state_rx = api_client.subscribe_token_state();
|
||||
let enable_sync = AtomicBool::new(enable_sync);
|
||||
|
||||
let ws_client = WSClient::new(WSClientConfig {
|
||||
buffer_capacity: 100,
|
||||
ping_per_secs: 2,
|
||||
retry_connect_per_pings: 5,
|
||||
});
|
||||
let ws_client = Arc::new(RwLock::new(ws_client));
|
||||
let api_client = Arc::new(RwLock::new(api_client));
|
||||
|
||||
spawn_ws_conn(&device_id, token_state_rx, &ws_client, &api_client);
|
||||
Self {
|
||||
config,
|
||||
client: api_client,
|
||||
enable_sync,
|
||||
device_id,
|
||||
ws_client,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_client(&self) -> Option<Arc<AFCloudClient>> {
|
||||
if self.enable_sync.load(Ordering::SeqCst) {
|
||||
Some(self.client.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AppFlowyServer for AFCloudServer {
|
||||
fn set_enable_sync(&self, uid: i64, enable: bool) {
|
||||
tracing::info!("{} cloud sync: {}", uid, enable);
|
||||
self.enable_sync.store(enable, Ordering::SeqCst);
|
||||
}
|
||||
fn user_service(&self) -> Arc<dyn UserCloudService> {
|
||||
Arc::new(AFCloudUserAuthServiceImpl::new(self.config.clone()))
|
||||
let server = AFServerImpl(self.get_client());
|
||||
Arc::new(AFCloudUserAuthServiceImpl::new(server))
|
||||
}
|
||||
|
||||
fn folder_service(&self) -> Arc<dyn FolderCloudService> {
|
||||
Arc::new(AFCloudFolderCloudServiceImpl())
|
||||
let server = AFServerImpl(self.get_client());
|
||||
Arc::new(AFCloudFolderCloudServiceImpl(server))
|
||||
}
|
||||
|
||||
fn database_service(&self) -> Arc<dyn DatabaseCloudService> {
|
||||
Arc::new(AFCloudDatabaseCloudServiceImpl())
|
||||
let server = AFServerImpl(self.get_client());
|
||||
Arc::new(AFCloudDatabaseCloudServiceImpl(server))
|
||||
}
|
||||
|
||||
fn document_service(&self) -> Arc<dyn DocumentCloudService> {
|
||||
Arc::new(AFCloudDocumentCloudServiceImpl())
|
||||
let server = AFServerImpl(self.get_client());
|
||||
Arc::new(AFCloudDocumentCloudServiceImpl(server))
|
||||
}
|
||||
|
||||
fn collab_storage(&self, _collab_object: &CollabObject) -> Option<Arc<dyn RemoteCollabStorage>> {
|
||||
None
|
||||
fn collab_ws_channel(
|
||||
&self,
|
||||
object_id: &str,
|
||||
) -> FutureResult<Option<Arc<WebSocketChannel>>, anyhow::Error> {
|
||||
if self.enable_sync.load(Ordering::SeqCst) {
|
||||
let object_id = object_id.to_string();
|
||||
let weak_ws_client = Arc::downgrade(&self.ws_client);
|
||||
FutureResult::new(async move {
|
||||
match weak_ws_client.upgrade() {
|
||||
None => {
|
||||
tracing::warn!("🟡Collab WS client is dropped");
|
||||
Ok(None)
|
||||
},
|
||||
Some(ws_client) => Ok(
|
||||
ws_client
|
||||
.read()
|
||||
.await
|
||||
.subscribe(BusinessID::CollabId, object_id)
|
||||
.await
|
||||
.ok(),
|
||||
),
|
||||
}
|
||||
})
|
||||
} else {
|
||||
FutureResult::new(async { Ok(None) })
|
||||
}
|
||||
}
|
||||
|
||||
fn file_storage(&self) -> Option<Arc<dyn FileStorageService>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawns a new asynchronous task to handle WebSocket connections based on token state.
|
||||
///
|
||||
/// This function listens to the `token_state_rx` channel for token state updates. Depending on the
|
||||
/// received state, it either refreshes the WebSocket connection or disconnects from it.
|
||||
fn spawn_ws_conn(
|
||||
device_id: &Arc<parking_lot::RwLock<String>>,
|
||||
mut token_state_rx: TokenStateReceiver,
|
||||
ws_client: &Arc<RwLock<WSClient>>,
|
||||
api_client: &Arc<RwLock<Client>>,
|
||||
) {
|
||||
let weak_device_id = Arc::downgrade(device_id);
|
||||
let weak_ws_client = Arc::downgrade(ws_client);
|
||||
let weak_api_client = Arc::downgrade(api_client);
|
||||
tokio::spawn(async move {
|
||||
while let Ok(token_state) = token_state_rx.recv().await {
|
||||
tracing::info!("🟢Token state: {:?}", token_state);
|
||||
match token_state {
|
||||
TokenState::Refresh => {
|
||||
if let (Some(api_client), Some(ws_client), Some(device_id)) = (
|
||||
weak_api_client.upgrade(),
|
||||
weak_ws_client.upgrade(),
|
||||
weak_device_id.upgrade(),
|
||||
) {
|
||||
let device_id = device_id.read().clone();
|
||||
if let Ok(ws_addr) = api_client.read().await.ws_url(&device_id) {
|
||||
tracing::info!("🟢Connecting to websocket");
|
||||
let _ = ws_client.write().await.connect(ws_addr).await;
|
||||
}
|
||||
}
|
||||
},
|
||||
TokenState::Invalid => {
|
||||
if let Some(ws_client) = weak_ws_client.upgrade() {
|
||||
tracing::info!("🟡Disconnecting from websocket");
|
||||
ws_client.write().await.disconnect().await;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub trait AFServer: Send + Sync + 'static {
|
||||
fn get_client(&self) -> Option<Arc<AFCloudClient>>;
|
||||
fn try_get_client(&self) -> Result<Arc<AFCloudClient>, Error>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AFServerImpl(pub Option<Arc<AFCloudClient>>);
|
||||
|
||||
impl AFServer for AFServerImpl {
|
||||
fn get_client(&self) -> Option<Arc<AFCloudClient>> {
|
||||
self.0.clone()
|
||||
}
|
||||
|
||||
fn try_get_client(&self) -> Result<Arc<AFCloudClient>, Error> {
|
||||
match self.0.clone() {
|
||||
None => Err(
|
||||
FlowyError::new(
|
||||
ErrorCode::DataSyncRequired,
|
||||
"Data Sync is disabled, please enable it first",
|
||||
)
|
||||
.into(),
|
||||
),
|
||||
Some(client) => Ok(client),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ pub use server::*;
|
||||
|
||||
pub mod af_cloud;
|
||||
pub mod local_server;
|
||||
mod request;
|
||||
// mod request;
|
||||
mod response;
|
||||
mod server;
|
||||
pub mod supabase;
|
||||
|
@ -1,7 +1,5 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use collab_define::CollabObject;
|
||||
use collab_plugins::cloud_storage::RemoteCollabStorage;
|
||||
use parking_lot::RwLock;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
@ -70,10 +68,6 @@ impl AppFlowyServer for LocalServer {
|
||||
Arc::new(LocalServerDocumentCloudServiceImpl())
|
||||
}
|
||||
|
||||
fn collab_storage(&self, _collab_object: &CollabObject) -> Option<Arc<dyn RemoteCollabStorage>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn file_storage(&self) -> Option<Arc<dyn FileStorageService>> {
|
||||
None
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use client_api::ws::WebSocketChannel;
|
||||
use collab_define::CollabObject;
|
||||
use collab_plugins::cloud_storage::RemoteCollabStorage;
|
||||
use parking_lot::RwLock;
|
||||
@ -9,6 +10,7 @@ use flowy_document_deps::cloud::DocumentCloudService;
|
||||
use flowy_folder_deps::cloud::FolderCloudService;
|
||||
use flowy_storage::FileStorageService;
|
||||
use flowy_user_deps::cloud::UserCloudService;
|
||||
use lib_infra::future::FutureResult;
|
||||
|
||||
pub trait AppFlowyEncryption: Send + Sync + 'static {
|
||||
fn get_secret(&self) -> Option<String>;
|
||||
@ -85,7 +87,16 @@ pub trait AppFlowyServer: Send + Sync + 'static {
|
||||
/// # Returns
|
||||
///
|
||||
/// An `Option` that might contain an `Arc` wrapping the `RemoteCollabStorage` interface.
|
||||
fn collab_storage(&self, collab_object: &CollabObject) -> Option<Arc<dyn RemoteCollabStorage>>;
|
||||
fn collab_storage(&self, _collab_object: &CollabObject) -> Option<Arc<dyn RemoteCollabStorage>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn collab_ws_channel(
|
||||
&self,
|
||||
_object_id: &str,
|
||||
) -> FutureResult<Option<Arc<WebSocketChannel>>, anyhow::Error> {
|
||||
FutureResult::new(async { Ok(None) })
|
||||
}
|
||||
|
||||
fn file_storage(&self) -> Option<Arc<dyn FileStorageService>>;
|
||||
}
|
||||
|
@ -62,8 +62,11 @@ where
|
||||
|
||||
async fn get_all_updates(&self, object: &CollabObject) -> Result<Vec<Vec<u8>>, Error> {
|
||||
let postgrest = self.server.try_get_weak_postgrest()?;
|
||||
let action =
|
||||
FetchObjectUpdateAction::new(object.object_id.clone(), object.ty.clone(), postgrest);
|
||||
let action = FetchObjectUpdateAction::new(
|
||||
object.object_id.clone(),
|
||||
object.collab_type.clone(),
|
||||
postgrest,
|
||||
);
|
||||
let updates = action.run().await?;
|
||||
Ok(updates)
|
||||
}
|
||||
@ -140,10 +143,14 @@ where
|
||||
update: Vec<u8>,
|
||||
) -> Result<(), Error> {
|
||||
if let Some(postgrest) = self.server.get_postgrest() {
|
||||
let workspace_id = object.get_workspace_id().ok_or(anyhow::anyhow!(
|
||||
"Can't get the workspace id in CollabObject"
|
||||
))?;
|
||||
send_update(workspace_id, object, update, &postgrest, &self.secret()).await?;
|
||||
send_update(
|
||||
object.workspace_id.clone(),
|
||||
object,
|
||||
update,
|
||||
&postgrest,
|
||||
&self.secret(),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -156,16 +163,14 @@ where
|
||||
init_update: Vec<u8>,
|
||||
) -> Result<(), Error> {
|
||||
let postgrest = self.server.try_get_postgrest()?;
|
||||
let workspace_id = object
|
||||
.get_workspace_id()
|
||||
.ok_or(anyhow::anyhow!("Invalid workspace id"))?;
|
||||
|
||||
let update_items = get_updates_from_server(&object.object_id, &object.ty, &postgrest).await?;
|
||||
let update_items =
|
||||
get_updates_from_server(&object.object_id, &object.collab_type, &postgrest).await?;
|
||||
|
||||
// If the update_items is empty, we can send the init_update directly
|
||||
if update_items.is_empty() {
|
||||
send_update(
|
||||
workspace_id,
|
||||
object.workspace_id.clone(),
|
||||
object,
|
||||
init_update,
|
||||
&postgrest,
|
||||
@ -197,18 +202,13 @@ pub(crate) async fn flush_collab_with_update(
|
||||
) -> Result<(), Error> {
|
||||
// 2.Merge the updates into one and then delete the merged updates
|
||||
let merge_result = spawn_blocking(move || merge_updates(update_items, update)).await??;
|
||||
|
||||
let workspace_id = object
|
||||
.get_workspace_id()
|
||||
.ok_or(anyhow::anyhow!("Invalid workspace id"))?;
|
||||
|
||||
let value_size = merge_result.new_update.len() as i32;
|
||||
let md5 = md5(&merge_result.new_update);
|
||||
|
||||
tracing::trace!(
|
||||
"Flush collab id:{} type:{} is_encrypt: {}",
|
||||
object.object_id,
|
||||
object.ty,
|
||||
object.collab_type,
|
||||
secret.is_some()
|
||||
);
|
||||
let (new_update, encrypt) =
|
||||
@ -219,11 +219,11 @@ pub(crate) async fn flush_collab_with_update(
|
||||
.insert("encrypt", encrypt)
|
||||
.insert("md5", md5)
|
||||
.insert("value_size", value_size)
|
||||
.insert("partition_key", partition_key(&object.ty))
|
||||
.insert("partition_key", partition_key(&object.collab_type))
|
||||
.insert("uid", object.uid)
|
||||
.insert("workspace_id", workspace_id)
|
||||
.insert("workspace_id", &object.workspace_id)
|
||||
.insert("removed_keys", merge_result.merged_keys)
|
||||
.insert("did", object.get_device_id())
|
||||
.insert("did", &object.device_id)
|
||||
.build();
|
||||
|
||||
postgrest
|
||||
@ -247,13 +247,13 @@ pub(crate) async fn send_update(
|
||||
let (update, encrypt) = SupabaseBinaryColumnEncoder::encode(update, encryption_secret)?;
|
||||
let builder = InsertParamsBuilder::new()
|
||||
.insert("oid", object.object_id.clone())
|
||||
.insert("partition_key", partition_key(&object.ty))
|
||||
.insert("partition_key", partition_key(&object.collab_type))
|
||||
.insert("value", update)
|
||||
.insert("encrypt", encrypt)
|
||||
.insert("uid", object.uid)
|
||||
.insert("md5", md5)
|
||||
.insert("workspace_id", workspace_id)
|
||||
.insert("did", object.get_device_id())
|
||||
.insert("did", &object.device_id)
|
||||
.insert("value_size", value_size);
|
||||
|
||||
let params = builder.build();
|
||||
|
@ -150,7 +150,7 @@ pub async fn create_snapshot(
|
||||
.insert(
|
||||
InsertParamsBuilder::new()
|
||||
.insert(AF_COLLAB_SNAPSHOT_OID_COLUMN, object.object_id.clone())
|
||||
.insert("name", object.ty.to_string())
|
||||
.insert("name", object.collab_type.to_string())
|
||||
.insert(AF_COLLAB_SNAPSHOT_ENCRYPT_COLUMN, encrypt)
|
||||
.insert(AF_COLLAB_SNAPSHOT_BLOB_COLUMN, snapshot)
|
||||
.insert(AF_COLLAB_SNAPSHOT_BLOB_SIZE_COLUMN, value_size)
|
||||
|
@ -292,9 +292,12 @@ where
|
||||
.upgrade()
|
||||
.ok_or(anyhow::anyhow!("postgrest is not available"))?;
|
||||
|
||||
let updates =
|
||||
get_updates_from_server(&collab_object.object_id, &collab_object.ty, &postgrest)
|
||||
.await?;
|
||||
let updates = get_updates_from_server(
|
||||
&collab_object.object_id,
|
||||
&collab_object.collab_type,
|
||||
&postgrest,
|
||||
)
|
||||
.await?;
|
||||
|
||||
flush_collab_with_update(
|
||||
&collab_object,
|
||||
|
@ -9,7 +9,7 @@ mod supabase_test;
|
||||
pub fn setup_log() {
|
||||
static START: Once = Once::new();
|
||||
START.call_once(|| {
|
||||
let level = "debug";
|
||||
let level = "trace";
|
||||
let mut filters = vec![];
|
||||
filters.push(format!("flowy_server={}", level));
|
||||
std::env::set_var("RUST_LOG", filters.join(","));
|
||||
|
@ -10,7 +10,7 @@ use crate::supabase_test::util::{
|
||||
};
|
||||
|
||||
#[tokio::test]
|
||||
async fn supabase_create_workspace_test() {
|
||||
async fn supabase_create_database_test() {
|
||||
if get_supabase_ci_config().is_none() {
|
||||
return;
|
||||
}
|
||||
@ -27,13 +27,13 @@ async fn supabase_create_workspace_test() {
|
||||
for _i in 0..3 {
|
||||
let row_id = uuid::Uuid::new_v4().to_string();
|
||||
row_ids.push(row_id.clone());
|
||||
let collab_object = CollabObject {
|
||||
object_id: row_id,
|
||||
uid: user.user_id,
|
||||
ty: CollabType::DatabaseRow,
|
||||
meta: Default::default(),
|
||||
}
|
||||
.with_workspace_id(user.latest_workspace.id.clone());
|
||||
let collab_object = CollabObject::new(
|
||||
user.user_id,
|
||||
row_id,
|
||||
CollabType::DatabaseRow,
|
||||
user.latest_workspace.id.clone(),
|
||||
"fake_device_id".to_string(),
|
||||
);
|
||||
collab_service
|
||||
.send_update(&collab_object, 0, vec![1, 2, 3])
|
||||
.await
|
||||
|
@ -39,13 +39,13 @@ async fn supabase_get_folder_test() {
|
||||
let params = third_party_sign_up_param(uuid);
|
||||
let user: SignUpResponse = user_service.sign_up(BoxAny::new(params)).await.unwrap();
|
||||
|
||||
let collab_object = CollabObject {
|
||||
object_id: user.latest_workspace.id.clone(),
|
||||
uid: user.user_id,
|
||||
ty: CollabType::Folder,
|
||||
meta: Default::default(),
|
||||
}
|
||||
.with_workspace_id(user.latest_workspace.id.clone());
|
||||
let collab_object = CollabObject::new(
|
||||
user.user_id,
|
||||
user.latest_workspace.id.clone(),
|
||||
CollabType::Folder,
|
||||
user.latest_workspace.id.clone(),
|
||||
"fake_device_id".to_string(),
|
||||
);
|
||||
|
||||
let doc = Doc::with_client_id(1);
|
||||
let map = { doc.get_or_insert_map("map") };
|
||||
@ -113,13 +113,13 @@ async fn supabase_duplicate_updates_test() {
|
||||
let params = third_party_sign_up_param(uuid);
|
||||
let user: SignUpResponse = user_service.sign_up(BoxAny::new(params)).await.unwrap();
|
||||
|
||||
let collab_object = CollabObject {
|
||||
object_id: user.latest_workspace.id.clone(),
|
||||
uid: user.user_id,
|
||||
ty: CollabType::Folder,
|
||||
meta: Default::default(),
|
||||
}
|
||||
.with_workspace_id(user.latest_workspace.id.clone());
|
||||
let collab_object = CollabObject::new(
|
||||
user.user_id,
|
||||
user.latest_workspace.id.clone(),
|
||||
CollabType::Folder,
|
||||
user.latest_workspace.id.clone(),
|
||||
"fake_device_id".to_string(),
|
||||
);
|
||||
let doc = Doc::with_client_id(1);
|
||||
let map = { doc.get_or_insert_map("map") };
|
||||
let mut duplicated_updates = vec![];
|
||||
@ -220,13 +220,13 @@ async fn supabase_diff_state_vector_test() {
|
||||
let params = third_party_sign_up_param(uuid);
|
||||
let user: SignUpResponse = user_service.sign_up(BoxAny::new(params)).await.unwrap();
|
||||
|
||||
let collab_object = CollabObject {
|
||||
object_id: user.latest_workspace.id.clone(),
|
||||
uid: user.user_id,
|
||||
ty: CollabType::Folder,
|
||||
meta: Default::default(),
|
||||
}
|
||||
.with_workspace_id(user.latest_workspace.id.clone());
|
||||
let collab_object = CollabObject::new(
|
||||
user.user_id,
|
||||
user.latest_workspace.id.clone(),
|
||||
CollabType::Folder,
|
||||
user.latest_workspace.id.clone(),
|
||||
"fake_device_id".to_string(),
|
||||
);
|
||||
let doc = Doc::with_client_id(1);
|
||||
let map = { doc.get_or_insert_map("map") };
|
||||
let array = { doc.get_or_insert_array("array") };
|
||||
|
@ -14,4 +14,4 @@ bytes = "1.0.1"
|
||||
mime_guess = "2.0"
|
||||
lib-infra = { path = "../../../shared-lib/lib-infra" }
|
||||
url = "2.2.2"
|
||||
flowy-error = { path = "../flowy-error", features = ["impl_from_reqwest"] }
|
||||
flowy-error = { workspace = true, features = ["impl_from_reqwest"] }
|
||||
|
@ -6,24 +6,23 @@ edition = "2018"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
flowy-core = { path = "../flowy-core" }
|
||||
flowy-user = { path = "../flowy-user"}
|
||||
flowy-user-deps = { path = "../flowy-user-deps"}
|
||||
flowy-net = { path = "../flowy-net"}
|
||||
flowy-core = { workspace = true }
|
||||
flowy-user = { workspace = true }
|
||||
flowy-user-deps = { workspace = true }
|
||||
flowy-folder2 = { path = "../flowy-folder2", features = ["test_helper"] }
|
||||
flowy-folder-deps = { path = "../flowy-folder-deps" }
|
||||
flowy-folder-deps = { workspace = true }
|
||||
flowy-database2 = { path = "../flowy-database2" }
|
||||
flowy-database-deps = { path = "../flowy-database-deps" }
|
||||
flowy-database-deps = { workspace = true }
|
||||
flowy-document2 = { path = "../flowy-document2" }
|
||||
flowy-document-deps = { path = "../flowy-document-deps" }
|
||||
flowy-encrypt = { path = "../flowy-encrypt" }
|
||||
lib-dispatch = { path = "../lib-dispatch" }
|
||||
flowy-document-deps = { workspace = true }
|
||||
flowy-encrypt = { workspace = true }
|
||||
lib-dispatch = { workspace = true }
|
||||
lib-infra = { path = "../../../shared-lib/lib-infra" }
|
||||
flowy-server = { path = "../flowy-server" }
|
||||
flowy-server-config = { path = "../flowy-server-config" }
|
||||
flowy-notification = { path = "../flowy-notification" }
|
||||
flowy-server-config = { workspace = true }
|
||||
flowy-notification = { workspace = true }
|
||||
anyhow = "1.0.71"
|
||||
flowy-storage = { path = "../flowy-storage" }
|
||||
flowy-storage = { workspace = true }
|
||||
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = {version = "1.0"}
|
||||
@ -46,6 +45,7 @@ collab-document = { version = "0.1.0" }
|
||||
collab-folder = { version = "0.1.0" }
|
||||
collab-database = { version = "0.1.0" }
|
||||
collab-plugins = { version = "0.1.0" }
|
||||
collab-define = { version = "0.1.0" }
|
||||
assert-json-diff = "2.0.2"
|
||||
tokio-postgres = { version = "0.7.8" }
|
||||
zip = "0.6.6"
|
||||
|
@ -527,7 +527,7 @@ async fn update_date_cell_event_test() {
|
||||
let error = test
|
||||
.update_date_cell(DateChangesetPB {
|
||||
cell_id: cell_path,
|
||||
date: Some(timestamp.clone()),
|
||||
date: Some(timestamp),
|
||||
time: None,
|
||||
include_time: None,
|
||||
clear_flag: None,
|
||||
|
@ -5,7 +5,7 @@ use collab::core::collab::MutexCollab;
|
||||
use collab::core::origin::CollabOrigin;
|
||||
use collab::preclude::updates::decoder::Decode;
|
||||
use collab::preclude::{merge_updates_v1, JsonValue, Update};
|
||||
use collab_plugins::cloud_storage::CollabType;
|
||||
use collab_define::CollabType;
|
||||
|
||||
use flowy_database2::entities::{DatabasePB, DatabaseViewIdPB, RepeatedDatabaseSnapshotPB};
|
||||
use flowy_database2::event_map::DatabaseEvent::*;
|
||||
|
@ -2,6 +2,7 @@ use std::collections::HashMap;
|
||||
|
||||
use assert_json_diff::assert_json_eq;
|
||||
use collab_database::rows::database_row_document_id_from_row_id;
|
||||
use collab_define::CollabType;
|
||||
use collab_document::blocks::DocumentData;
|
||||
use collab_folder::core::FolderData;
|
||||
use nanoid::nanoid;
|
||||
@ -9,7 +10,7 @@ use serde_json::json;
|
||||
|
||||
use flowy_core::DEFAULT_NAME;
|
||||
use flowy_encrypt::decrypt_text;
|
||||
use flowy_server::supabase::define::{CollabType, USER_EMAIL, USER_UUID};
|
||||
use flowy_server::supabase::define::{USER_EMAIL, USER_UUID};
|
||||
use flowy_test::document::document_event::DocumentEventTest;
|
||||
use flowy_test::event_builder::EventBuilder;
|
||||
use flowy_test::FlowyCoreTest;
|
||||
|
@ -7,12 +7,12 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
lib-infra = { path = "../../../shared-lib/lib-infra" }
|
||||
flowy-error = { path = "../flowy-error" }
|
||||
flowy-error = { workspace = true }
|
||||
uuid = { version = "1.3.3", features = ["v4"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
collab-define = { version = "0.1.0" }
|
||||
serde_json = {version = "1.0"}
|
||||
serde_json = { version = "1.0"}
|
||||
serde_repr = "0.1"
|
||||
chrono = { version = "0.4.22", default-features = false, features = ["clock", "serde"] }
|
||||
chrono = { version = "0.4.27", default-features = false, features = ["clock", "serde"] }
|
||||
anyhow = "1.0.71"
|
||||
tokio = { version = "1.26", features = ["sync"] }
|
||||
|
@ -7,22 +7,22 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
flowy-derive = { path = "../../../shared-lib/flowy-derive" }
|
||||
flowy-sqlite = { path = "../flowy-sqlite", optional = true }
|
||||
flowy-encrypt = { path = "../flowy-encrypt" }
|
||||
flowy-error = { path = "../flowy-error", features = ["impl_from_sqlite", "impl_from_dispatch_error"] }
|
||||
flowy-folder-deps = { path = "../flowy-folder-deps" }
|
||||
flowy-sqlite = { workspace = true, optional = true }
|
||||
flowy-encrypt = { workspace = true }
|
||||
flowy-error = { workspace = true, features = ["impl_from_sqlite", "impl_from_dispatch_error"] }
|
||||
flowy-folder-deps = { workspace = true }
|
||||
lib-infra = { path = "../../../shared-lib/lib-infra" }
|
||||
flowy-notification = { path = "../flowy-notification" }
|
||||
flowy-server-config = { path = "../flowy-server-config" }
|
||||
lib-dispatch = { path = "../lib-dispatch" }
|
||||
appflowy-integrate = { version = "0.1.0" }
|
||||
flowy-notification = { workspace = true }
|
||||
flowy-server-config = { workspace = true }
|
||||
lib-dispatch = { workspace = true }
|
||||
collab-integrate = { workspace = true }
|
||||
collab = { version = "0.1.0" }
|
||||
collab-folder = { version = "0.1.0" }
|
||||
collab-document = { version = "0.1.0" }
|
||||
collab-database = { version = "0.1.0" }
|
||||
collab-user = { version = "0.1.0" }
|
||||
collab-define = { version = "0.1.0" }
|
||||
flowy-user-deps = { path = "../flowy-user-deps" }
|
||||
flowy-user-deps = { workspace = true }
|
||||
anyhow = "1.0.75"
|
||||
|
||||
tracing = { version = "0.1", features = ["log"] }
|
||||
@ -43,7 +43,7 @@ validator = "0.16.0"
|
||||
unicode-segmentation = "1.10"
|
||||
fancy-regex = "0.11.0"
|
||||
uuid = { version = "1.3.3", features = [ "v4"] }
|
||||
chrono = { version = "0.4.22", default-features = false, features = ["clock"] }
|
||||
chrono = { version = "0.4.27", default-features = false, features = ["clock"] }
|
||||
base64 = "^0.21"
|
||||
|
||||
[dev-dependencies]
|
||||
|
@ -453,6 +453,7 @@ pub async fn reset_workspace_handler(
|
||||
"The workspace id is empty",
|
||||
));
|
||||
}
|
||||
manager.reset_workspace(reset_pb).await?;
|
||||
let session = manager.get_session()?;
|
||||
manager.reset_workspace(reset_pb, session.device_id).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
use std::string::ToString;
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
use appflowy_integrate::collab_builder::AppFlowyCollabBuilder;
|
||||
use appflowy_integrate::RocksCollabDB;
|
||||
use collab_user::core::MutexUserAwareness;
|
||||
use serde_json::Value;
|
||||
use tokio::sync::{Mutex, RwLock};
|
||||
use uuid::Uuid;
|
||||
|
||||
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
|
||||
use collab_integrate::RocksCollabDB;
|
||||
use flowy_error::{internal_error, ErrorCode, FlowyResult};
|
||||
use flowy_sqlite::kv::StorePreferences;
|
||||
use flowy_sqlite::schema::user_table;
|
||||
@ -597,6 +597,7 @@ impl UserManager {
|
||||
|
||||
if let Err(err) = sync_user_data_to_cloud(
|
||||
self.cloud_services.get_user_service()?,
|
||||
"",
|
||||
new_user,
|
||||
&new_collab_db,
|
||||
)
|
||||
|
@ -1,12 +1,12 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use appflowy_integrate::{RocksCollabDB, YrsDocAction};
|
||||
use collab::core::collab::MutexCollab;
|
||||
use collab::core::origin::{CollabClient, CollabOrigin};
|
||||
use collab_document::document::Document;
|
||||
use collab_document::document_data::default_document_data;
|
||||
use collab_folder::core::Folder;
|
||||
|
||||
use collab_integrate::{RocksCollabDB, YrsDocAction};
|
||||
use flowy_error::{internal_error, FlowyResult};
|
||||
|
||||
use crate::migrations::migration::UserDataMigration;
|
||||
|
@ -3,7 +3,6 @@ use std::ops::{Deref, DerefMut};
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use appflowy_integrate::{PersistenceError, RocksCollabDB, YrsDocAction};
|
||||
use collab::core::collab::MutexCollab;
|
||||
use collab::core::origin::{CollabClient, CollabOrigin};
|
||||
use collab::preclude::Collab;
|
||||
@ -15,6 +14,7 @@ use collab_database::user::DatabaseWithViewsArray;
|
||||
use collab_folder::core::Folder;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
|
||||
use collab_integrate::{PersistenceError, RocksCollabDB, YrsDocAction};
|
||||
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
||||
use flowy_folder_deps::cloud::gen_view_id;
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use appflowy_integrate::RocksCollabDB;
|
||||
use chrono::NaiveDateTime;
|
||||
use diesel::{RunQueryDsl, SqliteConnection};
|
||||
|
||||
use collab_integrate::RocksCollabDB;
|
||||
use flowy_error::FlowyResult;
|
||||
use flowy_sqlite::schema::user_data_migration_records;
|
||||
use flowy_sqlite::ConnectionPool;
|
||||
|
@ -4,7 +4,6 @@ use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::{anyhow, Error};
|
||||
use appflowy_integrate::{PersistenceError, RocksCollabDB, YrsDocAction};
|
||||
use collab::core::collab::MutexCollab;
|
||||
use collab::preclude::Collab;
|
||||
use collab_database::database::get_database_row_ids;
|
||||
@ -14,6 +13,7 @@ use collab_define::{CollabObject, CollabType};
|
||||
use collab_folder::core::{Folder, View, ViewLayout};
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use collab_integrate::{PersistenceError, RocksCollabDB, YrsDocAction};
|
||||
use flowy_error::FlowyResult;
|
||||
use flowy_user_deps::cloud::UserCloudService;
|
||||
|
||||
@ -22,16 +22,27 @@ use crate::migrations::MigrationUser;
|
||||
#[tracing::instrument(level = "info", skip_all, err)]
|
||||
pub async fn sync_user_data_to_cloud(
|
||||
user_service: Arc<dyn UserCloudService>,
|
||||
device_id: &str,
|
||||
new_user: &MigrationUser,
|
||||
collab_db: &Arc<RocksCollabDB>,
|
||||
) -> FlowyResult<()> {
|
||||
let workspace_id = new_user.session.user_workspace.id.clone();
|
||||
let uid = new_user.session.user_id;
|
||||
let folder = Arc::new(sync_folder(uid, &workspace_id, collab_db, user_service.clone()).await?);
|
||||
let folder = Arc::new(
|
||||
sync_folder(
|
||||
uid,
|
||||
&workspace_id,
|
||||
device_id,
|
||||
collab_db,
|
||||
user_service.clone(),
|
||||
)
|
||||
.await?,
|
||||
);
|
||||
|
||||
let database_records = sync_database_views(
|
||||
uid,
|
||||
&workspace_id,
|
||||
device_id,
|
||||
&new_user.session.user_workspace.database_views_aggregate_id,
|
||||
collab_db,
|
||||
user_service.clone(),
|
||||
@ -46,6 +57,7 @@ pub async fn sync_user_data_to_cloud(
|
||||
folder.clone(),
|
||||
database_records.clone(),
|
||||
workspace_id.to_string(),
|
||||
device_id.to_string(),
|
||||
view,
|
||||
collab_db.clone(),
|
||||
user_service.clone(),
|
||||
@ -63,6 +75,7 @@ fn sync_views(
|
||||
folder: Arc<MutexFolder>,
|
||||
database_records: Vec<Arc<DatabaseWithViews>>,
|
||||
workspace_id: String,
|
||||
device_id: String,
|
||||
view: Arc<View>,
|
||||
collab_db: Arc<RocksCollabDB>,
|
||||
user_service: Arc<dyn UserCloudService>,
|
||||
@ -77,8 +90,13 @@ fn sync_views(
|
||||
object_id
|
||||
);
|
||||
|
||||
let collab_object =
|
||||
CollabObject::new(uid, object_id, collab_type).with_workspace_id(workspace_id.to_string());
|
||||
let collab_object = CollabObject::new(
|
||||
uid,
|
||||
object_id,
|
||||
collab_type,
|
||||
workspace_id.to_string(),
|
||||
device_id.clone(),
|
||||
);
|
||||
|
||||
match view.layout {
|
||||
ViewLayout::Document => {
|
||||
@ -108,8 +126,13 @@ fn sync_views(
|
||||
tracing::debug!("sync row: {}", row_id);
|
||||
let document_id = database_row_document_id_from_row_id(&row_id);
|
||||
|
||||
let database_row_collab_object = CollabObject::new(uid, row_id, CollabType::DatabaseRow)
|
||||
.with_workspace_id(workspace_id.to_string());
|
||||
let database_row_collab_object = CollabObject::new(
|
||||
uid,
|
||||
row_id,
|
||||
CollabType::DatabaseRow,
|
||||
workspace_id.to_string(),
|
||||
device_id.clone(),
|
||||
);
|
||||
let database_row_update =
|
||||
get_collab_init_update(uid, &database_row_collab_object, &collab_db)?;
|
||||
tracing::info!(
|
||||
@ -122,8 +145,13 @@ fn sync_views(
|
||||
.create_collab_object(&database_row_collab_object, database_row_update)
|
||||
.await;
|
||||
|
||||
let database_row_document = CollabObject::new(uid, document_id, CollabType::Document)
|
||||
.with_workspace_id(workspace_id.to_string());
|
||||
let database_row_document = CollabObject::new(
|
||||
uid,
|
||||
document_id,
|
||||
CollabType::Document,
|
||||
workspace_id.to_string(),
|
||||
device_id.to_string(),
|
||||
);
|
||||
// sync document in the row if exist
|
||||
if let Ok(document_update) =
|
||||
get_collab_init_update(uid, &database_row_document, &collab_db)
|
||||
@ -149,6 +177,7 @@ fn sync_views(
|
||||
folder.clone(),
|
||||
database_records.clone(),
|
||||
workspace_id.clone(),
|
||||
device_id.to_string(),
|
||||
child_view,
|
||||
collab_db.clone(),
|
||||
user_service.clone(),
|
||||
@ -210,6 +239,7 @@ fn get_database_init_update(
|
||||
async fn sync_folder(
|
||||
uid: i64,
|
||||
workspace_id: &str,
|
||||
device_id: &str,
|
||||
collab_db: &Arc<RocksCollabDB>,
|
||||
user_service: Arc<dyn UserCloudService>,
|
||||
) -> Result<MutexFolder, Error> {
|
||||
@ -231,8 +261,13 @@ async fn sync_folder(
|
||||
)
|
||||
};
|
||||
|
||||
let collab_object = CollabObject::new(uid, workspace_id.to_string(), CollabType::Folder)
|
||||
.with_workspace_id(workspace_id.to_string());
|
||||
let collab_object = CollabObject::new(
|
||||
uid,
|
||||
workspace_id.to_string(),
|
||||
CollabType::Folder,
|
||||
workspace_id.to_string(),
|
||||
device_id.to_string(),
|
||||
);
|
||||
tracing::info!(
|
||||
"sync object: {} with update: {}",
|
||||
collab_object,
|
||||
@ -251,6 +286,7 @@ async fn sync_folder(
|
||||
async fn sync_database_views(
|
||||
uid: i64,
|
||||
workspace_id: &str,
|
||||
device_id: &str,
|
||||
database_views_aggregate_id: &str,
|
||||
collab_db: &Arc<RocksCollabDB>,
|
||||
user_service: Arc<dyn UserCloudService>,
|
||||
@ -259,8 +295,9 @@ async fn sync_database_views(
|
||||
uid,
|
||||
database_views_aggregate_id.to_string(),
|
||||
CollabType::WorkspaceDatabase,
|
||||
)
|
||||
.with_workspace_id(workspace_id.to_string());
|
||||
workspace_id.to_string(),
|
||||
device_id.to_string(),
|
||||
);
|
||||
|
||||
// Use the temporary result to short the lifetime of the TransactionMut
|
||||
let result = {
|
||||
|
@ -1,10 +1,10 @@
|
||||
use std::path::PathBuf;
|
||||
use std::{collections::HashMap, sync::Arc, time::Duration};
|
||||
|
||||
use appflowy_integrate::RocksCollabDB;
|
||||
use lazy_static::lazy_static;
|
||||
use parking_lot::RwLock;
|
||||
|
||||
use collab_integrate::RocksCollabDB;
|
||||
use flowy_error::{ErrorCode, FlowyError};
|
||||
use flowy_sqlite::schema::user_workspace_table;
|
||||
use flowy_sqlite::ConnectionPool;
|
||||
|
@ -1,11 +1,11 @@
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
use appflowy_integrate::RocksCollabDB;
|
||||
use collab::core::collab::{CollabRawData, MutexCollab};
|
||||
use collab_define::reminder::Reminder;
|
||||
use collab_define::CollabType;
|
||||
use collab_user::core::{MutexUserAwareness, UserAwareness};
|
||||
|
||||
use collab_integrate::RocksCollabDB;
|
||||
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
||||
|
||||
use crate::entities::ReminderPB;
|
||||
|
@ -89,10 +89,18 @@ impl UserManager {
|
||||
|
||||
/// Reset the remote workspace using local workspace data. This is useful when a user wishes to
|
||||
/// open a workspace on a new device that hasn't fully synchronized with the server.
|
||||
pub async fn reset_workspace(&self, reset: ResetWorkspacePB) -> FlowyResult<()> {
|
||||
let collab_object =
|
||||
CollabObject::new(reset.uid, reset.workspace_id.clone(), CollabType::Folder)
|
||||
.with_workspace_id(reset.workspace_id);
|
||||
pub async fn reset_workspace(
|
||||
&self,
|
||||
reset: ResetWorkspacePB,
|
||||
device_id: String,
|
||||
) -> FlowyResult<()> {
|
||||
let collab_object = CollabObject::new(
|
||||
reset.uid,
|
||||
reset.workspace_id.clone(),
|
||||
CollabType::Folder,
|
||||
reset.workspace_id.clone(),
|
||||
device_id,
|
||||
);
|
||||
self
|
||||
.cloud_services
|
||||
.get_user_service()?
|
||||
|
30
frontend/scripts/tool/update_collab_rev.sh
Executable file
30
frontend/scripts/tool/update_collab_rev.sh
Executable file
@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Ensure a new revision ID is provided
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "Usage: $0 <new_revision_id>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
NEW_REV="$1"
|
||||
echo "New revision: $NEW_REV"
|
||||
directories=("rust-lib" "appflowy_tauri/src-tauri")
|
||||
|
||||
for dir in "${directories[@]}"; do
|
||||
echo "Updating $dir"
|
||||
|
||||
cd "$dir"
|
||||
sed -i.bak "/^collab[[:alnum:]-]*[[:space:]]*=/s/rev = \"[a-fA-F0-9]\{6,40\}\"/rev = \"$NEW_REV\"/g" Cargo.toml
|
||||
|
||||
# Detect changed crates
|
||||
collab_crates=($(grep -E '^collab[a-zA-Z0-9_-]* =' Cargo.toml | awk -F'=' '{print $1}' | tr -d ' '))
|
||||
|
||||
# Update only the changed crates in Cargo.lock
|
||||
for crate in "${collab_crates[@]}"; do
|
||||
echo "Updating $crate"
|
||||
cargo update -p $crate
|
||||
done
|
||||
|
||||
cd ..
|
||||
done
|
||||
|
330
shared-lib/Cargo.lock
generated
330
shared-lib/Cargo.lock
generated
@ -46,29 +46,12 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi 0.1.19",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||
|
||||
[[package]]
|
||||
name = "basic-toml"
|
||||
version = "0.1.2"
|
||||
@ -99,15 +82,6 @@ version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
@ -133,12 +107,6 @@ version = "3.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.4.0"
|
||||
@ -327,22 +295,13 @@ version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "850878694b7933ca4c9569d30a34b55031b9b139ee1fc7b94a527c4ef960d690"
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
|
||||
dependencies = [
|
||||
"block-buffer 0.10.4",
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
@ -358,19 +317,6 @@ version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.1"
|
||||
@ -479,104 +425,12 @@ version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.4"
|
||||
@ -669,23 +523,6 @@ version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503"
|
||||
|
||||
[[package]]
|
||||
name = "humansize"
|
||||
version = "2.1.3"
|
||||
@ -695,12 +532,6 @@ dependencies = [
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.53"
|
||||
@ -725,16 +556,6 @@ dependencies = [
|
||||
"cxx-build",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
|
||||
dependencies = [
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ignore"
|
||||
version = "0.4.20"
|
||||
@ -851,32 +672,6 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lib-ws"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"dashmap",
|
||||
"env_logger",
|
||||
"futures",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"lib-infra",
|
||||
"log",
|
||||
"parking_lot",
|
||||
"pin-project",
|
||||
"protobuf",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"strum_macros",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"tracing",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.139"
|
||||
@ -981,12 +776,6 @@ version = "1.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||
|
||||
[[package]]
|
||||
name = "os_pipe"
|
||||
version = "0.9.2"
|
||||
@ -1188,12 +977,6 @@ version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.15"
|
||||
@ -1523,30 +1306,6 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_repr"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98d0516900518c29efa217c298fa1f4e6c6ffc85ae29fd7f4ee48f176e1a9ed5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha-1"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6"
|
||||
dependencies = [
|
||||
"block-buffer 0.9.0",
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest 0.9.0",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.6"
|
||||
@ -1555,7 +1314,7 @@ checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest 0.10.6",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1579,12 +1338,6 @@ version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5"
|
||||
|
||||
[[package]]
|
||||
name = "slug"
|
||||
version = "0.1.4"
|
||||
@ -1734,21 +1487,6 @@ dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec_macros"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.26.0"
|
||||
@ -1780,19 +1518,6 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-tungstenite"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "511de3f85caf1c98983545490c3d09685fa8eb634e57eec22bb4db271f46cbd8"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"log",
|
||||
"pin-project",
|
||||
"tokio",
|
||||
"tungstenite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.11"
|
||||
@ -1850,25 +1575,6 @@ dependencies = [
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0b2d8558abd2e276b0a8df5c05a2ec762609344191e5fd23e292c910e9165b5"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"http",
|
||||
"httparse",
|
||||
"log",
|
||||
"rand 0.8.5",
|
||||
"sha-1",
|
||||
"thiserror",
|
||||
"url",
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.14.0"
|
||||
@ -1940,27 +1646,12 @@ dependencies = [
|
||||
"unic-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.10.1"
|
||||
@ -1973,23 +1664,6 @@ version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf-8"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.3"
|
||||
|
@ -1,7 +1,6 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"lib-ot",
|
||||
"lib-ws",
|
||||
"lib-infra",
|
||||
"flowy-derive",
|
||||
"flowy-ast",
|
||||
|
@ -6,7 +6,7 @@ edition = "2018"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "0.4.22", default-features = false, features = ["clock"] }
|
||||
chrono = { version = "0.4.27", default-features = false, features = ["clock"] }
|
||||
bytes = { version = "1.4" }
|
||||
pin-project = "1.0.12"
|
||||
futures-core = { version = "0.3" }
|
||||
|
@ -1,32 +0,0 @@
|
||||
[package]
|
||||
name = "lib-ws"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
serde_repr = "0.1"
|
||||
serde = "1.0"
|
||||
serde_json = {version = "1.0"}
|
||||
lib-infra = { path = "../lib-infra" }
|
||||
|
||||
tokio-tungstenite = "0.15"
|
||||
futures-util = "0.3.26"
|
||||
futures-channel = "0.3.26"
|
||||
tokio = { version = "1.26", features = ["full"]}
|
||||
futures = "0.3.26"
|
||||
bytes = "1.4"
|
||||
pin-project = "1.0"
|
||||
futures-core = { version = "0.3", default-features = false }
|
||||
url = "2.3.1"
|
||||
log = "0.4"
|
||||
tracing = { version = "0.1", features = ["log"] }
|
||||
protobuf = {version = "2.28.0"}
|
||||
strum_macros = "0.21"
|
||||
parking_lot = "0.12.1"
|
||||
dashmap = "5"
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1.26", features = ["full"]}
|
||||
env_logger = "0.8.4"
|
@ -1,223 +0,0 @@
|
||||
#![allow(clippy::all)]
|
||||
use crate::{
|
||||
errors::{internal_error, WSError},
|
||||
MsgReceiver, MsgSender,
|
||||
};
|
||||
use futures_core::{future::BoxFuture, ready};
|
||||
use futures_util::{FutureExt, StreamExt};
|
||||
use pin_project::pin_project;
|
||||
use std::{
|
||||
fmt,
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio_tungstenite::{
|
||||
connect_async,
|
||||
tungstenite::{handshake::client::Response, Error, Message},
|
||||
MaybeTlsStream, WebSocketStream,
|
||||
};
|
||||
|
||||
type WsConnectResult = Result<(WebSocketStream<MaybeTlsStream<TcpStream>>, Response), Error>;
|
||||
|
||||
#[pin_project]
|
||||
pub struct WSConnectionFuture {
|
||||
msg_tx: Option<MsgSender>,
|
||||
ws_rx: Option<MsgReceiver>,
|
||||
#[pin]
|
||||
fut: Pin<Box<dyn Future<Output = WsConnectResult> + Send + Sync>>,
|
||||
}
|
||||
|
||||
impl WSConnectionFuture {
|
||||
pub fn new(msg_tx: MsgSender, ws_rx: MsgReceiver, addr: String) -> Self {
|
||||
WSConnectionFuture {
|
||||
msg_tx: Some(msg_tx),
|
||||
ws_rx: Some(ws_rx),
|
||||
fut: Box::pin(async move { connect_async(&addr).await }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Future for WSConnectionFuture {
|
||||
type Output = Result<WSStream, WSError>;
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
// [[pin]]
|
||||
// poll async function. The following methods not work.
|
||||
// 1.
|
||||
// let f = connect_async("");
|
||||
// pin_mut!(f);
|
||||
// ready!(Pin::new(&mut a).poll(cx))
|
||||
//
|
||||
// 2.ready!(Pin::new(&mut Box::pin(connect_async(""))).poll(cx))
|
||||
//
|
||||
// An async method calls poll multiple times and might return to the executor. A
|
||||
// single poll call can only return to the executor once and will get
|
||||
// resumed through another poll invocation. the connect_async call multiple time
|
||||
// from the beginning. So I use fut to hold the future and continue to
|
||||
// poll it. (Fix me if i was wrong)
|
||||
loop {
|
||||
return match ready!(self.as_mut().project().fut.poll(cx)) {
|
||||
Ok((stream, _)) => {
|
||||
tracing::debug!("[WebSocket]: connect success");
|
||||
let (msg_tx, ws_rx) = (
|
||||
self
|
||||
.msg_tx
|
||||
.take()
|
||||
.expect("[WebSocket]: WSConnection should be call once "),
|
||||
self
|
||||
.ws_rx
|
||||
.take()
|
||||
.expect("[WebSocket]: WSConnection should be call once "),
|
||||
);
|
||||
Poll::Ready(Ok(WSStream::new(msg_tx, ws_rx, stream)))
|
||||
},
|
||||
Err(error) => {
|
||||
tracing::debug!("[WebSocket]: ❌ connect failed: {:?}", error);
|
||||
Poll::Ready(Err(error.into()))
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Fut = BoxFuture<'static, Result<(), WSError>>;
|
||||
#[pin_project]
|
||||
pub struct WSStream {
|
||||
#[allow(dead_code)]
|
||||
msg_tx: MsgSender,
|
||||
#[pin]
|
||||
inner: Option<(Fut, Fut)>,
|
||||
}
|
||||
|
||||
impl WSStream {
|
||||
pub fn new(
|
||||
msg_tx: MsgSender,
|
||||
ws_rx: MsgReceiver,
|
||||
stream: WebSocketStream<MaybeTlsStream<TcpStream>>,
|
||||
) -> Self {
|
||||
let (ws_write, ws_read) = stream.split();
|
||||
Self {
|
||||
msg_tx: msg_tx.clone(),
|
||||
inner: Some((
|
||||
Box::pin(async move {
|
||||
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
let read = async {
|
||||
ws_read
|
||||
.for_each(|message| async {
|
||||
match tx.send(send_message(msg_tx.clone(), message)) {
|
||||
Ok(_) => {},
|
||||
Err(e) => log::error!("[WebSocket]: WSStream sender closed unexpectedly: {} ", e),
|
||||
}
|
||||
})
|
||||
.await;
|
||||
Ok(())
|
||||
};
|
||||
|
||||
let read_ret = async {
|
||||
loop {
|
||||
match rx.recv().await {
|
||||
None => {
|
||||
return Err(
|
||||
WSError::internal()
|
||||
.context("[WebSocket]: WSStream receiver closed unexpectedly"),
|
||||
);
|
||||
},
|
||||
Some(result) => {
|
||||
if result.is_err() {
|
||||
return result;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
futures::pin_mut!(read);
|
||||
futures::pin_mut!(read_ret);
|
||||
return tokio::select! {
|
||||
result = read => result,
|
||||
result = read_ret => result,
|
||||
};
|
||||
}),
|
||||
Box::pin(async move {
|
||||
let result = ws_rx
|
||||
.map(Ok)
|
||||
.forward(ws_write)
|
||||
.await
|
||||
.map_err(internal_error);
|
||||
result
|
||||
}),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for WSStream {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("WSStream").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Future for WSStream {
|
||||
type Output = Result<(), WSError>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let (mut ws_read, mut ws_write) = self.inner.take().unwrap();
|
||||
match ws_read.poll_unpin(cx) {
|
||||
Poll::Ready(l) => Poll::Ready(l),
|
||||
Poll::Pending => {
|
||||
//
|
||||
match ws_write.poll_unpin(cx) {
|
||||
Poll::Ready(r) => Poll::Ready(r),
|
||||
Poll::Pending => {
|
||||
self.inner = Some((ws_read, ws_write));
|
||||
Poll::Pending
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn send_message(msg_tx: MsgSender, message: Result<Message, Error>) -> Result<(), WSError> {
|
||||
match message {
|
||||
Ok(Message::Binary(bytes)) => msg_tx
|
||||
.unbounded_send(Message::Binary(bytes))
|
||||
.map_err(internal_error),
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => Err(WSError::internal().context(e)),
|
||||
}
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub struct Retry<F> {
|
||||
f: F,
|
||||
#[allow(dead_code)]
|
||||
retry_time: usize,
|
||||
addr: String,
|
||||
}
|
||||
|
||||
impl<F> Retry<F>
|
||||
where
|
||||
F: Fn(&str),
|
||||
{
|
||||
#[allow(dead_code)]
|
||||
pub fn new(addr: &str, f: F) -> Self {
|
||||
Self {
|
||||
f,
|
||||
retry_time: 3,
|
||||
addr: addr.to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> Future for Retry<F>
|
||||
where
|
||||
F: Fn(&str),
|
||||
{
|
||||
type Output = ();
|
||||
|
||||
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
(self.f)(&self.addr);
|
||||
|
||||
Poll::Ready(())
|
||||
}
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
use futures_channel::mpsc::TrySendError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_repr::*;
|
||||
use std::fmt::Debug;
|
||||
use strum_macros::Display;
|
||||
use tokio::sync::oneshot::error::RecvError;
|
||||
use tokio_tungstenite::tungstenite::{http::StatusCode, Message};
|
||||
use url::ParseError;
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
pub struct WSError {
|
||||
pub code: ErrorCode,
|
||||
pub msg: String,
|
||||
}
|
||||
|
||||
macro_rules! static_ws_error {
|
||||
($name:ident, $status:expr) => {
|
||||
#[allow(non_snake_case, missing_docs)]
|
||||
pub fn $name() -> WSError {
|
||||
WSError {
|
||||
code: $status,
|
||||
msg: format!("{}", $status),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl WSError {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn new(code: ErrorCode) -> WSError {
|
||||
WSError {
|
||||
code,
|
||||
msg: "".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn context<T: Debug>(mut self, error: T) -> Self {
|
||||
self.msg = format!("{:?}", error);
|
||||
self
|
||||
}
|
||||
|
||||
static_ws_error!(internal, ErrorCode::InternalError);
|
||||
static_ws_error!(unsupported_message, ErrorCode::UnsupportedMessage);
|
||||
static_ws_error!(unauthorized, ErrorCode::Unauthorized);
|
||||
}
|
||||
|
||||
pub(crate) fn internal_error<T>(e: T) -> WSError
|
||||
where
|
||||
T: std::fmt::Debug,
|
||||
{
|
||||
WSError::internal().context(e)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize_repr, Deserialize_repr, Display, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
#[derive(Default)]
|
||||
pub enum ErrorCode {
|
||||
#[default]
|
||||
InternalError = 0,
|
||||
UnsupportedMessage = 1,
|
||||
Unauthorized = 2,
|
||||
}
|
||||
|
||||
impl std::convert::From<url::ParseError> for WSError {
|
||||
fn from(error: ParseError) -> Self {
|
||||
WSError::internal().context(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<protobuf::ProtobufError> for WSError {
|
||||
fn from(error: protobuf::ProtobufError) -> Self {
|
||||
WSError::internal().context(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<futures_channel::mpsc::TrySendError<Message>> for WSError {
|
||||
fn from(error: TrySendError<Message>) -> Self {
|
||||
WSError::internal().context(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<RecvError> for WSError {
|
||||
fn from(error: RecvError) -> Self {
|
||||
WSError::internal().context(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<tokio_tungstenite::tungstenite::Error> for WSError {
|
||||
fn from(error: tokio_tungstenite::tungstenite::Error) -> Self {
|
||||
match error {
|
||||
tokio_tungstenite::tungstenite::Error::Http(response) => {
|
||||
if response.status() == StatusCode::UNAUTHORIZED {
|
||||
WSError::unauthorized()
|
||||
} else {
|
||||
WSError::internal().context(response)
|
||||
}
|
||||
},
|
||||
_ => WSError::internal().context(error),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
pub mod connect;
|
||||
pub mod errors;
|
||||
mod msg;
|
||||
mod ws;
|
||||
|
||||
pub use msg::*;
|
||||
pub use ws::*;
|
@ -1,46 +0,0 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_repr::*;
|
||||
use tokio_tungstenite::tungstenite::Message as TokioMessage;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct WebSocketRawMessage {
|
||||
pub channel: WSChannel,
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl WebSocketRawMessage {
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
serde_json::to_vec(&self).unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn from_bytes<T: AsRef<[u8]>>(bytes: T) -> Self {
|
||||
serde_json::from_slice(bytes.as_ref()).unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
// The lib-ws crate should not contain business logic.So WSChannel should be removed into another place.
|
||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Eq, PartialEq, Hash)]
|
||||
#[repr(u8)]
|
||||
#[derive(Default)]
|
||||
pub enum WSChannel {
|
||||
#[default]
|
||||
Document = 0,
|
||||
Folder = 1,
|
||||
Database = 2,
|
||||
}
|
||||
|
||||
impl ToString for WSChannel {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
WSChannel::Document => "0".to_string(),
|
||||
WSChannel::Folder => "1".to_string(),
|
||||
WSChannel::Database => "2".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<WebSocketRawMessage> for TokioMessage {
|
||||
fn from(msg: WebSocketRawMessage) -> Self {
|
||||
TokioMessage::Binary(msg.to_bytes())
|
||||
}
|
||||
}
|
@ -1,435 +0,0 @@
|
||||
#![allow(clippy::type_complexity)]
|
||||
use crate::{
|
||||
connect::{WSConnectionFuture, WSStream},
|
||||
errors::WSError,
|
||||
WSChannel, WebSocketRawMessage,
|
||||
};
|
||||
use dashmap::DashMap;
|
||||
use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
|
||||
use futures_core::{ready, Stream};
|
||||
use lib_infra::retry::{Action, FixedInterval, Retry};
|
||||
use pin_project::pin_project;
|
||||
use std::{
|
||||
fmt::Formatter,
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
sync::Arc,
|
||||
task::{Context, Poll},
|
||||
time::Duration,
|
||||
};
|
||||
use tokio::sync::{broadcast, oneshot, RwLock};
|
||||
use tokio_tungstenite::tungstenite::{
|
||||
protocol::{frame::coding::CloseCode, CloseFrame},
|
||||
Message,
|
||||
};
|
||||
|
||||
pub type MsgReceiver = UnboundedReceiver<Message>;
|
||||
pub type MsgSender = UnboundedSender<Message>;
|
||||
type Handlers = DashMap<WSChannel, Arc<dyn WSMessageReceiver>>;
|
||||
|
||||
pub trait WSMessageReceiver: Sync + Send + 'static {
|
||||
fn source(&self) -> WSChannel;
|
||||
fn receive_message(&self, msg: WebSocketRawMessage);
|
||||
}
|
||||
|
||||
pub struct WSController {
|
||||
handlers: Handlers,
|
||||
addr: Arc<RwLock<Option<String>>>,
|
||||
sender: Arc<RwLock<Option<Arc<WSSender>>>>,
|
||||
conn_state_notify: Arc<RwLock<WSConnectStateNotifier>>,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for WSController {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("WebSocket")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::default::Default for WSController {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
handlers: DashMap::new(),
|
||||
addr: Arc::new(RwLock::new(None)),
|
||||
sender: Arc::new(RwLock::new(None)),
|
||||
conn_state_notify: Arc::new(RwLock::new(WSConnectStateNotifier::default())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WSController {
|
||||
pub fn new() -> Self {
|
||||
WSController::default()
|
||||
}
|
||||
|
||||
pub fn add_ws_message_receiver(
|
||||
&self,
|
||||
handler: Arc<dyn WSMessageReceiver>,
|
||||
) -> Result<(), WSError> {
|
||||
let source = handler.source();
|
||||
if self.handlers.contains_key(&source) {
|
||||
log::error!("{:?} is already registered", source);
|
||||
}
|
||||
self.handlers.insert(source, handler);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn start(&self, addr: String) -> Result<(), WSError> {
|
||||
*self.addr.write().await = Some(addr.clone());
|
||||
let strategy = FixedInterval::from_millis(5000).take(3);
|
||||
self.connect(addr, strategy).await
|
||||
}
|
||||
|
||||
pub async fn stop(&self) {
|
||||
if self
|
||||
.conn_state_notify
|
||||
.read()
|
||||
.await
|
||||
.conn_state
|
||||
.is_connected()
|
||||
{
|
||||
tracing::trace!("[{}] stop", self);
|
||||
self
|
||||
.conn_state_notify
|
||||
.write()
|
||||
.await
|
||||
.update_state(WSConnectState::Disconnected);
|
||||
}
|
||||
}
|
||||
|
||||
async fn connect<T, I>(&self, addr: String, strategy: T) -> Result<(), WSError>
|
||||
where
|
||||
T: IntoIterator<IntoIter = I, Item = Duration>,
|
||||
I: Iterator<Item = Duration> + Send + 'static,
|
||||
{
|
||||
let mut conn_state_notify = self.conn_state_notify.write().await;
|
||||
let conn_state = conn_state_notify.conn_state.clone();
|
||||
if conn_state.is_connected() || conn_state.is_connecting() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let (ret, rx) = oneshot::channel::<Result<(), WSError>>();
|
||||
*self.addr.write().await = Some(addr.clone());
|
||||
let action = WSConnectAction {
|
||||
addr,
|
||||
handlers: self.handlers.clone(),
|
||||
};
|
||||
let retry = Retry::new(strategy, action);
|
||||
conn_state_notify.update_state(WSConnectState::Connecting);
|
||||
drop(conn_state_notify);
|
||||
|
||||
let cloned_conn_state = self.conn_state_notify.clone();
|
||||
let cloned_sender = self.sender.clone();
|
||||
tracing::trace!("[{}] start connecting", self);
|
||||
tokio::spawn(async move {
|
||||
match retry.await {
|
||||
Ok(result) => {
|
||||
let WSConnectResult {
|
||||
stream,
|
||||
handlers_fut,
|
||||
sender,
|
||||
} = result;
|
||||
|
||||
cloned_conn_state
|
||||
.write()
|
||||
.await
|
||||
.update_state(WSConnectState::Connected);
|
||||
*cloned_sender.write().await = Some(Arc::new(sender));
|
||||
|
||||
let _ = ret.send(Ok(()));
|
||||
spawn_stream_and_handlers(stream, handlers_fut).await;
|
||||
},
|
||||
Err(e) => {
|
||||
cloned_conn_state
|
||||
.write()
|
||||
.await
|
||||
.update_state(WSConnectState::Disconnected);
|
||||
let _ = ret.send(Err(WSError::internal().context(e)));
|
||||
},
|
||||
}
|
||||
});
|
||||
rx.await?
|
||||
}
|
||||
|
||||
pub async fn retry(&self, count: usize) -> Result<(), WSError> {
|
||||
if !self
|
||||
.conn_state_notify
|
||||
.read()
|
||||
.await
|
||||
.conn_state
|
||||
.is_disconnected()
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
tracing::trace!("[WebSocket]: retry connect...");
|
||||
let strategy = FixedInterval::from_millis(5000).take(count);
|
||||
let addr = self
|
||||
.addr
|
||||
.read()
|
||||
.await
|
||||
.as_ref()
|
||||
.expect("Retry web socket connection failed, should call start_connect first")
|
||||
.clone();
|
||||
|
||||
self.connect(addr, strategy).await
|
||||
}
|
||||
|
||||
pub async fn subscribe_state(&self) -> broadcast::Receiver<WSConnectState> {
|
||||
self.conn_state_notify.read().await.notify.subscribe()
|
||||
}
|
||||
|
||||
pub async fn ws_message_sender(&self) -> Result<Option<Arc<WSSender>>, WSError> {
|
||||
let sender = self.sender.read().await.clone();
|
||||
match sender {
|
||||
None => match self.conn_state_notify.read().await.conn_state {
|
||||
WSConnectState::Disconnected => {
|
||||
let msg = "WebSocket is disconnected";
|
||||
Err(WSError::internal().context(msg))
|
||||
},
|
||||
_ => Ok(None),
|
||||
},
|
||||
Some(sender) => Ok(Some(sender)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn spawn_stream_and_handlers(stream: WSStream, handlers: WSHandlerFuture) {
|
||||
tokio::select! {
|
||||
result = stream => {
|
||||
if let Err(e) = result {
|
||||
tracing::error!("WSStream error: {:?}", e);
|
||||
}
|
||||
},
|
||||
result = handlers => tracing::debug!("handlers completed {:?}", result),
|
||||
};
|
||||
}
|
||||
|
||||
#[pin_project]
|
||||
pub struct WSHandlerFuture {
|
||||
#[pin]
|
||||
msg_rx: MsgReceiver,
|
||||
handlers: Handlers,
|
||||
}
|
||||
|
||||
impl WSHandlerFuture {
|
||||
fn new(handlers: Handlers, msg_rx: MsgReceiver) -> Self {
|
||||
Self { msg_rx, handlers }
|
||||
}
|
||||
|
||||
fn handler_ws_message(&self, message: Message) {
|
||||
if let Message::Binary(bytes) = message {
|
||||
self.handle_binary_message(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_binary_message(&self, bytes: Vec<u8>) {
|
||||
let msg = WebSocketRawMessage::from_bytes(bytes);
|
||||
match self.handlers.get(&msg.channel) {
|
||||
None => log::error!("Can't find any handler for message: {:?}", msg),
|
||||
Some(handler) => handler.receive_message(msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Future for WSHandlerFuture {
|
||||
type Output = ();
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
loop {
|
||||
match ready!(self.as_mut().project().msg_rx.poll_next(cx)) {
|
||||
None => {
|
||||
return Poll::Ready(());
|
||||
},
|
||||
Some(message) => self.handler_ws_message(message),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WSSender(MsgSender);
|
||||
|
||||
impl WSSender {
|
||||
pub fn send_msg<T: Into<WebSocketRawMessage>>(&self, msg: T) -> Result<(), WSError> {
|
||||
let msg = msg.into();
|
||||
self
|
||||
.0
|
||||
.unbounded_send(msg.into())
|
||||
.map_err(|e| WSError::internal().context(e))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn send_text(&self, source: &WSChannel, text: &str) -> Result<(), WSError> {
|
||||
let msg = WebSocketRawMessage {
|
||||
channel: source.clone(),
|
||||
data: text.as_bytes().to_vec(),
|
||||
};
|
||||
self.send_msg(msg)
|
||||
}
|
||||
|
||||
pub fn send_binary(&self, source: &WSChannel, bytes: Vec<u8>) -> Result<(), WSError> {
|
||||
let msg = WebSocketRawMessage {
|
||||
channel: source.clone(),
|
||||
data: bytes,
|
||||
};
|
||||
self.send_msg(msg)
|
||||
}
|
||||
|
||||
pub fn send_disconnect(&self, reason: &str) -> Result<(), WSError> {
|
||||
let frame = CloseFrame {
|
||||
code: CloseCode::Normal,
|
||||
reason: reason.to_owned().into(),
|
||||
};
|
||||
let msg = Message::Close(Some(frame));
|
||||
self
|
||||
.0
|
||||
.unbounded_send(msg)
|
||||
.map_err(|e| WSError::internal().context(e))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct WSConnectAction {
|
||||
addr: String,
|
||||
handlers: Handlers,
|
||||
}
|
||||
|
||||
impl Action for WSConnectAction {
|
||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Item, Self::Error>> + Send + Sync>>;
|
||||
type Item = WSConnectResult;
|
||||
type Error = WSError;
|
||||
|
||||
fn run(&mut self) -> Self::Future {
|
||||
let addr = self.addr.clone();
|
||||
let handlers = self.handlers.clone();
|
||||
Box::pin(WSConnectActionFut::new(addr, handlers))
|
||||
}
|
||||
}
|
||||
|
||||
struct WSConnectResult {
|
||||
stream: WSStream,
|
||||
handlers_fut: WSHandlerFuture,
|
||||
sender: WSSender,
|
||||
}
|
||||
|
||||
#[pin_project]
|
||||
struct WSConnectActionFut {
|
||||
addr: String,
|
||||
#[pin]
|
||||
conn: WSConnectionFuture,
|
||||
handlers_fut: Option<WSHandlerFuture>,
|
||||
sender: Option<WSSender>,
|
||||
}
|
||||
|
||||
impl WSConnectActionFut {
|
||||
fn new(addr: String, handlers: Handlers) -> Self {
|
||||
// Stream User
|
||||
// ┌───────────────┐ ┌──────────────┐
|
||||
// ┌──────┐ │ ┌─────────┐ │ ┌────────┐ │ ┌────────┐ │
|
||||
// │Server│──────┼─▶│ ws_read │──┼───▶│ msg_tx │───┼─▶│ msg_rx │ │
|
||||
// └──────┘ │ └─────────┘ │ └────────┘ │ └────────┘ │
|
||||
// ▲ │ │ │ │
|
||||
// │ │ ┌─────────┐ │ ┌────────┐ │ ┌────────┐ │
|
||||
// └─────────┼──│ws_write │◀─┼────│ ws_rx │◀──┼──│ ws_tx │ │
|
||||
// │ └─────────┘ │ └────────┘ │ └────────┘ │
|
||||
// └───────────────┘ └──────────────┘
|
||||
let (msg_tx, msg_rx) = futures_channel::mpsc::unbounded();
|
||||
let (ws_tx, ws_rx) = futures_channel::mpsc::unbounded();
|
||||
let sender = WSSender(ws_tx);
|
||||
let handlers_fut = WSHandlerFuture::new(handlers, msg_rx);
|
||||
let conn = WSConnectionFuture::new(msg_tx, ws_rx, addr.clone());
|
||||
Self {
|
||||
addr,
|
||||
conn,
|
||||
handlers_fut: Some(handlers_fut),
|
||||
sender: Some(sender),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Future for WSConnectActionFut {
|
||||
type Output = Result<WSConnectResult, WSError>;
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let mut this = self.project();
|
||||
match ready!(this.conn.as_mut().poll(cx)) {
|
||||
Ok(stream) => {
|
||||
let handlers_fut = this.handlers_fut.take().expect("Only take once");
|
||||
let sender = this.sender.take().expect("Only take once");
|
||||
Poll::Ready(Ok(WSConnectResult {
|
||||
stream,
|
||||
handlers_fut,
|
||||
sender,
|
||||
}))
|
||||
},
|
||||
Err(e) => Poll::Ready(Err(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub enum WSConnectState {
|
||||
Init,
|
||||
Connecting,
|
||||
Connected,
|
||||
Disconnected,
|
||||
}
|
||||
|
||||
impl WSConnectState {
|
||||
fn is_connected(&self) -> bool {
|
||||
self == &WSConnectState::Connected
|
||||
}
|
||||
|
||||
fn is_connecting(&self) -> bool {
|
||||
self == &WSConnectState::Connecting
|
||||
}
|
||||
|
||||
fn is_disconnected(&self) -> bool {
|
||||
self == &WSConnectState::Disconnected || self == &WSConnectState::Init
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for WSConnectState {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
WSConnectState::Init => f.write_str("Init"),
|
||||
WSConnectState::Connected => f.write_str("Connected"),
|
||||
WSConnectState::Connecting => f.write_str("Connecting"),
|
||||
WSConnectState::Disconnected => f.write_str("Disconnected"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for WSConnectState {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(&format!("{}", self))
|
||||
}
|
||||
}
|
||||
|
||||
struct WSConnectStateNotifier {
|
||||
conn_state: WSConnectState,
|
||||
notify: Arc<broadcast::Sender<WSConnectState>>,
|
||||
}
|
||||
|
||||
impl std::default::Default for WSConnectStateNotifier {
|
||||
fn default() -> Self {
|
||||
let (state_notify, _) = broadcast::channel(16);
|
||||
Self {
|
||||
conn_state: WSConnectState::Init,
|
||||
notify: Arc::new(state_notify),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WSConnectStateNotifier {
|
||||
fn update_state(&mut self, new_state: WSConnectState) {
|
||||
if self.conn_state == new_state {
|
||||
return;
|
||||
}
|
||||
tracing::debug!(
|
||||
"WebSocket connect state did change: {} -> {}",
|
||||
self.conn_state,
|
||||
new_state
|
||||
);
|
||||
self.conn_state = new_state.clone();
|
||||
let _ = self.notify.send(new_state);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user