feat: calling user event from web (#4535)

* refactor: user manager

* refactor: user manager

* refactor: session location

* refactor: user manager

* chore: gen ts files

* feat: implement indexeddb persistence

* chore: integrate user manager

* chore: update

* chore: run on web thread

* chore: run on web thread

* chore: fix test

* chore: add test

* chore: add test

* chore: add user & sign in with password

* chore: fix test

* chore: update docs

* chore: fix warnings

* chore: gen files

* chore: add user

* chore: add files

* chore: update config

* chore: update scirpt

* chore: update scirpt

* fix: build

* chore: update command

* fix: ci

* ci: fix

* fix: compile

* fix: compile

* fix: ci

* fix: compile

* fix: tauri build

* chore: fix test

* chore: fix test
This commit is contained in:
Nathan.fooo 2024-01-30 05:36:27 +08:00 committed by GitHub
parent 86a0569d84
commit 55c97b56a3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
164 changed files with 9334 additions and 2885 deletions

View File

@ -78,7 +78,7 @@ jobs:
af_cloud_test_base_url: http://localhost
af_cloud_test_ws_url: ws://localhost/ws
af_cloud_test_gotrue_url: http://localhost/gotrue
run: cargo test --no-default-features --features="rev-sqlite" -- --nocapture
run: cargo test --no-default-features --features="rev-sqlite,dart" -- --nocapture
- name: rustfmt rust-lib

View File

@ -79,4 +79,4 @@ jobs:
working-directory: frontend/appflowy_web
run: |
pnpm install
pnpm run wasm
pnpm run build_release_wasm

View File

@ -49,6 +49,7 @@ LIB_EXT = "a"
APP_ENVIRONMENT = "local"
FLUTTER_FLOWY_SDK_PATH = "appflowy_flutter/packages/appflowy_backend"
TAURI_BACKEND_SERVICE_PATH = "appflowy_tauri/src/services/backend"
WEB_BACKEND_SERVICE_PATH = "appflowy_web/src/services/backend"
# Test default config
TEST_CRATE_TYPE = "cdylib"
TEST_LIB_EXT = "dylib"

View File

@ -35,7 +35,7 @@
"emoji-mart": "^5.5.2",
"emoji-regex": "^10.2.1",
"events": "^3.3.0",
"google-protobuf": "^3.21.2",
"google-protobuf": "^3.15.12",
"i18next": "^22.4.10",
"i18next-browser-languagedetector": "^7.0.1",
"i18next-resources-to-backend": "^1.1.4",
@ -46,7 +46,7 @@
"lodash-es": "^4.17.21",
"nanoid": "^4.0.0",
"prismjs": "^1.29.0",
"protoc-gen-ts": "^0.8.5",
"protoc-gen-ts": "0.8.7",
"quill": "^1.3.7",
"quill-delta": "^5.1.0",
"react": "^18.2.0",
@ -80,7 +80,7 @@
"devDependencies": {
"@svgr/plugin-svgo": "^8.0.1",
"@tauri-apps/cli": "^1.5.6",
"@types/google-protobuf": "^3.15.6",
"@types/google-protobuf": "^3.15.12",
"@types/is-hotkey": "^0.1.7",
"@types/jest": "^29.5.3",
"@types/katex": "^0.16.0",

View File

@ -53,7 +53,7 @@ dependencies:
specifier: ^3.3.0
version: 3.3.0
google-protobuf:
specifier: ^3.21.2
specifier: ^3.15.12
version: 3.21.2
i18next:
specifier: ^22.4.10
@ -86,8 +86,8 @@ dependencies:
specifier: ^1.29.0
version: 1.29.0
protoc-gen-ts:
specifier: ^0.8.5
version: 0.8.6(google-protobuf@3.21.2)(typescript@4.9.5)
specifier: 0.8.7
version: 0.8.7
quill:
specifier: ^1.3.7
version: 1.3.7
@ -184,8 +184,8 @@ devDependencies:
specifier: ^1.5.6
version: 1.5.6
'@types/google-protobuf':
specifier: ^3.15.6
version: 3.15.6
specifier: ^3.15.12
version: 3.15.12
'@types/is-hotkey':
specifier: ^0.1.7
version: 0.1.7
@ -2241,8 +2241,8 @@ packages:
resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==}
dev: true
/@types/google-protobuf@3.15.6:
resolution: {integrity: sha512-pYVNNJ+winC4aek+lZp93sIKxnXt5qMkuKmaqS3WGuTq0Bw1ZDYNBgzG5kkdtwcv+GmYJGo3yEg6z2cKKAiEdw==}
/@types/google-protobuf@3.15.12:
resolution: {integrity: sha512-40um9QqwHjRS92qnOaDpL7RmDK15NuZYo9HihiJRbYkMQZlWnuH8AdvbMy8/o6lgLmKbDUKa+OALCltHdbOTpQ==}
dev: true
/@types/graceful-fs@4.1.6:
@ -5629,15 +5629,9 @@ packages:
object-assign: 4.1.1
react-is: 16.13.1
/protoc-gen-ts@0.8.6(google-protobuf@3.21.2)(typescript@4.9.5):
resolution: {integrity: sha512-66oeorGy4QBvYjQGd/gaeOYyFqKyRmRgTpofmnw8buMG0P7A0jQjoKSvKJz5h5tNUaVkIzvGBUTRVGakrhhwpA==}
/protoc-gen-ts@0.8.7:
resolution: {integrity: sha512-jr4VJey2J9LVYCV7EVyVe53g1VMw28cCmYJhBe5e3YX5wiyiDwgxWxeDf9oTqAe4P1bN/YGAkW2jhlH8LohwiQ==}
hasBin: true
peerDependencies:
google-protobuf: ^3.13.0
typescript: 4.x.x
dependencies:
google-protobuf: 3.21.2
typescript: 4.9.5
dev: false
/proxy-compare@2.5.1:
@ -6835,6 +6829,7 @@ packages:
resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==}
engines: {node: '>=4.2.0'}
hasBin: true
dev: true
/unbox-primitive@1.0.2:
resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}

View File

@ -64,6 +64,17 @@ dependencies = [
"subtle",
]
[[package]]
name = "again"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05802a5ad4d172eaf796f7047b42d0af9db513585d16d4169660a21613d34b93"
dependencies = [
"log",
"rand 0.7.3",
"wasm-timer",
]
[[package]]
name = "ahash"
version = "0.7.6"
@ -151,7 +162,7 @@ checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
[[package]]
name = "app-error"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee3abdb27a2d056e7399b486354d26e802720c00#ee3abdb27a2d056e7399b486354d26e802720c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69c69f6474eaa531fd822e9353cc5955b98e45eb#69c69f6474eaa531fd822e9353cc5955b98e45eb"
dependencies = [
"anyhow",
"bincode",
@ -171,9 +182,13 @@ name = "appflowy_tauri"
version = "0.0.0"
dependencies = [
"bytes",
"flowy-config",
"flowy-core",
"flowy-date",
"flowy-document",
"flowy-error",
"flowy-notification",
"flowy-user",
"lib-dispatch",
"serde",
"serde_json",
@ -618,9 +633,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.31"
version = "0.4.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb"
dependencies = [
"android-tzdata",
"iana-time-zone",
@ -628,7 +643,7 @@ dependencies = [
"num-traits",
"serde",
"wasm-bindgen",
"windows-targets 0.48.0",
"windows-targets 0.52.0",
]
[[package]]
@ -699,8 +714,9 @@ dependencies = [
[[package]]
name = "client-api"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee3abdb27a2d056e7399b486354d26e802720c00#ee3abdb27a2d056e7399b486354d26e802720c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69c69f6474eaa531fd822e9353cc5955b98e45eb#69c69f6474eaa531fd822e9353cc5955b98e45eb"
dependencies = [
"again",
"anyhow",
"app-error",
"async-trait",
@ -718,7 +734,7 @@ dependencies = [
"gotrue-entity",
"mime",
"mime_guess",
"parking_lot",
"parking_lot 0.12.1",
"prost",
"realtime-entity",
"realtime-protocol",
@ -727,15 +743,16 @@ dependencies = [
"serde",
"serde_json",
"serde_repr",
"shared_entity",
"shared-entity",
"thiserror",
"tokio",
"tokio-retry",
"tokio-stream",
"tokio-tungstenite",
"tracing",
"url",
"uuid",
"wasm-bindgen-futures",
"websocket",
"workspace-template",
"yrs",
]
@ -799,14 +816,14 @@ dependencies = [
[[package]]
name = "collab"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=3eef93f35f606edef2541888cb91cd6686d77225#3eef93f35f606edef2541888cb91cd6686d77225"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e3620ae820e921955b9c3ca3ffee24aad544f972#e3620ae820e921955b9c3ca3ffee24aad544f972"
dependencies = [
"anyhow",
"async-trait",
"bincode",
"bytes",
"js-sys",
"parking_lot",
"parking_lot 0.12.1",
"serde",
"serde_json",
"serde_repr",
@ -821,7 +838,7 @@ dependencies = [
[[package]]
name = "collab-database"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=3eef93f35f606edef2541888cb91cd6686d77225#3eef93f35f606edef2541888cb91cd6686d77225"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e3620ae820e921955b9c3ca3ffee24aad544f972#e3620ae820e921955b9c3ca3ffee24aad544f972"
dependencies = [
"anyhow",
"async-trait",
@ -833,7 +850,7 @@ dependencies = [
"lazy_static",
"lru",
"nanoid",
"parking_lot",
"parking_lot 0.12.1",
"serde",
"serde_json",
"serde_repr",
@ -849,13 +866,13 @@ dependencies = [
[[package]]
name = "collab-document"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=3eef93f35f606edef2541888cb91cd6686d77225#3eef93f35f606edef2541888cb91cd6686d77225"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e3620ae820e921955b9c3ca3ffee24aad544f972#e3620ae820e921955b9c3ca3ffee24aad544f972"
dependencies = [
"anyhow",
"collab",
"collab-entity",
"nanoid",
"parking_lot",
"parking_lot 0.12.1",
"serde",
"serde_json",
"thiserror",
@ -867,7 +884,7 @@ dependencies = [
[[package]]
name = "collab-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=3eef93f35f606edef2541888cb91cd6686d77225#3eef93f35f606edef2541888cb91cd6686d77225"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e3620ae820e921955b9c3ca3ffee24aad544f972#e3620ae820e921955b9c3ca3ffee24aad544f972"
dependencies = [
"anyhow",
"bytes",
@ -881,13 +898,13 @@ dependencies = [
[[package]]
name = "collab-folder"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=3eef93f35f606edef2541888cb91cd6686d77225#3eef93f35f606edef2541888cb91cd6686d77225"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e3620ae820e921955b9c3ca3ffee24aad544f972#e3620ae820e921955b9c3ca3ffee24aad544f972"
dependencies = [
"anyhow",
"chrono",
"collab",
"collab-entity",
"parking_lot",
"parking_lot 0.12.1",
"serde",
"serde_json",
"serde_repr",
@ -907,7 +924,7 @@ dependencies = [
"collab-entity",
"collab-plugins",
"lib-infra",
"parking_lot",
"parking_lot 0.12.1",
"serde",
"serde_json",
"tokio",
@ -917,7 +934,7 @@ dependencies = [
[[package]]
name = "collab-plugins"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=3eef93f35f606edef2541888cb91cd6686d77225#3eef93f35f606edef2541888cb91cd6686d77225"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e3620ae820e921955b9c3ca3ffee24aad544f972#e3620ae820e921955b9c3ca3ffee24aad544f972"
dependencies = [
"anyhow",
"async-stream",
@ -932,7 +949,7 @@ dependencies = [
"indexed_db_futures",
"js-sys",
"lazy_static",
"parking_lot",
"parking_lot 0.12.1",
"rand 0.8.5",
"rocksdb",
"serde",
@ -955,12 +972,12 @@ dependencies = [
[[package]]
name = "collab-user"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=3eef93f35f606edef2541888cb91cd6686d77225#3eef93f35f606edef2541888cb91cd6686d77225"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e3620ae820e921955b9c3ca3ffee24aad544f972#e3620ae820e921955b9c3ca3ffee24aad544f972"
dependencies = [
"anyhow",
"collab",
"collab-entity",
"parking_lot",
"parking_lot 0.12.1",
"serde",
"serde_json",
"tokio",
@ -1276,7 +1293,7 @@ dependencies = [
"hashbrown 0.12.3",
"lock_api",
"once_cell",
"parking_lot_core",
"parking_lot_core 0.9.8",
]
[[package]]
@ -1288,7 +1305,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
[[package]]
name = "database-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee3abdb27a2d056e7399b486354d26e802720c00#ee3abdb27a2d056e7399b486354d26e802720c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69c69f6474eaa531fd822e9353cc5955b98e45eb#69c69f6474eaa531fd822e9353cc5955b98e45eb"
dependencies = [
"anyhow",
"app-error",
@ -1715,7 +1732,7 @@ dependencies = [
"lib-dispatch",
"lib-infra",
"lib-log",
"parking_lot",
"parking_lot 0.12.1",
"serde",
"serde_json",
"serde_repr",
@ -1766,7 +1783,7 @@ dependencies = [
"lib-infra",
"lru",
"nanoid",
"parking_lot",
"parking_lot 0.12.1",
"protobuf",
"rayon",
"rust_decimal",
@ -1837,7 +1854,7 @@ dependencies = [
"lib-infra",
"lru",
"nanoid",
"parking_lot",
"parking_lot 0.12.1",
"protobuf",
"scraper 0.18.1",
"serde",
@ -1922,7 +1939,7 @@ dependencies = [
"lib-dispatch",
"lib-infra",
"nanoid",
"parking_lot",
"parking_lot 0.12.1",
"protobuf",
"serde_json",
"strum_macros 0.21.1",
@ -1988,7 +2005,7 @@ dependencies = [
"lib-dispatch",
"lib-infra",
"mime_guess",
"parking_lot",
"parking_lot 0.12.1",
"postgrest",
"reqwest",
"serde",
@ -2022,7 +2039,7 @@ dependencies = [
"diesel_derives",
"diesel_migrations",
"libsqlite3-sys",
"parking_lot",
"parking_lot 0.12.1",
"r2d2",
"scheduled-thread-pool",
"serde",
@ -2082,7 +2099,7 @@ dependencies = [
"lib-dispatch",
"lib-infra",
"once_cell",
"parking_lot",
"parking_lot 0.12.1",
"protobuf",
"serde",
"serde_json",
@ -2102,6 +2119,7 @@ name = "flowy-user-pub"
version = "0.1.0"
dependencies = [
"anyhow",
"base64 0.21.5",
"chrono",
"collab",
"collab-entity",
@ -2113,6 +2131,7 @@ dependencies = [
"serde_repr",
"tokio",
"tokio-stream",
"tracing",
"uuid",
]
@ -2549,7 +2568,7 @@ dependencies = [
[[package]]
name = "gotrue"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee3abdb27a2d056e7399b486354d26e802720c00#ee3abdb27a2d056e7399b486354d26e802720c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69c69f6474eaa531fd822e9353cc5955b98e45eb#69c69f6474eaa531fd822e9353cc5955b98e45eb"
dependencies = [
"anyhow",
"futures-util",
@ -2566,10 +2585,11 @@ dependencies = [
[[package]]
name = "gotrue-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee3abdb27a2d056e7399b486354d26e802720c00#ee3abdb27a2d056e7399b486354d26e802720c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69c69f6474eaa531fd822e9353cc5955b98e45eb#69c69f6474eaa531fd822e9353cc5955b98e45eb"
dependencies = [
"anyhow",
"app-error",
"chrono",
"jsonwebtoken",
"lazy_static",
"serde",
@ -3002,7 +3022,7 @@ dependencies = [
[[package]]
name = "infra"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee3abdb27a2d056e7399b486354d26e802720c00#ee3abdb27a2d056e7399b486354d26e802720c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69c69f6474eaa531fd822e9353cc5955b98e45eb#69c69f6474eaa531fd822e9353cc5955b98e45eb"
dependencies = [
"anyhow",
"reqwest",
@ -3109,9 +3129,9 @@ dependencies = [
[[package]]
name = "js-sys"
version = "0.3.64"
version = "0.3.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1"
dependencies = [
"wasm-bindgen",
]
@ -3193,7 +3213,7 @@ dependencies = [
"futures-util",
"getrandom 0.2.10",
"nanoid",
"parking_lot",
"parking_lot 0.12.1",
"pin-project",
"protobuf",
"serde",
@ -3204,6 +3224,7 @@ dependencies = [
"tracing",
"validator",
"wasm-bindgen",
"wasm-bindgen-futures",
]
[[package]]
@ -3873,6 +3894,17 @@ dependencies = [
"system-deps 6.1.1",
]
[[package]]
name = "parking_lot"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
dependencies = [
"instant",
"lock_api",
"parking_lot_core 0.8.6",
]
[[package]]
name = "parking_lot"
version = "0.12.1"
@ -3880,7 +3912,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core",
"parking_lot_core 0.9.8",
]
[[package]]
name = "parking_lot_core"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
dependencies = [
"cfg-if",
"instant",
"libc",
"redox_syscall 0.2.16",
"smallvec",
"winapi",
]
[[package]]
@ -4532,7 +4578,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93"
dependencies = [
"log",
"parking_lot",
"parking_lot 0.12.1",
"scheduled-thread-pool",
]
@ -4654,7 +4700,7 @@ dependencies = [
[[package]]
name = "realtime-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee3abdb27a2d056e7399b486354d26e802720c00#ee3abdb27a2d056e7399b486354d26e802720c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69c69f6474eaa531fd822e9353cc5955b98e45eb#69c69f6474eaa531fd822e9353cc5955b98e45eb"
dependencies = [
"anyhow",
"bincode",
@ -4670,13 +4716,14 @@ dependencies = [
"serde_json",
"thiserror",
"tokio-tungstenite",
"websocket",
"yrs",
]
[[package]]
name = "realtime-protocol"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee3abdb27a2d056e7399b486354d26e802720c00#ee3abdb27a2d056e7399b486354d26e802720c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69c69f6474eaa531fd822e9353cc5955b98e45eb#69c69f6474eaa531fd822e9353cc5955b98e45eb"
dependencies = [
"anyhow",
"bincode",
@ -5017,7 +5064,7 @@ version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19"
dependencies = [
"parking_lot",
"parking_lot 0.12.1",
]
[[package]]
@ -5322,9 +5369,9 @@ dependencies = [
]
[[package]]
name = "shared_entity"
name = "shared-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee3abdb27a2d056e7399b486354d26e802720c00#ee3abdb27a2d056e7399b486354d26e802720c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69c69f6474eaa531fd822e9353cc5955b98e45eb#69c69f6474eaa531fd822e9353cc5955b98e45eb"
dependencies = [
"anyhow",
"app-error",
@ -5507,7 +5554,7 @@ checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b"
dependencies = [
"new_debug_unreachable",
"once_cell",
"parking_lot",
"parking_lot 0.12.1",
"phf_shared 0.10.0",
"precomputed-hash",
"serde",
@ -5671,7 +5718,7 @@ dependencies = [
"ndk-sys",
"objc",
"once_cell",
"parking_lot",
"parking_lot 0.12.1",
"png",
"raw-window-handle",
"scopeguard",
@ -6061,7 +6108,7 @@ dependencies = [
"libc",
"mio",
"num_cpus",
"parking_lot",
"parking_lot 0.12.1",
"pin-project-lite",
"signal-hook-registry",
"socket2 0.5.5",
@ -6646,9 +6693,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.89"
version = "0.2.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e"
checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@ -6656,9 +6703,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.89"
version = "0.2.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826"
checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd"
dependencies = [
"bumpalo",
"log",
@ -6671,9 +6718,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.37"
version = "0.4.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03"
checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461"
dependencies = [
"cfg-if",
"js-sys",
@ -6683,9 +6730,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.89"
version = "0.2.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2"
checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@ -6693,9 +6740,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.89"
version = "0.2.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7"
dependencies = [
"proc-macro2",
"quote",
@ -6706,9 +6753,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.89"
version = "0.2.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f"
checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b"
[[package]]
name = "wasm-streams"
@ -6723,6 +6770,21 @@ dependencies = [
"web-sys",
]
[[package]]
name = "wasm-timer"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f"
dependencies = [
"futures",
"js-sys",
"parking_lot 0.11.2",
"pin-utils",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]]
name = "web-sys"
version = "0.3.64"
@ -6786,6 +6848,23 @@ version = "0.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc"
[[package]]
name = "websocket"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69c69f6474eaa531fd822e9353cc5955b98e45eb#69c69f6474eaa531fd822e9353cc5955b98e45eb"
dependencies = [
"futures-channel",
"futures-util",
"http",
"httparse",
"js-sys",
"thiserror",
"tokio",
"tokio-tungstenite",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "webview2-com"
version = "0.19.1"
@ -7172,7 +7251,7 @@ dependencies = [
[[package]]
name = "workspace-template"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee3abdb27a2d056e7399b486354d26e802720c00#ee3abdb27a2d056e7399b486354d26e802720c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69c69f6474eaa531fd822e9353cc5955b98e45eb#69c69f6474eaa531fd822e9353cc5955b98e45eb"
dependencies = [
"anyhow",
"async-trait",

View File

@ -40,8 +40,12 @@ bytes.workspace = true
tracing.workspace = true
lib-dispatch = { path = "../../rust-lib/lib-dispatch", features = ["use_serde"] }
flowy-core = { path = "../../rust-lib/flowy-core", features = ["rev-sqlite", "ts"] }
flowy-error = { path = "../../rust-lib/flowy-error", features = ["impl_from_sqlite", "impl_from_dispatch_error", "impl_from_appflowy_cloud", "impl_from_reqwest", "impl_from_serde"] }
flowy-notification = { path = "../../rust-lib/flowy-notification", features = ["ts"] }
flowy-user = { path = "../../rust-lib/flowy-user", features = ["tauri_ts"] }
flowy-config = { path = "../../rust-lib/flowy-config", features = ["tauri_ts"] }
flowy-date = { path = "../../rust-lib/flowy-date", features = ["tauri_ts"] }
flowy-error = { path = "../../rust-lib/flowy-error", features = ["impl_from_sqlite", "impl_from_dispatch_error", "impl_from_appflowy_cloud", "impl_from_reqwest", "impl_from_serde", "tauri_ts"] }
flowy-document = { path = "../../rust-lib/flowy-document", features = ["tauri_ts"] }
flowy-notification = { path = "../../rust-lib/flowy-notification", features = ["tauri_ts"] }
uuid = "1.5.0"
[features]
@ -58,7 +62,7 @@ custom-protocol = ["tauri/custom-protocol"]
# Run the script:
# scripts/tool/update_client_api_rev.sh new_rev_id
# ⚠️⚠️⚠️️
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "ee3abdb27a2d056e7399b486354d26e802720c00" }
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "69c69f6474eaa531fd822e9353cc5955b98e45eb" }
# Please use the following script to update collab.
# Working directory: frontend
#
@ -68,10 +72,10 @@ client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "ee3
# To switch to the local path, run:
# scripts/tool/update_collab_source.sh
# ⚠️⚠️⚠️️
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "3eef93f35f606edef2541888cb91cd6686d77225" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "3eef93f35f606edef2541888cb91cd6686d77225" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "3eef93f35f606edef2541888cb91cd6686d77225" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "3eef93f35f606edef2541888cb91cd6686d77225" }
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "3eef93f35f606edef2541888cb91cd6686d77225" }
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "3eef93f35f606edef2541888cb91cd6686d77225" }
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "3eef93f35f606edef2541888cb91cd6686d77225" }
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e3620ae820e921955b9c3ca3ffee24aad544f972" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e3620ae820e921955b9c3ca3ffee24aad544f972" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e3620ae820e921955b9c3ca3ffee24aad544f972" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e3620ae820e921955b9c3ca3ffee24aad544f972" }
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e3620ae820e921955b9c3ca3ffee24aad544f972" }
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e3620ae820e921955b9c3ca3ffee24aad544f972" }
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e3620ae820e921955b9c3ca3ffee24aad544f972" }

View File

@ -40,6 +40,6 @@ pub async fn invoke_request(
let request: AFPluginRequest = request.into();
let state: State<AppFlowyCore> = app_handler.state();
let dispatcher = state.inner().dispatcher();
let response = AFPluginDispatcher::async_send(dispatcher, request).await;
let response = AFPluginDispatcher::async_send(dispatcher.as_ref(), request).await;
response.into()
}

View File

@ -0,0 +1,7 @@
src/services
src/styles
node_modules/
dist/
src-tauri/
.eslintrc.cjs
tsconfig.json

View File

@ -23,4 +23,7 @@ dist-ssr
*.sln
*.sw?
appflowy-wasm/target
wasm-libs/**/target
**/src/services/backend/models/
**/src/services/backend/events/
**/src/appflowy_app/i18n/translations/

View File

@ -0,0 +1,19 @@
.DS_Store
node_modules
/build
/public
/.svelte-kit
/package
/.vscode
.env
.env.*
!.env.example
# rust and generated ts code
/src-tauri
/src/services
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

View File

@ -0,0 +1,20 @@
module.exports = {
arrowParens: 'always',
bracketSpacing: true,
endOfLine: 'lf',
htmlWhitespaceSensitivity: 'css',
insertPragma: false,
jsxBracketSameLine: false,
jsxSingleQuote: true,
printWidth: 121,
plugins: [require('prettier-plugin-tailwindcss')],
proseWrap: 'preserve',
quoteProps: 'as-needed',
requirePragma: false,
semi: true,
singleQuote: true,
tabWidth: 2,
trailingComma: 'es5',
useTabs: false,
vueIndentScriptAndStyle: false,
};

View File

@ -20,4 +20,26 @@ pnpm run build
# generate wasm
pnpm run wasm
```
## Run tests in Chrome
> Before executing the test, you need to install the [Chrome Driver](https://chromedriver.chromium.org/downloads). If
> you are using a Mac, you can easily install it using Homebrew.
>
> ```shell
> brew install chromedriver
> ```
Go to `frontend/appflowy_web/wasm-libs` and run:
```shell
wasm-pack test --chrome
```
Run tests in headless Chrome
```shell
wasm-pack test --headless --chrome
```

File diff suppressed because it is too large Load Diff

View File

@ -1,26 +0,0 @@
[package]
name = "appflowy-wasm"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
wasm-bindgen = { version = "0.2.89" }
lazy_static = "1.4.0"
lib-dispatch = { workspace = true, features = ["single_thread"] }
parking_lot.workspace = true
tracing.workspace = true
tracing-core = { version = "0.1.32" }
tracing-wasm = "0.2.1"
flowy-notification = { workspace = true, features = ["ts"] }
serde.workspace = true
[workspace.dependencies]
lib-dispatch = { path = "../../rust-lib/lib-dispatch" }
parking_lot = { version = "0.12.1" }
tracing = { version = "0.1.22" }
flowy-notification = { path = "../../rust-lib/flowy-notification" }
serde = { version = "1.0.194", features = ["derive"] }

View File

@ -1,60 +0,0 @@
use crate::notification::TSNotificationSender;
use flowy_notification::{register_notification_sender, unregister_all_notification_sender};
pub mod notification;
pub mod request;
use crate::request::{MutexDispatcher, WasmRequest};
use lazy_static::lazy_static;
use lib_dispatch::prelude::AFPluginDispatcher;
use lib_dispatch::runtime::AFPluginRuntime;
use std::sync::Arc;
use tracing::trace;
use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen::JsValue;
lazy_static! {
pub(crate) static ref DISPATCHER: MutexDispatcher = MutexDispatcher::new();
}
#[wasm_bindgen]
pub fn init_sdk(_data: String) -> i64 {
let runtime = Arc::new(AFPluginRuntime::new().unwrap());
*DISPATCHER.0.lock() = Some(Arc::new(AFPluginDispatcher::new(runtime, vec![])));
0
}
#[wasm_bindgen]
pub fn init_tracing() {
tracing_wasm::set_as_global_default();
}
#[wasm_bindgen]
pub fn async_event(name: String, payload: Vec<u8>) {
trace!("[WASM]: receives event: {}", name);
let dispatcher = DISPATCHER.0.lock().as_ref().unwrap().clone();
AFPluginDispatcher::boxed_async_send_with_callback(
dispatcher,
WasmRequest::new(name, payload),
|_| Box::pin(async {}),
);
}
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
pub fn log(s: &str);
#[wasm_bindgen(js_namespace = window)]
fn onFlowyNotify(event_name: &str, payload: JsValue);
}
#[wasm_bindgen]
pub fn register_listener() {
unregister_all_notification_sender();
register_notification_sender(TSNotificationSender::new());
}
pub fn on_event(event_name: &str, args: JsValue) {
onFlowyNotify(event_name, args);
}

View File

@ -1,64 +0,0 @@
use lib_dispatch::prelude::{AFPluginDispatcher, AFPluginRequest};
use parking_lot::Mutex;
use std::sync::Arc;
use wasm_bindgen::prelude::wasm_bindgen;
pub(crate) struct MutexDispatcher(pub Arc<Mutex<Option<Arc<AFPluginDispatcher>>>>);
impl MutexDispatcher {
pub(crate) fn new() -> Self {
Self(Arc::new(Mutex::new(None)))
}
}
unsafe impl Sync for MutexDispatcher {}
unsafe impl Send for MutexDispatcher {}
#[wasm_bindgen]
pub struct WasmRequest {
name: String,
payload: Vec<u8>,
}
impl WasmRequest {
pub fn new(name: String, payload: Vec<u8>) -> Self {
Self { name, payload }
}
}
impl From<WasmRequest> for AFPluginRequest {
fn from(request: WasmRequest) -> Self {
AFPluginRequest::new(request.name).payload(request.payload)
}
}
#[wasm_bindgen]
impl WasmRequest {
pub fn name(&self) -> String {
self.name.clone()
}
// Setter for the name
#[wasm_bindgen(setter)]
pub fn set_name(&mut self, name: String) {
self.name = name;
}
// Getter for payload that returns a pointer to the data
pub fn get_payload_ptr(&self) -> *const u8 {
self.payload.as_ptr()
}
// Getter for the length of the payload
pub fn get_payload_len(&self) -> usize {
self.payload.len()
}
// Setter or method to update payload
pub fn set_payload(&mut self, payload: &[u8]) {
self.payload = payload.to_vec();
}
}

View File

@ -4,17 +4,22 @@
"version": "0.0.0",
"type": "module",
"scripts": {
"wasm": "cd appflowy-wasm && wasm-pack build",
"dev": "pnpm run wasm && vite",
"build_release_wasm": "cd wasm-libs/af-wasm && wasm-pack build",
"build_dev_wasm": "cd wasm-libs/af-wasm && wasm-pack build --features=\"localhost_dev\"",
"dev": "pnpm run build_dev_wasm && vite",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"clean": "cargo make --cwd .. web_clean",
"preview": "vite preview"
},
"dependencies": {
"events": "^3.3.0",
"google-protobuf": "^3.21.2",
"protoc-gen-ts": "^0.8.5",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"ts-results": "^3.3.0",
"uuid": "^9.0.1"
},
"devDependencies": {
"@types/events": "^3.0.3",

View File

@ -7,12 +7,21 @@ dependencies:
google-protobuf:
specifier: ^3.21.2
version: 3.21.2
protoc-gen-ts:
specifier: ^0.8.5
version: 0.8.7
react:
specifier: ^18.2.0
version: 18.2.0
react-dom:
specifier: ^18.2.0
version: 18.2.0(react@18.2.0)
ts-results:
specifier: ^3.3.0
version: 3.3.0
uuid:
specifier: ^9.0.1
version: 9.0.1
devDependencies:
'@types/events':
@ -1669,6 +1678,11 @@ packages:
engines: {node: '>= 0.8.0'}
dev: true
/protoc-gen-ts@0.8.7:
resolution: {integrity: sha512-jr4VJey2J9LVYCV7EVyVe53g1VMw28cCmYJhBe5e3YX5wiyiDwgxWxeDf9oTqAe4P1bN/YGAkW2jhlH8LohwiQ==}
hasBin: true
dev: false
/punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
@ -1836,6 +1850,10 @@ packages:
typescript: 5.2.2
dev: true
/ts-results@3.3.0:
resolution: {integrity: sha512-FWqxGX2NHp5oCyaMd96o2y2uMQmSu8Dey6kvyuFdRJ2AzfmWo3kWa4UsPlCGlfQ/qu03m09ZZtppMoY8EMHuiA==}
dev: false
/type-check@0.4.0:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'}
@ -1875,6 +1893,11 @@ packages:
punycode: 2.3.1
dev: true
/uuid@9.0.1:
resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
hasBin: true
dev: false
/vite-plugin-wasm@3.3.0(vite@5.0.8):
resolution: {integrity: sha512-tVhz6w+W9MVsOCHzxo6SSMSswCeIw4HTrXEi6qL3IRzATl83jl09JVO1djBqPSwfjgnpVHNLYcaMbaDX5WB/pg==}
peerDependencies:

View File

@ -1,19 +1,17 @@
import reactLogo from "./assets/react.svg";
import viteLogo from "/vite.svg";
import "./App.css";
import {async_event, init_sdk, init_tracing} from "../appflowy-wasm/pkg/appflowy_wasm";
import { useEffect } from "react";
import { initApp } from "./application/init_app.ts";
import { subscribeNotification } from "./application/notification.ts";
import { initApp } from "@/application/app.ts";
import { subscribeNotification } from "@/application/notification.ts";
import { NotifyArgs } from "./@types/global";
import {init_tracing_log, init_wasm_core} from "../wasm-libs/af-wasm/pkg";
import { v4 as uuidv4 } from 'uuid';
import {AddUserPB, UserWasmEventAddUser} from "@/services/backend/events/af-user";
async function runWasm() {
init_tracing();
init_sdk("sdk config"); // Call your exported Wasm function.
}
runWasm();
init_tracing_log();
// FIXME: handle the promise that init_wasm_core returns
init_wasm_core();
function App() {
useEffect(() => {
@ -24,9 +22,13 @@ function App() {
}, []);
const handleClick = async () => {
const payload = new TextEncoder().encode("someString");
const res = await async_event("add", payload);
console.log(res);
let email = `${uuidv4()}@example.com`;
let password = "AppFlowy!2024";
const payload = AddUserPB.fromObject({email: email, password: password })
let result = await UserWasmEventAddUser(payload);
if (!result.ok) {
}
};
return (

View File

@ -0,0 +1,35 @@
import { initEventBus } from "./event_bus.ts";
import {async_event, register_listener} from "../../wasm-libs/af-wasm/pkg";
export function initApp() {
initEventBus();
register_listener();
}
type InvokeArgs = Record<string, unknown>;
export async function invoke<T>(cmd: string, args?: InvokeArgs): Promise<T> {
switch (cmd) {
case "invoke_request":
const request = args?.request as { ty?: unknown, payload?: unknown } | undefined;
if (!request || typeof request !== 'object') {
throw new Error("Invalid or missing 'request' argument in 'invoke_request'");
}
const { ty, payload } = request;
if (typeof ty !== 'string') {
throw new Error("Invalid 'ty' in request for 'invoke_request'");
}
if (!(payload instanceof Array)) {
throw new Error("Invalid 'payload' in request for 'invoke_request'");
}
return async_event(ty, new Uint8Array(payload));
default:
throw new Error(`Unknown command: ${cmd}`);
}
}

View File

@ -1,7 +0,0 @@
import { initEventBus } from "./event_bus.ts";
import { register_listener } from "../../appflowy-wasm/pkg";
export function initApp() {
initEventBus();
register_listener();
}

View File

@ -0,0 +1,2 @@
export * from "./models/af-user";
export * from "./models/flowy-error";

View File

@ -19,9 +19,13 @@
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"typeRoots": ["./node_modules/@types", "./src/@types"],
"baseUrl": "./",
"paths": {
"@/*": ["src/*"],
}
},
"include": ["src"],
"include": ["src", "vite.config.ts"],
"references": [{ "path": "./tsconfig.node.json" }]
}

View File

@ -5,4 +5,11 @@ import wasm from "vite-plugin-wasm";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), wasm()],
resolve: {
alias: [
{ find: 'src/', replacement: `${__dirname}/src/` },
{ find: '@/', replacement: `${__dirname}/src/` },
{ find: '$app/', replacement: `${__dirname}/src/application/` },
],
},
})

4892
frontend/appflowy_web/wasm-libs/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,81 @@
[workspace]
members = [
"af-wasm",
"af-user",
"af-persistence",
]
resolver = "2"
[workspace.dependencies]
af-user = { path = "af-user" }
af-persistence = { path = "af-persistence" }
lib-dispatch = { path = "../../rust-lib/lib-dispatch" }
parking_lot = { version = "0.12.1" }
tracing = { version = "0.1.22" }
serde = { version = "1.0.194", features = ["derive"] }
serde_json = "1.0"
collab-integrate = { path = "../../rust-lib/collab-integrate"}
flowy-notification = { path = "../../rust-lib/flowy-notification" }
flowy-user-pub = { path = "../../rust-lib/flowy-user-pub" }
flowy-server = { path = "../../rust-lib/flowy-server" }
flowy-server-pub = { path = "../../rust-lib/flowy-server-pub" }
flowy-error = { path = "../../rust-lib/flowy-error" }
flowy-derive = { path = "../../rust-lib/build-tool/flowy-derive" }
flowy-codegen = { path = "../../rust-lib/build-tool/flowy-codegen" }
flowy-document = { path = "../../rust-lib/flowy-document" }
lib-infra = { path = "../../rust-lib/lib-infra" }
collab = { version = "0.1.0" }
collab-entity = { version = "0.1.0" }
collab-user = { version = "0.1.0" }
bytes = { version = "1.5" }
protobuf = { version = "2.28.0" }
thiserror = "1.0"
anyhow = "1.0"
futures-util = "0.3"
uuid = { version = "1.5", features = ["serde", "v4", "v5"] }
tokio-stream = "0.1"
tokio = { version = "1.35", features = ["sync"] }
wasm-bindgen-futures = "0.4.40"
serde-wasm-bindgen = "0.4"
[profile.dev]
opt-level = 0
lto = false
codegen-units = 16
[profile.release]
lto = true
opt-level = 3
codegen-units = 1
[profile.profiling]
inherits = "release"
debug = true
codegen-units = 16
lto = false
[patch.crates-io]
# Please using the following command to update the revision id
# Current directory: frontend
# Run the script:
# scripts/tool/update_client_api_rev.sh new_rev_id
# ⚠️⚠️⚠️️
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "69c69f6474eaa531fd822e9353cc5955b98e45eb" }
# Please use the following script to update collab.
# Working directory: frontend
#
# To update the commit ID, run:
# scripts/tool/update_collab_rev.sh new_rev_id
#
# To switch to the local path, run:
# scripts/tool/update_collab_source.sh
# ⚠️⚠️⚠️️
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e3620ae820e921955b9c3ca3ffee24aad544f972" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e3620ae820e921955b9c3ca3ffee24aad544f972" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e3620ae820e921955b9c3ca3ffee24aad544f972" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e3620ae820e921955b9c3ca3ffee24aad544f972" }
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e3620ae820e921955b9c3ca3ffee24aad544f972" }
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e3620ae820e921955b9c3ca3ffee24aad544f972" }
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e3620ae820e921955b9c3ca3ffee24aad544f972" }

View File

@ -0,0 +1,22 @@
[package]
name = "af-persistence"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
indexed_db_futures = { version = "0.4" }
js-sys = "0.3"
wasm-bindgen = "0.2"
web-sys = { version = "0.3", features = ["console", "Window"] }
tokio = { version = "1.26.0", features = ["sync", "rt"] }
flowy-error.workspace = true
thiserror.workspace = true
anyhow.workspace = true
serde.workspace = true
serde_json.workspace = true
futures-util.workspace = true
wasm-bindgen-futures.workspace = true

View File

@ -0,0 +1,30 @@
use flowy_error::FlowyError;
use web_sys::DomException;
#[derive(Debug, thiserror::Error)]
pub enum PersistenceError {
#[error(transparent)]
Internal(#[from] anyhow::Error),
#[error(transparent)]
SerdeError(#[from] serde_json::Error),
#[error("{0}")]
RecordNotFound(String),
}
impl From<DomException> for PersistenceError {
fn from(value: DomException) -> Self {
PersistenceError::Internal(anyhow::anyhow!("DOMException: {:?}", value))
}
}
impl From<PersistenceError> for FlowyError {
fn from(value: PersistenceError) -> Self {
match value {
PersistenceError::Internal(value) => FlowyError::internal().with_context(value),
PersistenceError::SerdeError(value) => FlowyError::serde().with_context(value),
PersistenceError::RecordNotFound(value) => FlowyError::record_not_found().with_context(value),
}
}
}

View File

@ -0,0 +1,2 @@
pub mod error;
pub mod store;

View File

@ -0,0 +1,192 @@
use crate::error::PersistenceError;
use anyhow::anyhow;
use futures_util::future::LocalBoxFuture;
use indexed_db_futures::idb_object_store::IdbObjectStore;
use indexed_db_futures::idb_transaction::{IdbTransaction, IdbTransactionResult};
use indexed_db_futures::prelude::IdbOpenDbRequestLike;
use indexed_db_futures::{IdbDatabase, IdbQuerySource, IdbVersionChangeEvent};
use js_sys::Uint8Array;
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::future::Future;
use std::sync::Arc;
use tokio::sync::RwLock;
use wasm_bindgen::JsValue;
use web_sys::IdbTransactionMode;
pub trait IndexddbStore {
fn get<'a, T>(&'a self, key: &'a str) -> LocalBoxFuture<Result<Option<T>, PersistenceError>>
where
T: serde::de::DeserializeOwned + Sync;
fn set<'a, T>(&'a self, key: &'a str, value: T) -> LocalBoxFuture<Result<(), PersistenceError>>
where
T: serde::Serialize + Sync + 'a;
fn remove<'a>(&'a self, key: &'a str) -> LocalBoxFuture<Result<(), PersistenceError>>;
}
const APPFLOWY_STORE: &str = "appflowy_store";
pub struct AppFlowyWASMStore {
db: Arc<RwLock<IdbDatabase>>,
}
unsafe impl Send for AppFlowyWASMStore {}
unsafe impl Sync for AppFlowyWASMStore {}
impl AppFlowyWASMStore {
pub async fn new() -> Result<Self, PersistenceError> {
let mut db_req = IdbDatabase::open_u32("appflowy", 1)?;
db_req.set_on_upgrade_needed(Some(|evt: &IdbVersionChangeEvent| -> Result<(), JsValue> {
if !evt.db().object_store_names().any(|n| &n == APPFLOWY_STORE) {
evt.db().create_object_store(APPFLOWY_STORE)?;
}
Ok(())
}));
let db = Arc::new(RwLock::new(db_req.await?));
Ok(Self { db })
}
pub async fn begin_read_transaction<F, Fut>(&self, f: F) -> Result<(), PersistenceError>
where
F: FnOnce(&IndexddbStoreImpl<'_>) -> Fut,
Fut: Future<Output = Result<(), PersistenceError>>,
{
let db = self.db.read().await;
let txn = db.transaction_on_one_with_mode(APPFLOWY_STORE, IdbTransactionMode::Readonly)?;
let store = store_from_transaction(&txn)?;
let operation = IndexddbStoreImpl(store);
f(&operation).await?;
transaction_result_to_result(txn.await)?;
Ok(())
}
pub async fn begin_write_transaction<F>(&self, f: F) -> Result<(), PersistenceError>
where
F: FnOnce(IndexddbStoreImpl<'_>) -> LocalBoxFuture<'_, Result<(), PersistenceError>>,
{
let db = self.db.write().await;
let txn = db.transaction_on_one_with_mode(APPFLOWY_STORE, IdbTransactionMode::Readwrite)?;
let store = store_from_transaction(&txn)?;
let operation = IndexddbStoreImpl(store);
f(operation).await?;
transaction_result_to_result(txn.await)?;
Ok(())
}
}
pub struct IndexddbStoreImpl<'i>(IdbObjectStore<'i>);
impl<'i> IndexddbStore for IndexddbStoreImpl<'i> {
fn get<'a, T>(&'a self, key: &'a str) -> LocalBoxFuture<Result<Option<T>, PersistenceError>>
where
T: DeserializeOwned + Sync,
{
let js_key = to_js_value(key);
Box::pin(async move {
match self.0.get(&js_key)?.await? {
None => Err(PersistenceError::RecordNotFound(format!(
"Can't find the value for given key: {} ",
key
))),
Some(value) => {
let bytes = Uint8Array::new(&value).to_vec();
let object = serde_json::from_slice::<T>(&bytes)?;
Ok(Some(object))
},
}
})
}
fn set<'a, T>(&'a self, key: &'a str, value: T) -> LocalBoxFuture<Result<(), PersistenceError>>
where
T: Serialize + Sync + 'a,
{
let js_key = to_js_value(key);
Box::pin(async move {
let js_value = to_js_value(serde_json::to_vec(&value)?);
self.0.put_key_val(&js_key, &js_value)?.await?;
Ok(())
})
}
fn remove<'a>(&'a self, key: &'a str) -> LocalBoxFuture<Result<(), PersistenceError>> {
let js_key = to_js_value(key);
Box::pin(async move {
self.0.delete(&js_key)?.await?;
Ok(())
})
}
}
impl IndexddbStore for AppFlowyWASMStore {
fn get<'a, T>(&'a self, key: &'a str) -> LocalBoxFuture<Result<Option<T>, PersistenceError>>
where
T: serde::de::DeserializeOwned + Sync,
{
let db = Arc::downgrade(&self.db);
Box::pin(async move {
let db = db.upgrade().ok_or_else(|| {
PersistenceError::Internal(anyhow!("Failed to upgrade the database reference"))
})?;
let read_guard = db.read().await;
let txn =
read_guard.transaction_on_one_with_mode(APPFLOWY_STORE, IdbTransactionMode::Readonly)?;
let store = store_from_transaction(&txn)?;
IndexddbStoreImpl(store).get(key).await
})
}
fn set<'a, T>(&'a self, key: &'a str, value: T) -> LocalBoxFuture<Result<(), PersistenceError>>
where
T: serde::Serialize + Sync + 'a,
{
let db = Arc::downgrade(&self.db);
Box::pin(async move {
let db = db.upgrade().ok_or_else(|| {
PersistenceError::Internal(anyhow!("Failed to upgrade the database reference"))
})?;
let read_guard = db.read().await;
let txn =
read_guard.transaction_on_one_with_mode(APPFLOWY_STORE, IdbTransactionMode::Readwrite)?;
let store = store_from_transaction(&txn)?;
IndexddbStoreImpl(store).set(key, value).await?;
transaction_result_to_result(txn.await)?;
Ok(())
})
}
fn remove<'a>(&'a self, key: &'a str) -> LocalBoxFuture<Result<(), PersistenceError>> {
let db = Arc::downgrade(&self.db);
Box::pin(async move {
let db = db.upgrade().ok_or_else(|| {
PersistenceError::Internal(anyhow!("Failed to upgrade the database reference"))
})?;
let read_guard = db.read().await;
let txn =
read_guard.transaction_on_one_with_mode(APPFLOWY_STORE, IdbTransactionMode::Readwrite)?;
let store = store_from_transaction(&txn)?;
IndexddbStoreImpl(store).remove(key).await?;
transaction_result_to_result(txn.await)?;
Ok(())
})
}
}
fn to_js_value<K: AsRef<[u8]>>(key: K) -> JsValue {
JsValue::from(Uint8Array::from(key.as_ref()))
}
fn store_from_transaction<'a>(
txn: &'a IdbTransaction<'a>,
) -> Result<IdbObjectStore<'a>, PersistenceError> {
txn
.object_store(APPFLOWY_STORE)
.map_err(PersistenceError::from)
}
fn transaction_result_to_result(result: IdbTransactionResult) -> Result<(), PersistenceError> {
match result {
IdbTransactionResult::Success => Ok(()),
IdbTransactionResult::Error(err) => Err(PersistenceError::from(err)),
IdbTransactionResult::Abort => Err(PersistenceError::Internal(anyhow!("Transaction aborted"))),
}
}

View File

@ -0,0 +1,30 @@
[package]
name = "af-user"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
af-persistence.workspace = true
lib-dispatch = { workspace = true }
flowy-derive = { workspace = true }
flowy-error = { workspace = true, features = ["impl_from_dispatch_error", "impl_from_collab_persistence"] }
flowy-user-pub = { workspace = true }
strum_macros = "0.25.2"
tracing.workspace = true
lib-infra = { workspace = true }
collab = { workspace = true, features = ["wasm_build", "async-plugin"] }
collab-entity.workspace = true
collab-user.workspace = true
collab-integrate = { workspace = true, features = ["enable_wasm"] }
protobuf.workspace = true
bytes.workspace = true
anyhow.workspace = true
wasm-bindgen-futures.workspace = true
tokio = { workspace = true, features = ["sync"] }
[build-dependencies]
flowy-codegen = { workspace = true, features = ["ts"] }

View File

@ -0,0 +1,3 @@
# Check out the FlowyConfig (located in flowy_toml.rs) for more details.
proto_input = ["src/entities", "src/event_map.rs"]
event_files = ["src/event_map.rs"]

View File

@ -0,0 +1,18 @@
use flowy_codegen::Project;
fn main() {
let crate_name = env!("CARGO_PKG_NAME");
flowy_codegen::protobuf_file::ts_gen(
crate_name,
Project::Web {
relative_path: "../../../".to_string(),
},
);
flowy_codegen::ts_event::gen(
crate_name,
Project::Web {
relative_path: "../../../".to_string(),
},
);
}

View File

@ -0,0 +1,9 @@
pub(crate) const AF_USER_SESSION_KEY: &str = "af-user-session";
pub(crate) fn user_workspace_key(uid: i64) -> String {
format!("af-user-workspaces-{}", uid)
}
pub(crate) fn user_profile_key(uid: i64) -> String {
format!("af-user-profile-{}", uid)
}

View File

@ -0,0 +1,68 @@
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_user_pub::entities::Authenticator;
use std::collections::HashMap;
#[derive(ProtoBuf, Default)]
pub struct OauthSignInPB {
/// Use this field to store the third party auth information.
/// Different auth type has different fields.
/// Supabase:
/// - map: { "uuid": "xxx" }
///
#[pb(index = 1)]
pub map: HashMap<String, String>,
#[pb(index = 2)]
pub authenticator: AuthenticatorPB,
}
#[derive(ProtoBuf_Enum, Eq, PartialEq, Debug, Clone)]
pub enum AuthenticatorPB {
Local = 0,
Supabase = 1,
AppFlowyCloud = 2,
}
impl From<Authenticator> for AuthenticatorPB {
fn from(auth_type: Authenticator) -> Self {
match auth_type {
Authenticator::Supabase => AuthenticatorPB::Supabase,
Authenticator::Local => AuthenticatorPB::Local,
Authenticator::AppFlowyCloud => AuthenticatorPB::AppFlowyCloud,
}
}
}
impl From<AuthenticatorPB> for Authenticator {
fn from(pb: AuthenticatorPB) -> Self {
match pb {
AuthenticatorPB::Supabase => Authenticator::Supabase,
AuthenticatorPB::Local => Authenticator::Local,
AuthenticatorPB::AppFlowyCloud => Authenticator::AppFlowyCloud,
}
}
}
impl Default for AuthenticatorPB {
fn default() -> Self {
Self::AppFlowyCloud
}
}
#[derive(ProtoBuf, Default)]
pub struct AddUserPB {
#[pb(index = 1)]
pub email: String,
#[pb(index = 2)]
pub password: String,
}
#[derive(ProtoBuf, Default)]
pub struct UserSignInPB {
#[pb(index = 1)]
pub email: String,
#[pb(index = 2)]
pub password: String,
}

View File

@ -0,0 +1,5 @@
mod auth;
mod user;
pub use auth::*;
pub use user::*;

View File

@ -0,0 +1,69 @@
use crate::entities::AuthenticatorPB;
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_user_pub::entities::{EncryptionType, UserProfile};
#[derive(ProtoBuf, Default, Eq, PartialEq, Debug, Clone)]
pub struct UserProfilePB {
#[pb(index = 1)]
pub id: i64,
#[pb(index = 2)]
pub email: String,
#[pb(index = 3)]
pub name: String,
#[pb(index = 4)]
pub token: String,
#[pb(index = 5)]
pub icon_url: String,
#[pb(index = 6)]
pub openai_key: String,
#[pb(index = 7)]
pub authenticator: AuthenticatorPB,
#[pb(index = 8)]
pub encryption_sign: String,
#[pb(index = 9)]
pub workspace_id: String,
#[pb(index = 10)]
pub stability_ai_key: String,
}
impl From<UserProfile> for UserProfilePB {
fn from(user_profile: UserProfile) -> Self {
let (encryption_sign, _encryption_ty) = match user_profile.encryption_type {
EncryptionType::NoEncryption => ("".to_string(), EncryptionTypePB::NoEncryption),
EncryptionType::SelfEncryption(sign) => (sign, EncryptionTypePB::Symmetric),
};
Self {
id: user_profile.uid,
email: user_profile.email,
name: user_profile.name,
token: user_profile.token,
icon_url: user_profile.icon_url,
openai_key: user_profile.openai_key,
encryption_sign,
authenticator: user_profile.authenticator.into(),
workspace_id: user_profile.workspace_id,
stability_ai_key: user_profile.stability_ai_key,
}
}
}
#[derive(ProtoBuf_Enum, Eq, PartialEq, Debug, Clone)]
pub enum EncryptionTypePB {
NoEncryption = 0,
Symmetric = 1,
}
impl Default for EncryptionTypePB {
fn default() -> Self {
Self::NoEncryption
}
}

View File

@ -0,0 +1,50 @@
use crate::entities::*;
use crate::manager::UserManagerWASM;
use flowy_error::{FlowyError, FlowyResult};
use lib_dispatch::prelude::{data_result_ok, AFPluginData, AFPluginState, DataResult};
use lib_infra::box_any::BoxAny;
use std::rc::{Rc, Weak};
#[tracing::instrument(level = "debug", skip(data, manager), err)]
pub async fn oauth_sign_in_handler(
data: AFPluginData<OauthSignInPB>,
manager: AFPluginState<Weak<UserManagerWASM>>,
) -> DataResult<UserProfilePB, FlowyError> {
let manager = upgrade_manager(manager)?;
let params = data.into_inner();
let user_profile = manager.sign_up(BoxAny::new(params.map)).await?;
data_result_ok(user_profile.into())
}
#[tracing::instrument(level = "debug", skip(data, manager), err)]
pub async fn add_user_handler(
data: AFPluginData<AddUserPB>,
manager: AFPluginState<Weak<UserManagerWASM>>,
) -> Result<(), FlowyError> {
let manager = upgrade_manager(manager)?;
let params = data.into_inner();
manager.add_user(&params.email, &params.password).await?;
Ok(())
}
#[tracing::instrument(level = "debug", skip(data, manager), err)]
pub async fn sign_in_with_password_handler(
data: AFPluginData<UserSignInPB>,
manager: AFPluginState<Weak<UserManagerWASM>>,
) -> DataResult<UserProfilePB, FlowyError> {
let manager = upgrade_manager(manager)?;
let params = data.into_inner();
let user_profile = manager
.sign_in_with_password(&params.email, &params.password)
.await?;
data_result_ok(UserProfilePB::from(user_profile))
}
fn upgrade_manager(
manager: AFPluginState<Weak<UserManagerWASM>>,
) -> FlowyResult<Rc<UserManagerWASM>> {
let manager = manager
.upgrade()
.ok_or(FlowyError::internal().with_context("The user session is already drop"))?;
Ok(manager)
}

View File

@ -0,0 +1,29 @@
use crate::event_handler::*;
use crate::manager::UserManagerWASM;
use flowy_derive::{Flowy_Event, ProtoBuf_Enum};
use lib_dispatch::prelude::AFPlugin;
use std::rc::Weak;
use strum_macros::Display;
#[rustfmt::skip]
pub fn init(user_manager: Weak<UserManagerWASM>) -> AFPlugin {
AFPlugin::new()
.name("Flowy-User")
.state(user_manager)
.event(UserWasmEvent::OauthSignIn, oauth_sign_in_handler)
.event(UserWasmEvent::AddUser, add_user_handler)
.event(UserWasmEvent::SignInPassword, sign_in_with_password_handler)
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash, ProtoBuf_Enum, Flowy_Event)]
#[event_err = "FlowyError"]
pub enum UserWasmEvent {
#[event(input = "OauthSignInPB", output = "UserProfilePB")]
OauthSignIn = 0,
#[event(input = "AddUserPB")]
AddUser = 1,
#[event(input = "UserSignInPB", output = "UserProfilePB")]
SignInPassword = 2,
}

View File

@ -0,0 +1,6 @@
mod define;
pub mod entities;
mod event_handler;
pub mod event_map;
pub mod manager;
mod protobuf;

View File

@ -0,0 +1,217 @@
use crate::define::{user_profile_key, user_workspace_key, AF_USER_SESSION_KEY};
use af_persistence::store::{AppFlowyWASMStore, IndexddbStore};
use anyhow::Context;
use collab::core::collab::CollabDocState;
use collab_entity::CollabType;
use collab_integrate::collab_builder::{AppFlowyCollabBuilder, CollabBuilderConfig};
use collab_integrate::{CollabKVDB, MutexCollab};
use collab_user::core::{MutexUserAwareness, UserAwareness};
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
use flowy_user_pub::cloud::{UserCloudConfig, UserCloudServiceProvider};
use flowy_user_pub::entities::{
awareness_oid_from_user_uuid, AuthResponse, Authenticator, UserAuthResponse, UserProfile,
UserWorkspace,
};
use flowy_user_pub::session::Session;
use lib_infra::box_any::BoxAny;
use lib_infra::future::Fut;
use std::rc::Rc;
use std::sync::{Arc, Mutex, Weak};
use tracing::{error, instrument, trace};
pub trait UserCallback {
fn did_init(
&self,
user_id: i64,
cloud_config: &Option<UserCloudConfig>,
user_workspace: &UserWorkspace,
device_id: &str,
) -> Fut<FlowyResult<()>>;
fn did_sign_in(&self, uid: i64, workspace: &UserWorkspace, device_id: &str) -> FlowyResult<()>;
fn did_sign_up(
&self,
is_new_user: bool,
user_profile: &UserProfile,
user_workspace: &UserWorkspace,
device_id: &str,
) -> Fut<FlowyResult<()>>;
}
pub struct UserManagerWASM {
device_id: String,
pub(crate) cloud_services: Rc<dyn UserCloudServiceProvider>,
pub(crate) collab_builder: Weak<AppFlowyCollabBuilder>,
pub(crate) store: Rc<AppFlowyWASMStore>,
user_callbacks: Vec<Rc<dyn UserCallback>>,
#[allow(dead_code)]
pub(crate) user_awareness: Rc<Mutex<Option<MutexUserAwareness>>>,
pub(crate) collab_db: Arc<CollabKVDB>,
}
impl UserManagerWASM {
pub async fn new(
device_id: &str,
cloud_services: Rc<dyn UserCloudServiceProvider>,
collab_builder: Weak<AppFlowyCollabBuilder>,
) -> Result<Self, FlowyError> {
let device_id = device_id.to_string();
let store = Rc::new(AppFlowyWASMStore::new().await?);
let collab_db = Arc::new(CollabKVDB::new().await?);
Ok(Self {
device_id,
cloud_services,
collab_builder,
store,
user_callbacks: vec![],
user_awareness: Rc::new(Default::default()),
collab_db,
})
}
pub async fn sign_up(&self, params: BoxAny) -> FlowyResult<UserProfile> {
let auth_service = self.cloud_services.get_user_service()?;
let response: AuthResponse = auth_service.sign_up(params).await?;
let new_user_profile = UserProfile::from((&response, &Authenticator::AppFlowyCloud));
let new_session = Session::from(&response);
self.prepare_collab(&new_session);
self
.save_auth_data(&response, &new_user_profile, &new_session)
.await?;
if let Err(err) = self.initialize_user_awareness(&new_session).await {
error!("Failed to initialize user awareness: {:?}", err);
}
for callback in self.user_callbacks.iter() {
if let Err(e) = callback
.did_sign_up(
response.is_new_user,
&new_user_profile,
&new_session.user_workspace,
&self.device_id,
)
.await
{
error!("Failed to call did_sign_up callback: {:?}", e);
}
}
// TODO(nathan): send notification
// send_auth_state_notification(AuthStateChangedPB {
// state: AuthStatePB::AuthStateSignIn,
// message: "Sign in success".to_string(),
// });
Ok(new_user_profile)
}
pub(crate) async fn add_user(&self, email: &str, password: &str) -> Result<(), FlowyError> {
let auth_service = self.cloud_services.get_user_service()?;
auth_service.create_user(email, password).await?;
Ok(())
}
pub(crate) async fn sign_in_with_password(
&self,
email: &str,
password: &str,
) -> Result<UserProfile, FlowyError> {
let auth_service = self.cloud_services.get_user_service()?;
let user_profile = auth_service.sign_in_with_password(email, password).await?;
Ok(user_profile)
}
fn prepare_collab(&self, session: &Session) {
let collab_builder = self.collab_builder.upgrade().unwrap();
collab_builder.initialize(session.user_workspace.id.clone());
}
async fn initialize_user_awareness(&self, new_session: &Session) -> FlowyResult<()> {
let data = self
.cloud_services
.get_user_service()?
.get_user_awareness_doc_state(new_session.user_id)
.await?;
trace!("Get user awareness collab: {}", data.len());
let collab = self
.collab_for_user_awareness(new_session, Arc::downgrade(&self.collab_db), data)
.await?;
MutexUserAwareness::new(UserAwareness::create(collab, None));
Ok(())
}
#[instrument(level = "info", skip_all, err)]
async fn save_auth_data(
&self,
response: &impl UserAuthResponse,
user_profile: &UserProfile,
session: &Session,
) -> Result<(), FlowyError> {
let uid = user_profile.uid;
let user_profile = user_profile.clone();
let session = session.clone();
let user_workspace = response.user_workspaces().to_vec();
self
.store
.begin_write_transaction(|store| {
Box::pin(async move {
store.set(&user_workspace_key(uid), &user_workspace).await?;
store.set(AF_USER_SESSION_KEY, session).await?;
store.set(&user_profile_key(uid), user_profile).await?;
Ok(())
})
})
.await?;
Ok(())
}
pub async fn save_user_session(&self, session: &Session) -> FlowyResult<()> {
self.store.set(AF_USER_SESSION_KEY, session).await?;
Ok(())
}
pub async fn save_user_workspaces(
&self,
uid: i64,
user_workspaces: &[UserWorkspace],
) -> FlowyResult<()> {
self
.store
.set(&user_workspace_key(uid), &user_workspaces.to_vec())
.await?;
Ok(())
}
pub async fn save_user_profile(&self, user_profile: &UserProfile) -> FlowyResult<()> {
let uid = user_profile.uid;
self.store.set(&user_profile_key(uid), user_profile).await?;
Ok(())
}
async fn collab_for_user_awareness(
&self,
session: &Session,
collab_db: Weak<CollabKVDB>,
raw_data: CollabDocState,
) -> Result<Arc<MutexCollab>, FlowyError> {
let collab_builder = self.collab_builder.upgrade().ok_or(FlowyError::new(
ErrorCode::Internal,
"Unexpected error: collab builder is not available",
))?;
let user_awareness_id = awareness_oid_from_user_uuid(&session.user_uuid);
let collab = collab_builder
.build(
session.user_id,
&user_awareness_id.to_string(),
CollabType::UserAwareness,
raw_data,
collab_db,
CollabBuilderConfig::default().sync_enable(true),
)
.await
.context("Build collab for user awareness failed")?;
Ok(collab)
}
}

View File

@ -0,0 +1,689 @@
// This file is generated by rust-protobuf 2.28.0. Do not edit
// @generated
// https://github.com/rust-lang/rust-clippy/issues/702
#![allow(unknown_lints)]
#![allow(clippy::all)]
#![allow(unused_attributes)]
#![cfg_attr(rustfmt, rustfmt::skip)]
#![allow(box_pointers)]
#![allow(dead_code)]
#![allow(missing_docs)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
#![allow(trivial_casts)]
#![allow(unused_imports)]
#![allow(unused_results)]
//! Generated file from `auth.proto`
/// Generated files are compatible only with the same version
/// of protobuf runtime.
// const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_28_0;
#[derive(PartialEq,Clone,Default)]
pub struct OauthSignInPB {
// message fields
pub map: ::std::collections::HashMap<::std::string::String, ::std::string::String>,
pub authenticator: AuthenticatorPB,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
}
impl<'a> ::std::default::Default for &'a OauthSignInPB {
fn default() -> &'a OauthSignInPB {
<OauthSignInPB as ::protobuf::Message>::default_instance()
}
}
impl OauthSignInPB {
pub fn new() -> OauthSignInPB {
::std::default::Default::default()
}
// repeated .OauthSignInPB.MapEntry map = 1;
pub fn get_map(&self) -> &::std::collections::HashMap<::std::string::String, ::std::string::String> {
&self.map
}
pub fn clear_map(&mut self) {
self.map.clear();
}
// Param is passed by value, moved
pub fn set_map(&mut self, v: ::std::collections::HashMap<::std::string::String, ::std::string::String>) {
self.map = v;
}
// Mutable pointer to the field.
pub fn mut_map(&mut self) -> &mut ::std::collections::HashMap<::std::string::String, ::std::string::String> {
&mut self.map
}
// Take field
pub fn take_map(&mut self) -> ::std::collections::HashMap<::std::string::String, ::std::string::String> {
::std::mem::replace(&mut self.map, ::std::collections::HashMap::new())
}
// .AuthenticatorPB authenticator = 2;
pub fn get_authenticator(&self) -> AuthenticatorPB {
self.authenticator
}
pub fn clear_authenticator(&mut self) {
self.authenticator = AuthenticatorPB::Local;
}
// Param is passed by value, moved
pub fn set_authenticator(&mut self, v: AuthenticatorPB) {
self.authenticator = v;
}
}
impl ::protobuf::Message for OauthSignInPB {
fn is_initialized(&self) -> bool {
true
}
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
while !is.eof()? {
let (field_number, wire_type) = is.read_tag_unpack()?;
match field_number {
1 => {
::protobuf::rt::read_map_into::<::protobuf::types::ProtobufTypeString, ::protobuf::types::ProtobufTypeString>(wire_type, is, &mut self.map)?;
},
2 => {
::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.authenticator, 2, &mut self.unknown_fields)?
},
_ => {
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
},
};
}
::std::result::Result::Ok(())
}
// Compute sizes of nested messages
#[allow(unused_variables)]
fn compute_size(&self) -> u32 {
let mut my_size = 0;
my_size += ::protobuf::rt::compute_map_size::<::protobuf::types::ProtobufTypeString, ::protobuf::types::ProtobufTypeString>(1, &self.map);
if self.authenticator != AuthenticatorPB::Local {
my_size += ::protobuf::rt::enum_size(2, self.authenticator);
}
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size);
my_size
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
::protobuf::rt::write_map_with_cached_sizes::<::protobuf::types::ProtobufTypeString, ::protobuf::types::ProtobufTypeString>(1, &self.map, os)?;
if self.authenticator != AuthenticatorPB::Local {
os.write_enum(2, ::protobuf::ProtobufEnum::value(&self.authenticator))?;
}
os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(())
}
fn get_cached_size(&self) -> u32 {
self.cached_size.get()
}
fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
&self.unknown_fields
}
fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
&mut self.unknown_fields
}
fn as_any(&self) -> &dyn (::std::any::Any) {
self as &dyn (::std::any::Any)
}
fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
self as &mut dyn (::std::any::Any)
}
fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
self
}
fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
Self::descriptor_static()
}
fn new() -> OauthSignInPB {
OauthSignInPB::new()
}
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
descriptor.get(|| {
let mut fields = ::std::vec::Vec::new();
fields.push(::protobuf::reflect::accessor::make_map_accessor::<_, ::protobuf::types::ProtobufTypeString, ::protobuf::types::ProtobufTypeString>(
"map",
|m: &OauthSignInPB| { &m.map },
|m: &mut OauthSignInPB| { &mut m.map },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<AuthenticatorPB>>(
"authenticator",
|m: &OauthSignInPB| { &m.authenticator },
|m: &mut OauthSignInPB| { &mut m.authenticator },
));
::protobuf::reflect::MessageDescriptor::new_pb_name::<OauthSignInPB>(
"OauthSignInPB",
fields,
file_descriptor_proto()
)
})
}
fn default_instance() -> &'static OauthSignInPB {
static instance: ::protobuf::rt::LazyV2<OauthSignInPB> = ::protobuf::rt::LazyV2::INIT;
instance.get(OauthSignInPB::new)
}
}
impl ::protobuf::Clear for OauthSignInPB {
fn clear(&mut self) {
self.map.clear();
self.authenticator = AuthenticatorPB::Local;
self.unknown_fields.clear();
}
}
impl ::std::fmt::Debug for OauthSignInPB {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for OauthSignInPB {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Message(self)
}
}
#[derive(PartialEq,Clone,Default)]
pub struct AddUserPB {
// message fields
pub email: ::std::string::String,
pub password: ::std::string::String,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
}
impl<'a> ::std::default::Default for &'a AddUserPB {
fn default() -> &'a AddUserPB {
<AddUserPB as ::protobuf::Message>::default_instance()
}
}
impl AddUserPB {
pub fn new() -> AddUserPB {
::std::default::Default::default()
}
// string email = 1;
pub fn get_email(&self) -> &str {
&self.email
}
pub fn clear_email(&mut self) {
self.email.clear();
}
// Param is passed by value, moved
pub fn set_email(&mut self, v: ::std::string::String) {
self.email = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_email(&mut self) -> &mut ::std::string::String {
&mut self.email
}
// Take field
pub fn take_email(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.email, ::std::string::String::new())
}
// string password = 2;
pub fn get_password(&self) -> &str {
&self.password
}
pub fn clear_password(&mut self) {
self.password.clear();
}
// Param is passed by value, moved
pub fn set_password(&mut self, v: ::std::string::String) {
self.password = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_password(&mut self) -> &mut ::std::string::String {
&mut self.password
}
// Take field
pub fn take_password(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.password, ::std::string::String::new())
}
}
impl ::protobuf::Message for AddUserPB {
fn is_initialized(&self) -> bool {
true
}
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
while !is.eof()? {
let (field_number, wire_type) = is.read_tag_unpack()?;
match field_number {
1 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.email)?;
},
2 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.password)?;
},
_ => {
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
},
};
}
::std::result::Result::Ok(())
}
// Compute sizes of nested messages
#[allow(unused_variables)]
fn compute_size(&self) -> u32 {
let mut my_size = 0;
if !self.email.is_empty() {
my_size += ::protobuf::rt::string_size(1, &self.email);
}
if !self.password.is_empty() {
my_size += ::protobuf::rt::string_size(2, &self.password);
}
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size);
my_size
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
if !self.email.is_empty() {
os.write_string(1, &self.email)?;
}
if !self.password.is_empty() {
os.write_string(2, &self.password)?;
}
os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(())
}
fn get_cached_size(&self) -> u32 {
self.cached_size.get()
}
fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
&self.unknown_fields
}
fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
&mut self.unknown_fields
}
fn as_any(&self) -> &dyn (::std::any::Any) {
self as &dyn (::std::any::Any)
}
fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
self as &mut dyn (::std::any::Any)
}
fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
self
}
fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
Self::descriptor_static()
}
fn new() -> AddUserPB {
AddUserPB::new()
}
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
descriptor.get(|| {
let mut fields = ::std::vec::Vec::new();
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"email",
|m: &AddUserPB| { &m.email },
|m: &mut AddUserPB| { &mut m.email },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"password",
|m: &AddUserPB| { &m.password },
|m: &mut AddUserPB| { &mut m.password },
));
::protobuf::reflect::MessageDescriptor::new_pb_name::<AddUserPB>(
"AddUserPB",
fields,
file_descriptor_proto()
)
})
}
fn default_instance() -> &'static AddUserPB {
static instance: ::protobuf::rt::LazyV2<AddUserPB> = ::protobuf::rt::LazyV2::INIT;
instance.get(AddUserPB::new)
}
}
impl ::protobuf::Clear for AddUserPB {
fn clear(&mut self) {
self.email.clear();
self.password.clear();
self.unknown_fields.clear();
}
}
impl ::std::fmt::Debug for AddUserPB {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for AddUserPB {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Message(self)
}
}
#[derive(PartialEq,Clone,Default)]
pub struct UserSignInPB {
// message fields
pub email: ::std::string::String,
pub password: ::std::string::String,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
}
impl<'a> ::std::default::Default for &'a UserSignInPB {
fn default() -> &'a UserSignInPB {
<UserSignInPB as ::protobuf::Message>::default_instance()
}
}
impl UserSignInPB {
pub fn new() -> UserSignInPB {
::std::default::Default::default()
}
// string email = 1;
pub fn get_email(&self) -> &str {
&self.email
}
pub fn clear_email(&mut self) {
self.email.clear();
}
// Param is passed by value, moved
pub fn set_email(&mut self, v: ::std::string::String) {
self.email = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_email(&mut self) -> &mut ::std::string::String {
&mut self.email
}
// Take field
pub fn take_email(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.email, ::std::string::String::new())
}
// string password = 2;
pub fn get_password(&self) -> &str {
&self.password
}
pub fn clear_password(&mut self) {
self.password.clear();
}
// Param is passed by value, moved
pub fn set_password(&mut self, v: ::std::string::String) {
self.password = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_password(&mut self) -> &mut ::std::string::String {
&mut self.password
}
// Take field
pub fn take_password(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.password, ::std::string::String::new())
}
}
impl ::protobuf::Message for UserSignInPB {
fn is_initialized(&self) -> bool {
true
}
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
while !is.eof()? {
let (field_number, wire_type) = is.read_tag_unpack()?;
match field_number {
1 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.email)?;
},
2 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.password)?;
},
_ => {
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
},
};
}
::std::result::Result::Ok(())
}
// Compute sizes of nested messages
#[allow(unused_variables)]
fn compute_size(&self) -> u32 {
let mut my_size = 0;
if !self.email.is_empty() {
my_size += ::protobuf::rt::string_size(1, &self.email);
}
if !self.password.is_empty() {
my_size += ::protobuf::rt::string_size(2, &self.password);
}
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size);
my_size
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
if !self.email.is_empty() {
os.write_string(1, &self.email)?;
}
if !self.password.is_empty() {
os.write_string(2, &self.password)?;
}
os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(())
}
fn get_cached_size(&self) -> u32 {
self.cached_size.get()
}
fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
&self.unknown_fields
}
fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
&mut self.unknown_fields
}
fn as_any(&self) -> &dyn (::std::any::Any) {
self as &dyn (::std::any::Any)
}
fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
self as &mut dyn (::std::any::Any)
}
fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
self
}
fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
Self::descriptor_static()
}
fn new() -> UserSignInPB {
UserSignInPB::new()
}
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
descriptor.get(|| {
let mut fields = ::std::vec::Vec::new();
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"email",
|m: &UserSignInPB| { &m.email },
|m: &mut UserSignInPB| { &mut m.email },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"password",
|m: &UserSignInPB| { &m.password },
|m: &mut UserSignInPB| { &mut m.password },
));
::protobuf::reflect::MessageDescriptor::new_pb_name::<UserSignInPB>(
"UserSignInPB",
fields,
file_descriptor_proto()
)
})
}
fn default_instance() -> &'static UserSignInPB {
static instance: ::protobuf::rt::LazyV2<UserSignInPB> = ::protobuf::rt::LazyV2::INIT;
instance.get(UserSignInPB::new)
}
}
impl ::protobuf::Clear for UserSignInPB {
fn clear(&mut self) {
self.email.clear();
self.password.clear();
self.unknown_fields.clear();
}
}
impl ::std::fmt::Debug for UserSignInPB {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for UserSignInPB {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Message(self)
}
}
#[derive(Clone,PartialEq,Eq,Debug,Hash)]
pub enum AuthenticatorPB {
Local = 0,
Supabase = 1,
AppFlowyCloud = 2,
}
impl ::protobuf::ProtobufEnum for AuthenticatorPB {
fn value(&self) -> i32 {
*self as i32
}
fn from_i32(value: i32) -> ::std::option::Option<AuthenticatorPB> {
match value {
0 => ::std::option::Option::Some(AuthenticatorPB::Local),
1 => ::std::option::Option::Some(AuthenticatorPB::Supabase),
2 => ::std::option::Option::Some(AuthenticatorPB::AppFlowyCloud),
_ => ::std::option::Option::None
}
}
fn values() -> &'static [Self] {
static values: &'static [AuthenticatorPB] = &[
AuthenticatorPB::Local,
AuthenticatorPB::Supabase,
AuthenticatorPB::AppFlowyCloud,
];
values
}
fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor {
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::LazyV2::INIT;
descriptor.get(|| {
::protobuf::reflect::EnumDescriptor::new_pb_name::<AuthenticatorPB>("AuthenticatorPB", file_descriptor_proto())
})
}
}
impl ::std::marker::Copy for AuthenticatorPB {
}
impl ::std::default::Default for AuthenticatorPB {
fn default() -> Self {
AuthenticatorPB::Local
}
}
impl ::protobuf::reflect::ProtobufValue for AuthenticatorPB {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self))
}
}
static file_descriptor_proto_data: &'static [u8] = b"\
\n\nauth.proto\"\xaa\x01\n\rOauthSignInPB\x12)\n\x03map\x18\x01\x20\x03(\
\x0b2\x17.OauthSignInPB.MapEntryR\x03map\x126\n\rauthenticator\x18\x02\
\x20\x01(\x0e2\x10.AuthenticatorPBR\rauthenticator\x1a6\n\x08MapEntry\
\x12\x10\n\x03key\x18\x01\x20\x01(\tR\x03key\x12\x14\n\x05value\x18\x02\
\x20\x01(\tR\x05value:\x028\x01\"=\n\tAddUserPB\x12\x14\n\x05email\x18\
\x01\x20\x01(\tR\x05email\x12\x1a\n\x08password\x18\x02\x20\x01(\tR\x08p\
assword\"@\n\x0cUserSignInPB\x12\x14\n\x05email\x18\x01\x20\x01(\tR\x05e\
mail\x12\x1a\n\x08password\x18\x02\x20\x01(\tR\x08password*=\n\x0fAuthen\
ticatorPB\x12\t\n\x05Local\x10\0\x12\x0c\n\x08Supabase\x10\x01\x12\x11\n\
\rAppFlowyCloud\x10\x02b\x06proto3\
";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto {
::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()
}
pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
file_descriptor_proto_lazy.get(|| {
parse_descriptor_proto()
})
}

View File

@ -0,0 +1,95 @@
// This file is generated by rust-protobuf 2.28.0. Do not edit
// @generated
// https://github.com/rust-lang/rust-clippy/issues/702
#![allow(unknown_lints)]
#![allow(clippy::all)]
#![allow(unused_attributes)]
#![cfg_attr(rustfmt, rustfmt::skip)]
#![allow(box_pointers)]
#![allow(dead_code)]
#![allow(missing_docs)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
#![allow(trivial_casts)]
#![allow(unused_imports)]
#![allow(unused_results)]
//! Generated file from `event_map.proto`
/// Generated files are compatible only with the same version
/// of protobuf runtime.
// const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_28_0;
#[derive(Clone,PartialEq,Eq,Debug,Hash)]
pub enum UserWasmEvent {
OauthSignIn = 0,
AddUser = 1,
SignInPassword = 2,
}
impl ::protobuf::ProtobufEnum for UserWasmEvent {
fn value(&self) -> i32 {
*self as i32
}
fn from_i32(value: i32) -> ::std::option::Option<UserWasmEvent> {
match value {
0 => ::std::option::Option::Some(UserWasmEvent::OauthSignIn),
1 => ::std::option::Option::Some(UserWasmEvent::AddUser),
2 => ::std::option::Option::Some(UserWasmEvent::SignInPassword),
_ => ::std::option::Option::None
}
}
fn values() -> &'static [Self] {
static values: &'static [UserWasmEvent] = &[
UserWasmEvent::OauthSignIn,
UserWasmEvent::AddUser,
UserWasmEvent::SignInPassword,
];
values
}
fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor {
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::LazyV2::INIT;
descriptor.get(|| {
::protobuf::reflect::EnumDescriptor::new_pb_name::<UserWasmEvent>("UserWasmEvent", file_descriptor_proto())
})
}
}
impl ::std::marker::Copy for UserWasmEvent {
}
impl ::std::default::Default for UserWasmEvent {
fn default() -> Self {
UserWasmEvent::OauthSignIn
}
}
impl ::protobuf::reflect::ProtobufValue for UserWasmEvent {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self))
}
}
static file_descriptor_proto_data: &'static [u8] = b"\
\n\x0fevent_map.proto*A\n\rUserWasmEvent\x12\x0f\n\x0bOauthSignIn\x10\0\
\x12\x0b\n\x07AddUser\x10\x01\x12\x12\n\x0eSignInPassword\x10\x02b\x06pr\
oto3\
";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto {
::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()
}
pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
file_descriptor_proto_lazy.get(|| {
parse_descriptor_proto()
})
}

View File

@ -0,0 +1,12 @@
#![cfg_attr(rustfmt, rustfmt::skip)]
#![allow(ambiguous_glob_reexports)]
// Auto-generated, do not edit
mod event_map;
pub use event_map::*;
mod auth;
pub use auth::*;
mod user;
pub use user::*;

View File

@ -0,0 +1,618 @@
// This file is generated by rust-protobuf 2.28.0. Do not edit
// @generated
// https://github.com/rust-lang/rust-clippy/issues/702
#![allow(unknown_lints)]
#![allow(clippy::all)]
#![allow(unused_attributes)]
#![cfg_attr(rustfmt, rustfmt::skip)]
#![allow(box_pointers)]
#![allow(dead_code)]
#![allow(missing_docs)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
#![allow(trivial_casts)]
#![allow(unused_imports)]
#![allow(unused_results)]
//! Generated file from `user.proto`
/// Generated files are compatible only with the same version
/// of protobuf runtime.
// const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_28_0;
#[derive(PartialEq,Clone,Default)]
pub struct UserProfilePB {
// message fields
pub id: i64,
pub email: ::std::string::String,
pub name: ::std::string::String,
pub token: ::std::string::String,
pub icon_url: ::std::string::String,
pub openai_key: ::std::string::String,
pub authenticator: super::auth::AuthenticatorPB,
pub encryption_sign: ::std::string::String,
pub workspace_id: ::std::string::String,
pub stability_ai_key: ::std::string::String,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
}
impl<'a> ::std::default::Default for &'a UserProfilePB {
fn default() -> &'a UserProfilePB {
<UserProfilePB as ::protobuf::Message>::default_instance()
}
}
impl UserProfilePB {
pub fn new() -> UserProfilePB {
::std::default::Default::default()
}
// int64 id = 1;
pub fn get_id(&self) -> i64 {
self.id
}
pub fn clear_id(&mut self) {
self.id = 0;
}
// Param is passed by value, moved
pub fn set_id(&mut self, v: i64) {
self.id = v;
}
// string email = 2;
pub fn get_email(&self) -> &str {
&self.email
}
pub fn clear_email(&mut self) {
self.email.clear();
}
// Param is passed by value, moved
pub fn set_email(&mut self, v: ::std::string::String) {
self.email = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_email(&mut self) -> &mut ::std::string::String {
&mut self.email
}
// Take field
pub fn take_email(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.email, ::std::string::String::new())
}
// string name = 3;
pub fn get_name(&self) -> &str {
&self.name
}
pub fn clear_name(&mut self) {
self.name.clear();
}
// Param is passed by value, moved
pub fn set_name(&mut self, v: ::std::string::String) {
self.name = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_name(&mut self) -> &mut ::std::string::String {
&mut self.name
}
// Take field
pub fn take_name(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.name, ::std::string::String::new())
}
// string token = 4;
pub fn get_token(&self) -> &str {
&self.token
}
pub fn clear_token(&mut self) {
self.token.clear();
}
// Param is passed by value, moved
pub fn set_token(&mut self, v: ::std::string::String) {
self.token = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_token(&mut self) -> &mut ::std::string::String {
&mut self.token
}
// Take field
pub fn take_token(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.token, ::std::string::String::new())
}
// string icon_url = 5;
pub fn get_icon_url(&self) -> &str {
&self.icon_url
}
pub fn clear_icon_url(&mut self) {
self.icon_url.clear();
}
// Param is passed by value, moved
pub fn set_icon_url(&mut self, v: ::std::string::String) {
self.icon_url = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_icon_url(&mut self) -> &mut ::std::string::String {
&mut self.icon_url
}
// Take field
pub fn take_icon_url(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.icon_url, ::std::string::String::new())
}
// string openai_key = 6;
pub fn get_openai_key(&self) -> &str {
&self.openai_key
}
pub fn clear_openai_key(&mut self) {
self.openai_key.clear();
}
// Param is passed by value, moved
pub fn set_openai_key(&mut self, v: ::std::string::String) {
self.openai_key = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_openai_key(&mut self) -> &mut ::std::string::String {
&mut self.openai_key
}
// Take field
pub fn take_openai_key(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.openai_key, ::std::string::String::new())
}
// .AuthenticatorPB authenticator = 7;
pub fn get_authenticator(&self) -> super::auth::AuthenticatorPB {
self.authenticator
}
pub fn clear_authenticator(&mut self) {
self.authenticator = super::auth::AuthenticatorPB::Local;
}
// Param is passed by value, moved
pub fn set_authenticator(&mut self, v: super::auth::AuthenticatorPB) {
self.authenticator = v;
}
// string encryption_sign = 8;
pub fn get_encryption_sign(&self) -> &str {
&self.encryption_sign
}
pub fn clear_encryption_sign(&mut self) {
self.encryption_sign.clear();
}
// Param is passed by value, moved
pub fn set_encryption_sign(&mut self, v: ::std::string::String) {
self.encryption_sign = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_encryption_sign(&mut self) -> &mut ::std::string::String {
&mut self.encryption_sign
}
// Take field
pub fn take_encryption_sign(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.encryption_sign, ::std::string::String::new())
}
// string workspace_id = 9;
pub fn get_workspace_id(&self) -> &str {
&self.workspace_id
}
pub fn clear_workspace_id(&mut self) {
self.workspace_id.clear();
}
// Param is passed by value, moved
pub fn set_workspace_id(&mut self, v: ::std::string::String) {
self.workspace_id = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_workspace_id(&mut self) -> &mut ::std::string::String {
&mut self.workspace_id
}
// Take field
pub fn take_workspace_id(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.workspace_id, ::std::string::String::new())
}
// string stability_ai_key = 10;
pub fn get_stability_ai_key(&self) -> &str {
&self.stability_ai_key
}
pub fn clear_stability_ai_key(&mut self) {
self.stability_ai_key.clear();
}
// Param is passed by value, moved
pub fn set_stability_ai_key(&mut self, v: ::std::string::String) {
self.stability_ai_key = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_stability_ai_key(&mut self) -> &mut ::std::string::String {
&mut self.stability_ai_key
}
// Take field
pub fn take_stability_ai_key(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.stability_ai_key, ::std::string::String::new())
}
}
impl ::protobuf::Message for UserProfilePB {
fn is_initialized(&self) -> bool {
true
}
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
while !is.eof()? {
let (field_number, wire_type) = is.read_tag_unpack()?;
match field_number {
1 => {
if wire_type != ::protobuf::wire_format::WireTypeVarint {
return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
}
let tmp = is.read_int64()?;
self.id = tmp;
},
2 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.email)?;
},
3 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.name)?;
},
4 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.token)?;
},
5 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.icon_url)?;
},
6 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.openai_key)?;
},
7 => {
::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.authenticator, 7, &mut self.unknown_fields)?
},
8 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.encryption_sign)?;
},
9 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.workspace_id)?;
},
10 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.stability_ai_key)?;
},
_ => {
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
},
};
}
::std::result::Result::Ok(())
}
// Compute sizes of nested messages
#[allow(unused_variables)]
fn compute_size(&self) -> u32 {
let mut my_size = 0;
if self.id != 0 {
my_size += ::protobuf::rt::value_size(1, self.id, ::protobuf::wire_format::WireTypeVarint);
}
if !self.email.is_empty() {
my_size += ::protobuf::rt::string_size(2, &self.email);
}
if !self.name.is_empty() {
my_size += ::protobuf::rt::string_size(3, &self.name);
}
if !self.token.is_empty() {
my_size += ::protobuf::rt::string_size(4, &self.token);
}
if !self.icon_url.is_empty() {
my_size += ::protobuf::rt::string_size(5, &self.icon_url);
}
if !self.openai_key.is_empty() {
my_size += ::protobuf::rt::string_size(6, &self.openai_key);
}
if self.authenticator != super::auth::AuthenticatorPB::Local {
my_size += ::protobuf::rt::enum_size(7, self.authenticator);
}
if !self.encryption_sign.is_empty() {
my_size += ::protobuf::rt::string_size(8, &self.encryption_sign);
}
if !self.workspace_id.is_empty() {
my_size += ::protobuf::rt::string_size(9, &self.workspace_id);
}
if !self.stability_ai_key.is_empty() {
my_size += ::protobuf::rt::string_size(10, &self.stability_ai_key);
}
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size);
my_size
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
if self.id != 0 {
os.write_int64(1, self.id)?;
}
if !self.email.is_empty() {
os.write_string(2, &self.email)?;
}
if !self.name.is_empty() {
os.write_string(3, &self.name)?;
}
if !self.token.is_empty() {
os.write_string(4, &self.token)?;
}
if !self.icon_url.is_empty() {
os.write_string(5, &self.icon_url)?;
}
if !self.openai_key.is_empty() {
os.write_string(6, &self.openai_key)?;
}
if self.authenticator != super::auth::AuthenticatorPB::Local {
os.write_enum(7, ::protobuf::ProtobufEnum::value(&self.authenticator))?;
}
if !self.encryption_sign.is_empty() {
os.write_string(8, &self.encryption_sign)?;
}
if !self.workspace_id.is_empty() {
os.write_string(9, &self.workspace_id)?;
}
if !self.stability_ai_key.is_empty() {
os.write_string(10, &self.stability_ai_key)?;
}
os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(())
}
fn get_cached_size(&self) -> u32 {
self.cached_size.get()
}
fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
&self.unknown_fields
}
fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
&mut self.unknown_fields
}
fn as_any(&self) -> &dyn (::std::any::Any) {
self as &dyn (::std::any::Any)
}
fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
self as &mut dyn (::std::any::Any)
}
fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
self
}
fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
Self::descriptor_static()
}
fn new() -> UserProfilePB {
UserProfilePB::new()
}
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
descriptor.get(|| {
let mut fields = ::std::vec::Vec::new();
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeInt64>(
"id",
|m: &UserProfilePB| { &m.id },
|m: &mut UserProfilePB| { &mut m.id },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"email",
|m: &UserProfilePB| { &m.email },
|m: &mut UserProfilePB| { &mut m.email },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"name",
|m: &UserProfilePB| { &m.name },
|m: &mut UserProfilePB| { &mut m.name },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"token",
|m: &UserProfilePB| { &m.token },
|m: &mut UserProfilePB| { &mut m.token },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"icon_url",
|m: &UserProfilePB| { &m.icon_url },
|m: &mut UserProfilePB| { &mut m.icon_url },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"openai_key",
|m: &UserProfilePB| { &m.openai_key },
|m: &mut UserProfilePB| { &mut m.openai_key },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<super::auth::AuthenticatorPB>>(
"authenticator",
|m: &UserProfilePB| { &m.authenticator },
|m: &mut UserProfilePB| { &mut m.authenticator },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"encryption_sign",
|m: &UserProfilePB| { &m.encryption_sign },
|m: &mut UserProfilePB| { &mut m.encryption_sign },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"workspace_id",
|m: &UserProfilePB| { &m.workspace_id },
|m: &mut UserProfilePB| { &mut m.workspace_id },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"stability_ai_key",
|m: &UserProfilePB| { &m.stability_ai_key },
|m: &mut UserProfilePB| { &mut m.stability_ai_key },
));
::protobuf::reflect::MessageDescriptor::new_pb_name::<UserProfilePB>(
"UserProfilePB",
fields,
file_descriptor_proto()
)
})
}
fn default_instance() -> &'static UserProfilePB {
static instance: ::protobuf::rt::LazyV2<UserProfilePB> = ::protobuf::rt::LazyV2::INIT;
instance.get(UserProfilePB::new)
}
}
impl ::protobuf::Clear for UserProfilePB {
fn clear(&mut self) {
self.id = 0;
self.email.clear();
self.name.clear();
self.token.clear();
self.icon_url.clear();
self.openai_key.clear();
self.authenticator = super::auth::AuthenticatorPB::Local;
self.encryption_sign.clear();
self.workspace_id.clear();
self.stability_ai_key.clear();
self.unknown_fields.clear();
}
}
impl ::std::fmt::Debug for UserProfilePB {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for UserProfilePB {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Message(self)
}
}
#[derive(Clone,PartialEq,Eq,Debug,Hash)]
pub enum EncryptionTypePB {
NoEncryption = 0,
Symmetric = 1,
}
impl ::protobuf::ProtobufEnum for EncryptionTypePB {
fn value(&self) -> i32 {
*self as i32
}
fn from_i32(value: i32) -> ::std::option::Option<EncryptionTypePB> {
match value {
0 => ::std::option::Option::Some(EncryptionTypePB::NoEncryption),
1 => ::std::option::Option::Some(EncryptionTypePB::Symmetric),
_ => ::std::option::Option::None
}
}
fn values() -> &'static [Self] {
static values: &'static [EncryptionTypePB] = &[
EncryptionTypePB::NoEncryption,
EncryptionTypePB::Symmetric,
];
values
}
fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor {
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::LazyV2::INIT;
descriptor.get(|| {
::protobuf::reflect::EnumDescriptor::new_pb_name::<EncryptionTypePB>("EncryptionTypePB", file_descriptor_proto())
})
}
}
impl ::std::marker::Copy for EncryptionTypePB {
}
impl ::std::default::Default for EncryptionTypePB {
fn default() -> Self {
EncryptionTypePB::NoEncryption
}
}
impl ::protobuf::reflect::ProtobufValue for EncryptionTypePB {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self))
}
}
static file_descriptor_proto_data: &'static [u8] = b"\
\n\nuser.proto\x1a\nauth.proto\"\xc7\x02\n\rUserProfilePB\x12\x0e\n\x02i\
d\x18\x01\x20\x01(\x03R\x02id\x12\x14\n\x05email\x18\x02\x20\x01(\tR\x05\
email\x12\x12\n\x04name\x18\x03\x20\x01(\tR\x04name\x12\x14\n\x05token\
\x18\x04\x20\x01(\tR\x05token\x12\x19\n\x08icon_url\x18\x05\x20\x01(\tR\
\x07iconUrl\x12\x1d\n\nopenai_key\x18\x06\x20\x01(\tR\topenaiKey\x126\n\
\rauthenticator\x18\x07\x20\x01(\x0e2\x10.AuthenticatorPBR\rauthenticato\
r\x12'\n\x0fencryption_sign\x18\x08\x20\x01(\tR\x0eencryptionSign\x12!\n\
\x0cworkspace_id\x18\t\x20\x01(\tR\x0bworkspaceId\x12(\n\x10stability_ai\
_key\x18\n\x20\x01(\tR\x0estabilityAiKey*3\n\x10EncryptionTypePB\x12\x10\
\n\x0cNoEncryption\x10\0\x12\r\n\tSymmetric\x10\x01b\x06proto3\
";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto {
::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()
}
pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
file_descriptor_proto_lazy.get(|| {
parse_descriptor_proto()
})
}

View File

@ -0,0 +1,42 @@
[package]
name = "af-wasm"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
wasm-bindgen = { version = "0.2.89" }
lazy_static = "1.4.0"
lib-dispatch = { workspace = true, features = ["use_serde"] }
parking_lot.workspace = true
tracing.workspace = true
tracing-core = { version = "0.1.32" }
tracing-wasm = "0.2.1"
serde.workspace = true
collab-integrate = { workspace = true, features = ["enable_wasm"] }
tokio-stream.workspace = true
af-user.workspace = true
flowy-notification = { workspace = true, features = ["web_ts"] }
flowy-user-pub = { workspace = true }
flowy-server = { workspace = true, features = ["enable_wasm"] }
flowy-server-pub = { workspace = true }
flowy-error = { workspace = true, features = ["impl_from_dispatch_error", "web_ts"] }
flowy-document = { workspace = true, features = ["web_ts"] }
lib-infra = { workspace = true }
collab = { workspace = true, features = ["wasm_build", "async-plugin"] }
web-sys = "0.3"
wasm-bindgen-futures.workspace = true
uuid.workspace = true
serde-wasm-bindgen.workspace = true
js-sys = "0.3.67"
[dev-dependencies]
wasm-bindgen-test = "0.3.40"
tokio = { version = "1.0", features = ["sync"] }
[features]
localhost_dev = []

View File

@ -0,0 +1,45 @@
use crate::integrate::server::ServerProviderWASM;
use af_user::manager::UserManagerWASM;
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
use flowy_error::FlowyResult;
use flowy_server_pub::af_cloud_config::AFCloudConfiguration;
use lib_dispatch::prelude::AFPluginDispatcher;
use lib_dispatch::runtime::AFPluginRuntime;
use std::rc::Rc;
use std::sync::Arc;
pub struct AppFlowyWASMCore {
pub collab_builder: Arc<AppFlowyCollabBuilder>,
pub event_dispatcher: Rc<AFPluginDispatcher>,
pub user_manager: Rc<UserManagerWASM>,
}
impl AppFlowyWASMCore {
pub async fn new(device_id: &str, cloud_config: AFCloudConfiguration) -> FlowyResult<Self> {
let runtime = Arc::new(AFPluginRuntime::new().unwrap());
let server_provider = Rc::new(ServerProviderWASM::new(device_id, cloud_config));
let collab_builder = Arc::new(AppFlowyCollabBuilder::new(
server_provider.clone(),
device_id.to_string(),
));
let user_manager = Rc::new(
UserManagerWASM::new(
device_id,
server_provider.clone(),
Arc::downgrade(&collab_builder),
)
.await?,
);
let event_dispatcher = Rc::new(AFPluginDispatcher::new(
runtime,
vec![af_user::event_map::init(Rc::downgrade(&user_manager))],
));
Ok(Self {
collab_builder,
event_dispatcher,
user_manager,
})
}
}

View File

@ -0,0 +1 @@
pub mod server;

View File

@ -0,0 +1,106 @@
use collab::preclude::CollabPlugin;
use collab_integrate::collab_builder::{
CollabCloudPluginProvider, CollabPluginProviderContext, CollabPluginProviderType,
};
use flowy_error::FlowyError;
use flowy_server::af_cloud::AppFlowyCloudServer;
use flowy_server::AppFlowyServer;
use flowy_server_pub::af_cloud_config::AFCloudConfiguration;
use flowy_user_pub::cloud::{
UserCloudService, UserCloudServiceProvider, UserCloudServiceProviderBase,
};
use flowy_user_pub::entities::{Authenticator, UserTokenState};
use lib_infra::future::{to_fut, Fut};
use parking_lot::RwLock;
use std::rc::Rc;
use std::sync::Arc;
use tokio_stream::wrappers::WatchStream;
use tracing::{info, warn};
pub struct ServerProviderWASM {
device_id: String,
config: AFCloudConfiguration,
server: RwLock<Option<Rc<dyn AppFlowyServer>>>,
}
impl ServerProviderWASM {
pub fn new(device_id: &str, config: AFCloudConfiguration) -> Self {
info!("Server config: {}", config);
Self {
device_id: device_id.to_string(),
server: RwLock::new(Default::default()),
config,
}
}
pub fn get_server(&self) -> Rc<dyn AppFlowyServer> {
let server = self.server.read().as_ref().cloned();
match server {
Some(server) => server,
None => {
let server = Rc::new(AppFlowyCloudServer::new(
self.config.clone(),
true,
self.device_id.clone(),
));
*self.server.write() = Some(server.clone());
server
},
}
}
}
impl CollabCloudPluginProvider for ServerProviderWASM {
fn provider_type(&self) -> CollabPluginProviderType {
CollabPluginProviderType::AppFlowyCloud
}
fn get_plugins(&self, _context: CollabPluginProviderContext) -> Fut<Vec<Arc<dyn CollabPlugin>>> {
to_fut(async move { vec![] })
}
fn is_sync_enabled(&self) -> bool {
true
}
}
impl UserCloudServiceProvider for ServerProviderWASM {}
impl UserCloudServiceProviderBase for ServerProviderWASM {
fn set_token(&self, token: &str) -> Result<(), FlowyError> {
self.get_server().set_token(token)?;
Ok(())
}
fn subscribe_token_state(&self) -> Option<WatchStream<UserTokenState>> {
self.get_server().subscribe_token_state()
}
fn set_enable_sync(&self, _uid: i64, _enable_sync: bool) {
warn!("enable sync is not supported in wasm")
}
fn set_user_authenticator(&self, _authenticator: &Authenticator) {
warn!("set user authenticator is not supported in wasm")
}
fn get_user_authenticator(&self) -> Authenticator {
Authenticator::AppFlowyCloud
}
fn set_network_reachable(&self, _reachable: bool) {
warn!("set network reachable is not supported in wasm")
}
fn set_encrypt_secret(&self, _secret: String) {
warn!("set encrypt secret is not supported in wasm")
}
fn get_user_service(&self) -> Result<Arc<dyn UserCloudService>, FlowyError> {
Ok(self.get_server().user_service())
}
fn service_url(&self) -> String {
self.config.base_url.clone()
}
}

View File

@ -0,0 +1,161 @@
use crate::notification::TSNotificationSender;
use flowy_notification::{register_notification_sender, unregister_all_notification_sender};
use std::cell::RefCell;
use std::rc::Rc;
pub mod core;
mod integrate;
pub mod notification;
use crate::core::AppFlowyWASMCore;
use lazy_static::lazy_static;
use lib_dispatch::prelude::{
AFPluginDispatcher, AFPluginEventResponse, AFPluginRequest, StatusCode,
};
use flowy_server_pub::af_cloud_config::AFCloudConfiguration;
use tracing::{error, info};
use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen::JsValue;
use wasm_bindgen_futures::{future_to_promise, js_sys};
lazy_static! {
static ref APPFLOWY_CORE: RefCellAppFlowyCore = RefCellAppFlowyCore::new();
}
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
pub fn log(s: &str);
#[wasm_bindgen(js_namespace = window)]
fn onFlowyNotify(event_name: &str, payload: JsValue);
}
#[wasm_bindgen]
pub fn init_tracing_log() {
tracing_wasm::set_as_global_default();
}
#[wasm_bindgen]
pub fn init_wasm_core() -> js_sys::Promise {
#[cfg(feature = "localhost_dev")]
let config = AFCloudConfiguration {
base_url: "http://localhost".to_string(),
ws_base_url: "ws://localhost/ws".to_string(),
gotrue_url: "http://localhost/gotrue".to_string(),
};
#[cfg(not(feature = "localhost_dev"))]
let config = AFCloudConfiguration {
base_url: "https://beta.appflowy.cloud".to_string(),
ws_base_url: "wss://beta.appflowy.cloud/ws".to_string(),
gotrue_url: "https://beta.appflowy.cloud/gotrue".to_string(),
};
let future = async move {
if let Ok(core) = AppFlowyWASMCore::new("device_id", config).await {
*APPFLOWY_CORE.0.borrow_mut() = Some(core);
info!("🔥🔥🔥Initialized AppFlowyWASMCore");
} else {
error!("Failed to initialize AppFlowyWASMCore")
}
Ok(JsValue::from_str(""))
};
future_to_promise(future)
}
#[wasm_bindgen]
pub fn async_event(name: String, payload: Vec<u8>) -> js_sys::Promise {
if let Some(dispatcher) = APPFLOWY_CORE.dispatcher() {
let future = async move {
let request = WasmRequest::new(name, payload);
let event_resp =
AFPluginDispatcher::boxed_async_send_with_callback(dispatcher.as_ref(), request, |_| {
Box::pin(async {})
})
.await;
let response = WasmResponse::from(event_resp);
serde_wasm_bindgen::to_value(&response).map_err(error_response)
};
future_to_promise(future)
} else {
future_to_promise(async { Err(JsValue::from_str("Dispatcher is not initialized")) })
}
}
#[wasm_bindgen]
pub fn register_listener() {
unregister_all_notification_sender();
register_notification_sender(TSNotificationSender::new());
}
pub fn on_event(event_name: &str, args: JsValue) {
onFlowyNotify(event_name, args);
}
struct RefCellAppFlowyCore(RefCell<Option<AppFlowyWASMCore>>);
/// safety:
/// In a WebAssembly, implement the Sync for RefCellAppFlowyCore is safety
/// since WASM currently operates in a single-threaded environment.
unsafe impl Sync for RefCellAppFlowyCore {}
impl RefCellAppFlowyCore {
fn new() -> Self {
Self(RefCell::new(None))
}
fn dispatcher(&self) -> Option<Rc<AFPluginDispatcher>> {
self
.0
.borrow()
.as_ref()
.map(|core| core.event_dispatcher.clone())
}
}
fn error_response(error: serde_wasm_bindgen::Error) -> JsValue {
error!("Error: {}", error);
serde_wasm_bindgen::to_value(&WasmResponse::error(error.to_string())).unwrap()
}
pub struct WasmRequest {
name: String,
payload: Vec<u8>,
}
impl WasmRequest {
pub fn new(name: String, payload: Vec<u8>) -> Self {
Self { name, payload }
}
}
impl From<WasmRequest> for AFPluginRequest {
fn from(request: WasmRequest) -> Self {
AFPluginRequest::new(request.name).payload(request.payload)
}
}
#[derive(serde::Serialize)]
pub struct WasmResponse {
pub code: i8,
pub payload: Vec<u8>,
}
impl WasmResponse {
pub fn error(msg: String) -> Self {
Self {
code: StatusCode::Err as i8,
payload: msg.into_bytes(),
}
}
}
impl From<AFPluginEventResponse> for WasmResponse {
fn from(response: AFPluginEventResponse) -> Self {
Self {
code: response.status_code as i8,
payload: response.payload.to_vec(),
}
}
}

View File

@ -0,0 +1,4 @@
use wasm_bindgen_test::wasm_bindgen_test_configure;
wasm_bindgen_test_configure!(run_in_browser);
mod user;
pub(crate) mod util;

View File

@ -0,0 +1,10 @@
use crate::util::tester::{unique_email, WASMEventTester};
use wasm_bindgen_test::wasm_bindgen_test;
#[wasm_bindgen_test]
async fn sign_up_event_test() {
let tester = WASMEventTester::new().await;
let email = unique_email();
let user_profile = tester.sign_in_with_email(&email).await.unwrap();
assert_eq!(user_profile.email, email);
}

View File

@ -0,0 +1 @@
mod event_test;

View File

@ -0,0 +1,132 @@
use af_wasm::core::AppFlowyWASMCore;
use flowy_error::{internal_error, FlowyError};
use std::rc::Rc;
use std::{
convert::TryFrom,
fmt::{Debug, Display},
hash::Hash,
sync::Arc,
};
use lib_dispatch::prelude::{
AFPluginDispatcher, AFPluginEventResponse, AFPluginFromBytes, AFPluginRequest, ToBytes, *,
};
#[derive(Clone)]
pub struct EventBuilder {
context: TestContext,
}
impl EventBuilder {
pub fn new(core: Arc<AppFlowyWASMCore>) -> Self {
Self {
context: TestContext::new(core),
}
}
pub fn payload<P>(mut self, payload: P) -> Self
where
P: ToBytes,
{
match payload.into_bytes() {
Ok(bytes) => {
let module_request = self.take_request();
self.context.request = Some(module_request.payload(bytes))
},
Err(e) => {
tracing::error!("Set payload failed: {:?}", e);
},
}
self
}
pub fn event<Event>(mut self, event: Event) -> Self
where
Event: Eq + Hash + Debug + Clone + Display,
{
self.context.request = Some(AFPluginRequest::new(event));
self
}
pub async fn async_send(mut self) -> Self {
let request = self.take_request();
let resp = AFPluginDispatcher::async_send(self.dispatch().as_ref(), request).await;
self.context.response = Some(resp);
self
}
pub fn parse<R>(self) -> R
where
R: AFPluginFromBytes,
{
let response = self.get_response();
match response.clone().parse::<R, FlowyError>() {
Ok(Ok(data)) => data,
Ok(Err(e)) => {
panic!(
"Parser {:?} failed: {:?}, response {:?}",
std::any::type_name::<R>(),
e,
response
)
},
Err(e) => panic!(
"Dispatch {:?} failed: {:?}, response {:?}",
std::any::type_name::<R>(),
e,
response
),
}
}
#[allow(dead_code)]
pub fn try_parse<R>(self) -> Result<R, FlowyError>
where
R: AFPluginFromBytes,
{
let response = self.get_response();
response.parse::<R, FlowyError>().map_err(internal_error)?
}
#[allow(dead_code)]
pub fn error(self) -> Option<FlowyError> {
let response = self.get_response();
<AFPluginData<FlowyError>>::try_from(response.payload)
.ok()
.map(|data| data.into_inner())
}
fn dispatch(&self) -> &Rc<AFPluginDispatcher> {
&self.context.sdk.event_dispatcher
}
fn get_response(&self) -> AFPluginEventResponse {
self
.context
.response
.as_ref()
.expect("must call sync_send/async_send first")
.clone()
}
fn take_request(&mut self) -> AFPluginRequest {
self.context.request.take().expect("must call event first")
}
}
#[derive(Clone)]
pub struct TestContext {
pub sdk: Arc<AppFlowyWASMCore>,
request: Option<AFPluginRequest>,
response: Option<AFPluginEventResponse>,
}
impl TestContext {
pub fn new(sdk: Arc<AppFlowyWASMCore>) -> Self {
Self {
sdk,
request: None,
response: None,
}
}
}

View File

@ -0,0 +1,2 @@
mod event_builder;
pub mod tester;

View File

@ -0,0 +1,62 @@
use crate::util::event_builder::EventBuilder;
use af_user::entities::*;
use af_user::event_map::UserWasmEvent::*;
use af_wasm::core::AppFlowyWASMCore;
use flowy_error::FlowyResult;
use flowy_server_pub::af_cloud_config::AFCloudConfiguration;
use parking_lot::Once;
use std::sync::Arc;
use uuid::Uuid;
pub struct WASMEventTester {
core: Arc<AppFlowyWASMCore>,
}
impl WASMEventTester {
pub async fn new() -> Self {
setup_log();
let config = AFCloudConfiguration {
base_url: "http://localhost".to_string(),
ws_base_url: "ws://localhost/ws".to_string(),
gotrue_url: "http://localhost/gotrue".to_string(),
};
let core = Arc::new(AppFlowyWASMCore::new("device_id", config).await.unwrap());
Self { core }
}
pub async fn sign_in_with_email(&self, email: &str) -> FlowyResult<UserProfilePB> {
let email = email.to_string();
let password = "AppFlowy!2024".to_string();
let payload = AddUserPB {
email: email.clone(),
password: password.clone(),
};
EventBuilder::new(self.core.clone())
.event(AddUser)
.payload(payload)
.async_send()
.await;
let payload = UserSignInPB { email, password };
let user_profile = EventBuilder::new(self.core.clone())
.event(SignInPassword)
.payload(payload)
.async_send()
.await
.parse::<UserProfilePB>();
Ok(user_profile)
}
}
pub fn unique_email() -> String {
format!("{}@appflowy.io", Uuid::new_v4())
}
pub fn setup_log() {
static START: Once = Once::new();
START.call_once(|| {
tracing_wasm::set_as_global_default();
});
}

View File

@ -64,6 +64,17 @@ dependencies = [
"subtle",
]
[[package]]
name = "again"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05802a5ad4d172eaf796f7047b42d0af9db513585d16d4169660a21613d34b93"
dependencies = [
"log",
"rand 0.7.3",
"wasm-timer",
]
[[package]]
name = "ahash"
version = "0.7.6"
@ -152,7 +163,7 @@ checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
[[package]]
name = "app-error"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee3abdb27a2d056e7399b486354d26e802720c00#ee3abdb27a2d056e7399b486354d26e802720c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69c69f6474eaa531fd822e9353cc5955b98e45eb#69c69f6474eaa531fd822e9353cc5955b98e45eb"
dependencies = [
"anyhow",
"bincode",
@ -603,9 +614,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.31"
version = "0.4.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb"
dependencies = [
"android-tzdata",
"iana-time-zone",
@ -613,7 +624,7 @@ dependencies = [
"num-traits",
"serde",
"wasm-bindgen",
"windows-targets",
"windows-targets 0.52.0",
]
[[package]]
@ -662,8 +673,9 @@ dependencies = [
[[package]]
name = "client-api"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee3abdb27a2d056e7399b486354d26e802720c00#ee3abdb27a2d056e7399b486354d26e802720c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69c69f6474eaa531fd822e9353cc5955b98e45eb#69c69f6474eaa531fd822e9353cc5955b98e45eb"
dependencies = [
"again",
"anyhow",
"app-error",
"async-trait",
@ -681,7 +693,7 @@ dependencies = [
"gotrue-entity",
"mime",
"mime_guess",
"parking_lot",
"parking_lot 0.12.1",
"prost",
"realtime-entity",
"realtime-protocol",
@ -690,15 +702,16 @@ dependencies = [
"serde",
"serde_json",
"serde_repr",
"shared_entity",
"shared-entity",
"thiserror",
"tokio",
"tokio-retry",
"tokio-stream",
"tokio-tungstenite",
"tracing",
"url",
"uuid",
"wasm-bindgen-futures",
"websocket",
"workspace-template",
"yrs",
]
@ -731,14 +744,14 @@ dependencies = [
[[package]]
name = "collab"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=3eef93f35f606edef2541888cb91cd6686d77225#3eef93f35f606edef2541888cb91cd6686d77225"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e3620ae820e921955b9c3ca3ffee24aad544f972#e3620ae820e921955b9c3ca3ffee24aad544f972"
dependencies = [
"anyhow",
"async-trait",
"bincode",
"bytes",
"js-sys",
"parking_lot",
"parking_lot 0.12.1",
"serde",
"serde_json",
"serde_repr",
@ -753,7 +766,7 @@ dependencies = [
[[package]]
name = "collab-database"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=3eef93f35f606edef2541888cb91cd6686d77225#3eef93f35f606edef2541888cb91cd6686d77225"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e3620ae820e921955b9c3ca3ffee24aad544f972#e3620ae820e921955b9c3ca3ffee24aad544f972"
dependencies = [
"anyhow",
"async-trait",
@ -765,7 +778,7 @@ dependencies = [
"lazy_static",
"lru",
"nanoid",
"parking_lot",
"parking_lot 0.12.1",
"serde",
"serde_json",
"serde_repr",
@ -781,14 +794,14 @@ dependencies = [
[[package]]
name = "collab-document"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=3eef93f35f606edef2541888cb91cd6686d77225#3eef93f35f606edef2541888cb91cd6686d77225"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e3620ae820e921955b9c3ca3ffee24aad544f972#e3620ae820e921955b9c3ca3ffee24aad544f972"
dependencies = [
"anyhow",
"collab",
"collab-entity",
"getrandom 0.2.10",
"nanoid",
"parking_lot",
"parking_lot 0.12.1",
"serde",
"serde_json",
"thiserror",
@ -800,7 +813,7 @@ dependencies = [
[[package]]
name = "collab-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=3eef93f35f606edef2541888cb91cd6686d77225#3eef93f35f606edef2541888cb91cd6686d77225"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e3620ae820e921955b9c3ca3ffee24aad544f972#e3620ae820e921955b9c3ca3ffee24aad544f972"
dependencies = [
"anyhow",
"bytes",
@ -814,13 +827,13 @@ dependencies = [
[[package]]
name = "collab-folder"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=3eef93f35f606edef2541888cb91cd6686d77225#3eef93f35f606edef2541888cb91cd6686d77225"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e3620ae820e921955b9c3ca3ffee24aad544f972#e3620ae820e921955b9c3ca3ffee24aad544f972"
dependencies = [
"anyhow",
"chrono",
"collab",
"collab-entity",
"parking_lot",
"parking_lot 0.12.1",
"serde",
"serde_json",
"serde_repr",
@ -840,7 +853,7 @@ dependencies = [
"collab-entity",
"collab-plugins",
"lib-infra",
"parking_lot",
"parking_lot 0.12.1",
"serde",
"serde_json",
"tokio",
@ -850,7 +863,7 @@ dependencies = [
[[package]]
name = "collab-plugins"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=3eef93f35f606edef2541888cb91cd6686d77225#3eef93f35f606edef2541888cb91cd6686d77225"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e3620ae820e921955b9c3ca3ffee24aad544f972#e3620ae820e921955b9c3ca3ffee24aad544f972"
dependencies = [
"anyhow",
"async-stream",
@ -866,7 +879,7 @@ dependencies = [
"indexed_db_futures",
"js-sys",
"lazy_static",
"parking_lot",
"parking_lot 0.12.1",
"rand 0.8.5",
"rocksdb",
"serde",
@ -889,12 +902,12 @@ dependencies = [
[[package]]
name = "collab-user"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=3eef93f35f606edef2541888cb91cd6686d77225#3eef93f35f606edef2541888cb91cd6686d77225"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=e3620ae820e921955b9c3ca3ffee24aad544f972#e3620ae820e921955b9c3ca3ffee24aad544f972"
dependencies = [
"anyhow",
"collab",
"collab-entity",
"parking_lot",
"parking_lot 0.12.1",
"serde",
"serde_json",
"tokio",
@ -1174,15 +1187,19 @@ dependencies = [
"collab-integrate",
"crossbeam-utils",
"flowy-codegen",
"flowy-config",
"flowy-core",
"flowy-date",
"flowy-derive",
"flowy-document",
"flowy-error",
"flowy-notification",
"flowy-server",
"flowy-server-pub",
"flowy-user",
"lazy_static",
"lib-dispatch",
"parking_lot",
"parking_lot 0.12.1",
"protobuf",
"serde",
"serde_json",
@ -1202,7 +1219,7 @@ dependencies = [
"hashbrown 0.14.3",
"lock_api",
"once_cell",
"parking_lot_core",
"parking_lot_core 0.9.8",
]
[[package]]
@ -1214,7 +1231,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
[[package]]
name = "database-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee3abdb27a2d056e7399b486354d26e802720c00#ee3abdb27a2d056e7399b486354d26e802720c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69c69f6474eaa531fd822e9353cc5955b98e45eb#69c69f6474eaa531fd822e9353cc5955b98e45eb"
dependencies = [
"anyhow",
"app-error",
@ -1497,7 +1514,7 @@ dependencies = [
"lib-dispatch",
"lib-infra",
"nanoid",
"parking_lot",
"parking_lot 0.12.1",
"protobuf",
"rand 0.8.5",
"serde",
@ -1704,7 +1721,7 @@ dependencies = [
"lib-dispatch",
"lib-infra",
"lib-log",
"parking_lot",
"parking_lot 0.12.1",
"serde",
"serde_json",
"serde_repr",
@ -1756,7 +1773,7 @@ dependencies = [
"lib-infra",
"lru",
"nanoid",
"parking_lot",
"parking_lot 0.12.1",
"protobuf",
"rayon",
"rust_decimal",
@ -1827,7 +1844,7 @@ dependencies = [
"lib-infra",
"lru",
"nanoid",
"parking_lot",
"parking_lot 0.12.1",
"protobuf",
"scraper 0.18.1",
"serde",
@ -1914,7 +1931,7 @@ dependencies = [
"lib-dispatch",
"lib-infra",
"nanoid",
"parking_lot",
"parking_lot 0.12.1",
"protobuf",
"serde_json",
"strum_macros 0.21.1",
@ -1983,7 +2000,7 @@ dependencies = [
"lib-dispatch",
"lib-infra",
"mime_guess",
"parking_lot",
"parking_lot 0.12.1",
"postgrest",
"reqwest",
"serde",
@ -2020,7 +2037,7 @@ dependencies = [
"libsqlite3-sys",
"openssl",
"openssl-sys",
"parking_lot",
"parking_lot 0.12.1",
"r2d2",
"scheduled-thread-pool",
"serde",
@ -2083,7 +2100,7 @@ dependencies = [
"lib-infra",
"nanoid",
"once_cell",
"parking_lot",
"parking_lot 0.12.1",
"protobuf",
"quickcheck",
"quickcheck_macros",
@ -2107,6 +2124,7 @@ name = "flowy-user-pub"
version = "0.1.0"
dependencies = [
"anyhow",
"base64 0.21.5",
"chrono",
"collab",
"collab-entity",
@ -2118,6 +2136,7 @@ dependencies = [
"serde_repr",
"tokio",
"tokio-stream",
"tracing",
"uuid",
]
@ -2381,7 +2400,7 @@ dependencies = [
[[package]]
name = "gotrue"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee3abdb27a2d056e7399b486354d26e802720c00#ee3abdb27a2d056e7399b486354d26e802720c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69c69f6474eaa531fd822e9353cc5955b98e45eb#69c69f6474eaa531fd822e9353cc5955b98e45eb"
dependencies = [
"anyhow",
"futures-util",
@ -2398,10 +2417,11 @@ dependencies = [
[[package]]
name = "gotrue-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee3abdb27a2d056e7399b486354d26e802720c00#ee3abdb27a2d056e7399b486354d26e802720c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69c69f6474eaa531fd822e9353cc5955b98e45eb#69c69f6474eaa531fd822e9353cc5955b98e45eb"
dependencies = [
"anyhow",
"app-error",
"chrono",
"jsonwebtoken",
"lazy_static",
"serde",
@ -2773,7 +2793,7 @@ dependencies = [
[[package]]
name = "infra"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee3abdb27a2d056e7399b486354d26e802720c00#ee3abdb27a2d056e7399b486354d26e802720c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69c69f6474eaa531fd822e9353cc5955b98e45eb#69c69f6474eaa531fd822e9353cc5955b98e45eb"
dependencies = [
"anyhow",
"reqwest",
@ -2831,9 +2851,9 @@ dependencies = [
[[package]]
name = "js-sys"
version = "0.3.64"
version = "0.3.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1"
dependencies = [
"wasm-bindgen",
]
@ -2878,7 +2898,7 @@ dependencies = [
"futures-util",
"getrandom 0.2.10",
"nanoid",
"parking_lot",
"parking_lot 0.12.1",
"pin-project",
"protobuf",
"serde",
@ -2889,6 +2909,7 @@ dependencies = [
"tracing",
"validator",
"wasm-bindgen",
"wasm-bindgen-futures",
]
[[package]]
@ -3390,6 +3411,17 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "parking_lot"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
dependencies = [
"instant",
"lock_api",
"parking_lot_core 0.8.6",
]
[[package]]
name = "parking_lot"
version = "0.12.1"
@ -3397,7 +3429,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core",
"parking_lot_core 0.9.8",
]
[[package]]
name = "parking_lot_core"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
dependencies = [
"cfg-if",
"instant",
"libc",
"redox_syscall 0.2.16",
"smallvec",
"winapi",
]
[[package]]
@ -3410,7 +3456,7 @@ dependencies = [
"libc",
"redox_syscall 0.3.5",
"smallvec",
"windows-targets",
"windows-targets 0.48.5",
]
[[package]]
@ -4038,7 +4084,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93"
dependencies = [
"log",
"parking_lot",
"parking_lot 0.12.1",
"scheduled-thread-pool",
]
@ -4191,7 +4237,7 @@ dependencies = [
[[package]]
name = "realtime-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee3abdb27a2d056e7399b486354d26e802720c00#ee3abdb27a2d056e7399b486354d26e802720c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69c69f6474eaa531fd822e9353cc5955b98e45eb#69c69f6474eaa531fd822e9353cc5955b98e45eb"
dependencies = [
"anyhow",
"bincode",
@ -4207,13 +4253,14 @@ dependencies = [
"serde_json",
"thiserror",
"tokio-tungstenite",
"websocket",
"yrs",
]
[[package]]
name = "realtime-protocol"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee3abdb27a2d056e7399b486354d26e802720c00#ee3abdb27a2d056e7399b486354d26e802720c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69c69f6474eaa531fd822e9353cc5955b98e45eb#69c69f6474eaa531fd822e9353cc5955b98e45eb"
dependencies = [
"anyhow",
"bincode",
@ -4229,6 +4276,15 @@ version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
[[package]]
name = "redox_syscall"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "redox_syscall"
version = "0.3.5"
@ -4567,7 +4623,7 @@ version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19"
dependencies = [
"parking_lot",
"parking_lot 0.12.1",
]
[[package]]
@ -4790,9 +4846,9 @@ dependencies = [
]
[[package]]
name = "shared_entity"
name = "shared-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee3abdb27a2d056e7399b486354d26e802720c00#ee3abdb27a2d056e7399b486354d26e802720c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69c69f6474eaa531fd822e9353cc5955b98e45eb#69c69f6474eaa531fd822e9353cc5955b98e45eb"
dependencies = [
"anyhow",
"app-error",
@ -4932,7 +4988,7 @@ checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b"
dependencies = [
"new_debug_unreachable",
"once_cell",
"parking_lot",
"parking_lot 0.12.1",
"phf_shared 0.10.0",
"precomputed-hash",
"serde",
@ -5220,7 +5276,7 @@ dependencies = [
"libc",
"mio",
"num_cpus",
"parking_lot",
"parking_lot 0.12.1",
"pin-project-lite",
"signal-hook-registry",
"socket2 0.5.5",
@ -5273,7 +5329,7 @@ dependencies = [
"futures-channel",
"futures-util",
"log",
"parking_lot",
"parking_lot 0.12.1",
"percent-encoding",
"phf 0.11.2",
"pin-project-lite",
@ -5856,9 +5912,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.89"
version = "0.2.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e"
checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@ -5866,9 +5922,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.89"
version = "0.2.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826"
checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd"
dependencies = [
"bumpalo",
"log",
@ -5881,9 +5937,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.37"
version = "0.4.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03"
checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461"
dependencies = [
"cfg-if",
"js-sys",
@ -5893,9 +5949,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.89"
version = "0.2.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2"
checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@ -5903,9 +5959,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.89"
version = "0.2.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7"
dependencies = [
"proc-macro2",
"quote",
@ -5916,9 +5972,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.89"
version = "0.2.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f"
checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b"
[[package]]
name = "wasm-streams"
@ -5933,6 +5989,21 @@ dependencies = [
"web-sys",
]
[[package]]
name = "wasm-timer"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f"
dependencies = [
"futures",
"js-sys",
"parking_lot 0.11.2",
"pin-utils",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]]
name = "web-sys"
version = "0.3.64"
@ -5949,6 +6020,23 @@ version = "0.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc"
[[package]]
name = "websocket"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69c69f6474eaa531fd822e9353cc5955b98e45eb#69c69f6474eaa531fd822e9353cc5955b98e45eb"
dependencies = [
"futures-channel",
"futures-util",
"http",
"httparse",
"js-sys",
"thiserror",
"tokio",
"tokio-tungstenite",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "which"
version = "4.4.2"
@ -6008,7 +6096,7 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
dependencies = [
"windows-targets",
"windows-targets 0.48.5",
]
[[package]]
@ -6017,7 +6105,7 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets",
"windows-targets 0.48.5",
]
[[package]]
@ -6026,13 +6114,28 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
"windows_aarch64_gnullvm 0.48.5",
"windows_aarch64_msvc 0.48.5",
"windows_i686_gnu 0.48.5",
"windows_i686_msvc 0.48.5",
"windows_x86_64_gnu 0.48.5",
"windows_x86_64_gnullvm 0.48.5",
"windows_x86_64_msvc 0.48.5",
]
[[package]]
name = "windows-targets"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
dependencies = [
"windows_aarch64_gnullvm 0.52.0",
"windows_aarch64_msvc 0.52.0",
"windows_i686_gnu 0.52.0",
"windows_i686_msvc 0.52.0",
"windows_x86_64_gnu 0.52.0",
"windows_x86_64_gnullvm 0.52.0",
"windows_x86_64_msvc 0.52.0",
]
[[package]]
@ -6041,42 +6144,84 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
name = "winnow"
version = "0.5.30"
@ -6099,7 +6244,7 @@ dependencies = [
[[package]]
name = "workspace-template"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee3abdb27a2d056e7399b486354d26e802720c00#ee3abdb27a2d056e7399b486354d26e802720c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69c69f6474eaa531fd822e9353cc5955b98e45eb#69c69f6474eaa531fd822e9353cc5955b98e45eb"
dependencies = [
"anyhow",
"async-trait",

View File

@ -66,7 +66,7 @@ serde_json = "1.0.108"
serde = "1.0.194"
protobuf = { version = "2.28.0" }
diesel = { version = "2.1.0", features = ["sqlite", "chrono", "r2d2"] }
uuid = { version = "1.5.0", features = ["serde", "v4"] }
uuid = { version = "1.5.0", features = ["serde", "v4", "v5"] }
serde_repr = "0.1"
parking_lot = "0.12"
futures = "0.3.29"
@ -105,7 +105,7 @@ incremental = false
# Run the script:
# scripts/tool/update_client_api_rev.sh new_rev_id
# ⚠️⚠️⚠️️
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "ee3abdb27a2d056e7399b486354d26e802720c00" }
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "69c69f6474eaa531fd822e9353cc5955b98e45eb" }
# Please use the following script to update collab.
# Working directory: frontend
#
@ -115,10 +115,10 @@ client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "ee3
# To switch to the local path, run:
# scripts/tool/update_collab_source.sh
# ⚠️⚠️⚠️️
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "3eef93f35f606edef2541888cb91cd6686d77225" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "3eef93f35f606edef2541888cb91cd6686d77225" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "3eef93f35f606edef2541888cb91cd6686d77225" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "3eef93f35f606edef2541888cb91cd6686d77225" }
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "3eef93f35f606edef2541888cb91cd6686d77225" }
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "3eef93f35f606edef2541888cb91cd6686d77225" }
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "3eef93f35f606edef2541888cb91cd6686d77225" }
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e3620ae820e921955b9c3ca3ffee24aad544f972" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e3620ae820e921955b9c3ca3ffee24aad544f972" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e3620ae820e921955b9c3ca3ffee24aad544f972" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e3620ae820e921955b9c3ca3ffee24aad544f972" }
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e3620ae820e921955b9c3ca3ffee24aad544f972" }
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e3620ae820e921955b9c3ca3ffee24aad544f972" }
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "e3620ae820e921955b9c3ca3ffee24aad544f972" }

View File

@ -19,3 +19,55 @@ pub struct ProtoCache {
pub structs: Vec<String>,
pub enums: Vec<String>,
}
pub enum Project {
Tauri,
Web { relative_path: String },
Native,
}
impl Project {
pub fn dst(&self) -> String {
match self {
Project::Tauri => "appflowy_tauri/src/services/backend".to_string(),
Project::Web { .. } => "appflowy_web/src/services/backend".to_string(),
Project::Native => panic!("Native project is not supported yet."),
}
}
pub fn event_root(&self) -> String {
match self {
Project::Tauri => "../../".to_string(),
Project::Web { relative_path } => relative_path.to_string(),
Project::Native => panic!("Native project is not supported yet."),
}
}
pub fn model_root(&self) -> String {
match self {
Project::Tauri => "../../".to_string(),
Project::Web { relative_path } => relative_path.to_string(),
Project::Native => panic!("Native project is not supported yet."),
}
}
pub fn event_imports(&self) -> String {
match self {
Project::Tauri => r#"
/// Auto generate. Do not edit
import { Ok, Err, Result } from "ts-results";
import { invoke } from "@tauri-apps/api/tauri";
import * as pb from "../..";
"#
.to_string(),
Project::Web { .. } => r#"
/// Auto generate. Do not edit
import { Ok, Err, Result } from "ts-results";
import { invoke } from "@/application/app.ts";
import * as pb from "../..";
"#
.to_string(),
Project::Native => panic!("Native project is not supported yet."),
}
}
}

View File

@ -7,6 +7,7 @@ mod proto_info;
mod template;
use crate::util::path_string_with_component;
use crate::Project;
use itertools::Itertools;
use log::info;
pub use proto_gen::*;
@ -17,16 +18,10 @@ use std::path::{Path, PathBuf};
use std::process::Command;
use walkdir::WalkDir;
pub fn gen(crate_name: &str) {
let crate_path = std::fs::canonicalize(".")
.unwrap()
.as_path()
.display()
.to_string();
pub fn dart_gen(crate_name: &str) {
// 1. generate the proto files to proto_file_dir
#[cfg(feature = "proto_gen")]
let proto_crates = gen_proto_files(crate_name, &crate_path);
let proto_crates = gen_proto_files(crate_name);
for proto_crate in proto_crates {
let mut proto_file_paths = vec![];
@ -70,6 +65,55 @@ pub fn gen(crate_name: &str) {
&protoc_bin_path,
);
// 3. generate the protobuf files(Rust)
generate_rust_protobuf_files(
&protoc_bin_path,
&proto_file_paths,
&proto_file_output_path,
&protobuf_output_path,
);
}
}
#[allow(unused_variables)]
pub fn ts_gen(crate_name: &str, project: Project) {
// 1. generate the proto files to proto_file_dir
#[cfg(feature = "proto_gen")]
let proto_crates = gen_proto_files(crate_name);
for proto_crate in proto_crates {
let mut proto_file_paths = vec![];
let mut file_names = vec![];
let proto_file_output_path = proto_crate
.proto_output_path()
.to_str()
.unwrap()
.to_string();
let protobuf_output_path = proto_crate
.protobuf_crate_path()
.to_str()
.unwrap()
.to_string();
for (path, file_name) in WalkDir::new(&proto_file_output_path)
.into_iter()
.filter_map(|e| e.ok())
.map(|e| {
let path = e.path().to_str().unwrap().to_string();
let file_name = e.path().file_stem().unwrap().to_str().unwrap().to_string();
(path, file_name)
})
{
if path.ends_with(".proto") {
// https://stackoverflow.com/questions/49077147/how-can-i-force-build-rs-to-run-again-without-cleaning-my-whole-project
println!("cargo:rerun-if-changed={}", path);
proto_file_paths.push(path);
file_names.push(file_name);
}
}
let protoc_bin_path = protoc_bin_vendored::protoc_bin_path().unwrap();
// 2. generate the protobuf files(Dart)
#[cfg(feature = "ts")]
generate_ts_protobuf_files(
crate_name,
@ -77,6 +121,7 @@ pub fn gen(crate_name: &str) {
&proto_file_paths,
&file_names,
&protoc_bin_path,
&project,
);
// 3. generate the protobuf files(Rust)
@ -111,14 +156,14 @@ fn generate_ts_protobuf_files(
paths: &[String],
file_names: &Vec<String>,
protoc_bin_path: &Path,
project: &Project,
) {
let root = std::env::var("CARGO_MAKE_WORKING_DIRECTORY").unwrap_or("../../".to_string());
let tauri_backend_service_path = std::env::var("TAURI_BACKEND_SERVICE_PATH")
.unwrap_or("appflowy_tauri/src/services/backend".to_string());
let root = project.model_root();
let backend_service_path = project.dst();
let mut output = PathBuf::new();
output.push(root);
output.push(tauri_backend_service_path);
output.push(backend_service_path);
output.push("models");
output.push(name);
@ -136,6 +181,7 @@ fn generate_ts_protobuf_files(
// panic!("Generate ts pb file failed: {}, {:?}", path, err);
// }
println!("cargo:rerun-if-changed={}", output.to_str().unwrap());
let result = cmd_lib::run_cmd! {
${protoc_bin_path} --ts_out=${output} --proto_path=${proto_file_output_path} ${path}
};
@ -211,7 +257,7 @@ fn generate_dart_protobuf_files(
});
let protobuf_dart = path_string_with_component(&output, vec!["protobuf.dart"]);
println!("cargo:rerun-if-changed={}", protobuf_dart);
match std::fs::OpenOptions::new()
.create(true)
.write(true)
@ -282,8 +328,14 @@ pub fn check_pb_dart_plugin() {
}
#[cfg(feature = "proto_gen")]
fn gen_proto_files(crate_name: &str, crate_path: &str) -> Vec<ProtobufCrate> {
let crate_context = ProtoGenerator::gen(crate_name, crate_path);
pub fn gen_proto_files(crate_name: &str) -> Vec<ProtobufCrate> {
let crate_path = std::fs::canonicalize(".")
.unwrap()
.as_path()
.display()
.to_string();
let crate_context = ProtoGenerator::gen(crate_name, &crate_path);
let proto_crates = crate_context
.iter()
.map(|info| info.protobuf_crate.clone())

View File

@ -4,6 +4,7 @@ use crate::ast::EventASTContext;
use crate::flowy_toml::{parse_crate_config_from, CrateConfig};
use crate::ts_event::event_template::{EventRenderContext, EventTemplate};
use crate::util::{is_crate_dir, is_hidden, path_string_with_component, read_file};
use crate::Project;
use flowy_ast::ASTResult;
use std::collections::HashSet;
use std::fs::File;
@ -12,10 +13,9 @@ use std::path::PathBuf;
use syn::Item;
use walkdir::WalkDir;
pub fn gen(crate_name: &str) {
let root = std::env::var("CARGO_MAKE_WORKING_DIRECTORY").unwrap_or("../../".to_string());
let tauri_backend_service_path = std::env::var("TAURI_BACKEND_SERVICE_PATH")
.unwrap_or("appflowy_tauri/src/services/backend".to_string());
pub fn gen(crate_name: &str, project: Project) {
let root = project.event_root();
let backend_service_path = project.dst();
let crate_path = std::fs::canonicalize(".")
.unwrap()
@ -29,7 +29,8 @@ pub fn gen(crate_name: &str) {
.collect::<Vec<_>>();
let event_render_ctx = ast_to_event_render_ctx(event_ast.as_ref());
let mut render_result = TS_HEADER.to_string();
let mut render_result = project.event_imports();
for (index, render_ctx) in event_render_ctx.into_iter().enumerate() {
let mut event_template = EventTemplate::new();
@ -39,7 +40,7 @@ pub fn gen(crate_name: &str) {
}
render_result.push_str(TS_FOOTER);
let ts_event_folder: PathBuf = [&root, &tauri_backend_service_path, "events", crate_name]
let ts_event_folder: PathBuf = [&root, &backend_service_path, "events", crate_name]
.iter()
.collect();
if !ts_event_folder.as_path().exists() {
@ -196,12 +197,5 @@ pub fn ast_to_event_render_ctx(ast: &[EventASTContext]) -> Vec<EventRenderContex
.collect::<Vec<EventRenderContext>>()
}
const TS_HEADER: &str = r#"
/// Auto generate. Do not edit
import { Ok, Err, Result } from "ts-results";
import { invoke } from "@tauri-apps/api/tauri";
import * as pb from "../..";
"#;
const TS_FOOTER: &str = r#"
"#;

View File

@ -22,4 +22,4 @@ lib-infra = { workspace = true }
[features]
default = []
wasm_build = ["collab-plugins/wasm_build"]
enable_wasm = ["collab-plugins/wasm_build"]

View File

@ -4,7 +4,7 @@ use std::sync::{Arc, Weak};
use crate::CollabKVDB;
use anyhow::Error;
use collab::core::collab::{CollabDocState, MutexCollab};
use collab::preclude::{CollabBuilder, CollabPlugin};
use collab::preclude::CollabBuilder;
use collab_entity::{CollabObject, CollabType};
use collab_plugins::connect_state::{CollabConnectReachability, CollabConnectState};
use collab_plugins::local_storage::kv::snapshot::SnapshotPersistence;
@ -16,8 +16,9 @@ if_wasm! {
use collab_plugins::local_storage::indexeddb::IndexeddbDiskPlugin;
}
pub use crate::plugin_provider::CollabCloudPluginProvider;
use collab_plugins::local_storage::CollabPersistenceConfig;
use lib_infra::future::Fut;
use lib_infra::{if_native, if_wasm};
use parking_lot::{Mutex, RwLock};
use tracing::trace;
@ -64,31 +65,6 @@ impl Display for CollabPluginProviderContext {
}
}
pub trait CollabCloudPluginProvider: Send + Sync + 'static {
fn provider_type(&self) -> CollabPluginProviderType;
fn get_plugins(&self, context: CollabPluginProviderContext) -> Fut<Vec<Arc<dyn CollabPlugin>>>;
fn is_sync_enabled(&self) -> bool;
}
impl<T> CollabCloudPluginProvider for Arc<T>
where
T: CollabCloudPluginProvider,
{
fn provider_type(&self) -> CollabPluginProviderType {
(**self).provider_type()
}
fn get_plugins(&self, context: CollabPluginProviderContext) -> Fut<Vec<Arc<dyn CollabPlugin>>> {
(**self).get_plugins(context)
}
fn is_sync_enabled(&self) -> bool {
(**self).is_sync_enabled()
}
}
pub struct AppFlowyCollabBuilder {
network_reachability: CollabConnectReachability,
workspace_id: RwLock<Option<String>>,

View File

@ -2,8 +2,25 @@ pub use collab::core::collab::MutexCollab;
pub use collab::preclude::Snapshot;
pub use collab_plugins::local_storage::CollabPersistenceConfig;
pub use collab_plugins::CollabKVDB;
use collab_plugins::{if_native, if_wasm};
pub mod collab_builder;
pub mod config;
if_native! {
mod native;
mod plugin_provider {
pub use crate::native::plugin_provider::*;
}
}
if_wasm! {
mod wasm;
mod plugin_provider {
pub use crate::wasm::plugin_provider::*;
}
}
pub use collab_plugins::local_storage::kv::doc::CollabKVAction;
pub use collab_plugins::local_storage::kv::error::PersistenceError;
pub use collab_plugins::local_storage::kv::snapshot::{CollabSnapshot, SnapshotPersistence};

View File

@ -0,0 +1 @@
pub mod plugin_provider;

View File

@ -0,0 +1,29 @@
use crate::collab_builder::{CollabPluginProviderContext, CollabPluginProviderType};
use collab::preclude::CollabPlugin;
use lib_infra::future::Fut;
use std::sync::Arc;
pub trait CollabCloudPluginProvider: Send + Sync + 'static {
fn provider_type(&self) -> CollabPluginProviderType;
fn get_plugins(&self, context: CollabPluginProviderContext) -> Fut<Vec<Arc<dyn CollabPlugin>>>;
fn is_sync_enabled(&self) -> bool;
}
impl<T> CollabCloudPluginProvider for Arc<T>
where
T: CollabCloudPluginProvider,
{
fn provider_type(&self) -> CollabPluginProviderType {
(**self).provider_type()
}
fn get_plugins(&self, context: CollabPluginProviderContext) -> Fut<Vec<Arc<dyn CollabPlugin>>> {
(**self).get_plugins(context)
}
fn is_sync_enabled(&self) -> bool {
(**self).is_sync_enabled()
}
}

View File

@ -0,0 +1 @@
pub mod plugin_provider;

View File

@ -0,0 +1,30 @@
use crate::collab_builder::{CollabPluginProviderContext, CollabPluginProviderType};
use collab::preclude::CollabPlugin;
use lib_infra::future::Fut;
use std::rc::Rc;
use std::sync::Arc;
pub trait CollabCloudPluginProvider: 'static {
fn provider_type(&self) -> CollabPluginProviderType;
fn get_plugins(&self, context: CollabPluginProviderContext) -> Fut<Vec<Arc<dyn CollabPlugin>>>;
fn is_sync_enabled(&self) -> bool;
}
impl<T> CollabCloudPluginProvider for Rc<T>
where
T: CollabCloudPluginProvider,
{
fn provider_type(&self) -> CollabPluginProviderType {
(**self).provider_type()
}
fn get_plugins(&self, context: CollabPluginProviderContext) -> Fut<Vec<Arc<dyn CollabPlugin>>> {
(**self).get_plugins(context)
}
fn is_sync_enabled(&self) -> bool {
(**self).is_sync_enabled()
}
}

View File

@ -29,13 +29,17 @@ tracing.workspace = true
lib-dispatch = { workspace = true }
#flowy-core = { workspace = true, features = ["profiling"] }
flowy-core = { workspace = true }
flowy-notification = { workspace = true }
flowy-notification = { workspace = true, features = ["dart"] }
flowy-document = { workspace = true, features = ["dart"] }
flowy-config = { workspace = true, features = ["dart"] }
flowy-user = { workspace = true, features = ["dart"] }
flowy-date = { workspace = true, features = ["dart"] }
flowy-server = { workspace = true }
flowy-server-pub = { workspace = true}
collab-integrate = { workspace = true }
flowy-derive.workspace = true
serde_yaml = "0.9.27"
flowy-error = { workspace = true, features = ["impl_from_sqlite", "impl_from_dispatch_error", "impl_from_appflowy_cloud", "impl_from_reqwest", "impl_from_serde"] }
flowy-error = { workspace = true, features = ["impl_from_sqlite", "impl_from_dispatch_error", "impl_from_appflowy_cloud", "impl_from_reqwest", "impl_from_serde", "dart"] }
[features]
default = ["dart", "rev-sqlite"]

View File

@ -1,3 +1,3 @@
fn main() {
flowy_codegen::protobuf_file::gen(env!("CARGO_PKG_NAME"));
flowy_codegen::protobuf_file::dart_gen(env!("CARGO_PKG_NAME"));
}

View File

@ -100,7 +100,7 @@ pub extern "C" fn async_event(port: i64, input: *const u8, len: usize) {
Some(dispatcher) => dispatcher,
};
AFPluginDispatcher::boxed_async_send_with_callback(
dispatcher,
dispatcher.as_ref(),
request,
move |resp: AFPluginEventResponse| {
trace!("[FFI]: Post data to dart through {} port", port);

View File

@ -56,4 +56,4 @@ zip = "0.6.6"
default = ["supabase_cloud_test"]
dart = ["flowy-core/dart"]
supabase_cloud_test = []
single_thread = ["flowy-core/single_thread"]
single_thread = ["flowy-core/enable_wasm"]

View File

@ -50,7 +50,7 @@ impl EventBuilder {
pub async fn async_send(mut self) -> Self {
let request = self.get_request();
let resp = AFPluginDispatcher::async_send(self.dispatch(), request).await;
let resp = AFPluginDispatcher::async_send(self.dispatch().as_ref(), request).await;
self.context.response = Some(resp);
self
}

View File

@ -133,7 +133,7 @@ pub fn document_from_document_doc_state(doc_id: &str, doc_state: CollabDocState)
Document::from_doc_state(CollabOrigin::Empty, doc_state, doc_id, vec![]).unwrap()
}
#[cfg(feature = "single_thread")]
#[cfg(target_arch = "wasm32")]
async fn init_core(config: AppFlowyCoreConfig) -> AppFlowyCore {
// let runtime = tokio::runtime::Runtime::new().unwrap();
// let local_set = tokio::task::LocalSet::new();
@ -141,7 +141,7 @@ async fn init_core(config: AppFlowyCoreConfig) -> AppFlowyCore {
AppFlowyCore::new(config).await
}
#[cfg(not(feature = "single_thread"))]
#[cfg(not(target_arch = "wasm32"))]
async fn init_core(config: AppFlowyCoreConfig) -> AppFlowyCore {
std::thread::spawn(|| AppFlowyCore::new(config))
.join()

View File

@ -12,7 +12,7 @@ use uuid::Uuid;
use flowy_notification::entities::SubscribeObject;
use flowy_notification::NotificationSender;
use flowy_server::supabase::define::{USER_DEVICE_ID, USER_EMAIL, USER_SIGN_IN_URL, USER_UUID};
use flowy_server::af_cloud::define::{USER_DEVICE_ID, USER_EMAIL, USER_SIGN_IN_URL, USER_UUID};
use flowy_server_pub::af_cloud_config::AFCloudConfiguration;
use flowy_server_pub::AuthenticatorType;
use flowy_user::entities::{
@ -69,7 +69,7 @@ impl EventIntegrationTest {
.unwrap();
let request = AFPluginRequest::new(SignUp).payload(payload);
let user_profile = AFPluginDispatcher::async_send(self.appflowy_core.dispatcher(), request)
let user_profile = AFPluginDispatcher::async_send(&self.appflowy_core.dispatcher(), request)
.await
.parse::<UserProfilePB, FlowyError>()
.unwrap()

View File

@ -21,4 +21,4 @@ flowy-codegen.workspace = true
[features]
dart = ["flowy-codegen/dart"]
ts = ["flowy-codegen/ts"]
tauri_ts = ["flowy-codegen/ts"]

View File

@ -1,10 +1,13 @@
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);
{
flowy_codegen::protobuf_file::dart_gen(env!("CARGO_PKG_NAME"));
flowy_codegen::dart_event::gen(env!("CARGO_PKG_NAME"));
}
#[cfg(feature = "ts")]
flowy_codegen::ts_event::gen(crate_name);
#[cfg(feature = "tauri_ts")]
{
flowy_codegen::ts_event::gen(env!("CARGO_PKG_NAME"), flowy_codegen::Project::Tauri);
flowy_codegen::protobuf_file::ts_gen(env!("CARGO_PKG_NAME"), flowy_codegen::Project::Tauri);
}
}

View File

@ -18,7 +18,7 @@ flowy-sqlite = { workspace = true }
flowy-document = { workspace = true }
flowy-document-pub = { workspace = true }
flowy-error = { workspace = true }
flowy-server = { workspace = true }
flowy-server = { workspace = true, features = ["enable_supabase"] }
flowy-server-pub = { workspace = true }
flowy-config = { workspace = true }
flowy-date = { workspace = true }
@ -55,21 +55,15 @@ http_sync = []
native_sync = []
use_bunyan = ["lib-log/use_bunyan"]
dart = [
"flowy-user/dart",
"flowy-date/dart",
"flowy-folder/dart",
"flowy-database2/dart",
"flowy-document/dart",
"flowy-config/dart",
]
ts = [
"flowy-user/ts",
"flowy-date/ts",
"flowy-user/tauri_ts",
"flowy-folder/ts",
"flowy-database2/ts",
"flowy-document/ts",
"flowy-config/ts",
"flowy-config/tauri_ts",
]
rev-sqlite = ["flowy-user/rev-sqlite"]
openssl_vendored = ["flowy-sqlite/openssl_vendored"]
single_thread = ["collab-integrate/wasm_build"]
enable_wasm = ["collab-integrate/enable_wasm"]

View File

@ -24,7 +24,9 @@ use flowy_folder_pub::cloud::{
use flowy_server_pub::af_cloud_config::AFCloudConfiguration;
use flowy_server_pub::supabase_config::SupabaseConfiguration;
use flowy_storage::ObjectValue;
use flowy_user_pub::cloud::{UserCloudService, UserCloudServiceProvider};
use flowy_user_pub::cloud::{
UserCloudService, UserCloudServiceProvider, UserCloudServiceProviderBase,
};
use flowy_user_pub::entities::{Authenticator, UserTokenState};
use lib_infra::future::{to_fut, Fut, FutureResult};
@ -63,8 +65,9 @@ impl ObjectStorageService for ServerProvider {
})
}
}
impl UserCloudServiceProvider for ServerProvider {}
impl UserCloudServiceProvider for ServerProvider {
impl UserCloudServiceProviderBase for ServerProvider {
fn set_token(&self, token: &str) -> Result<(), FlowyError> {
let server = self.get_server()?;
server.set_token(token)?;

View File

@ -10,7 +10,7 @@ use flowy_document::manager::DocumentManager;
use flowy_error::FlowyResult;
use flowy_folder::manager::{FolderInitDataSource, FolderManager};
use flowy_user::event_map::UserStatusCallback;
use flowy_user_pub::cloud::{UserCloudConfig, UserCloudServiceProvider};
use flowy_user_pub::cloud::{UserCloudConfig, UserCloudServiceProviderBase};
use flowy_user_pub::entities::{Authenticator, UserProfile, UserWorkspace};
use lib_infra::future::{to_fut, Fut};

View File

@ -53,13 +53,13 @@ pub struct AppFlowyCore {
}
impl AppFlowyCore {
#[cfg(feature = "single_thread")]
#[cfg(target_arch = "wasm32")]
pub async fn new(config: AppFlowyCoreConfig) -> Self {
let runtime = Arc::new(AFPluginRuntime::new().unwrap());
Self::init(config, runtime).await
}
#[cfg(not(feature = "single_thread"))]
#[cfg(not(target_arch = "wasm32"))]
pub fn new(config: AppFlowyCoreConfig) -> Self {
let runtime = Arc::new(AFPluginRuntime::new().unwrap());
let cloned_runtime = runtime.clone();

View File

@ -55,5 +55,5 @@ flowy-codegen.workspace = true
[features]
dart = ["flowy-codegen/dart", "flowy-notification/dart"]
ts = ["flowy-codegen/ts", "flowy-notification/ts"]
wasm_build = ["collab-plugins/wasm_build"]
ts = ["flowy-codegen/ts", "flowy-notification/tauri_ts"]
enable_wasm = ["collab-plugins/wasm_build"]

View File

@ -1,10 +1,14 @@
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);
{
flowy_codegen::protobuf_file::dart_gen(crate_name);
flowy_codegen::dart_event::gen(crate_name);
}
#[cfg(feature = "ts")]
flowy_codegen::ts_event::gen(crate_name);
{
flowy_codegen::ts_event::gen(crate_name, flowy_codegen::Project::Tauri);
flowy_codegen::protobuf_file::ts_gen(crate_name, flowy_codegen::Project::Tauri);
}
}

View File

@ -21,8 +21,7 @@ fancy-regex = { version = "0.11.0" }
[features]
dart = ["flowy-codegen/dart"]
ts = ["flowy-codegen/ts"]
wasm_build = ["lib-dispatch/single_thread", "flowy-codegen/ts"]
tauri_ts = ["flowy-codegen/ts"]
[build-dependencies]
flowy-codegen.workspace = true

View File

@ -1,10 +1,13 @@
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);
{
flowy_codegen::protobuf_file::dart_gen(env!("CARGO_PKG_NAME"));
flowy_codegen::dart_event::gen(env!("CARGO_PKG_NAME"));
}
#[cfg(feature = "ts")]
flowy_codegen::ts_event::gen(crate_name);
#[cfg(feature = "tauri_ts")]
{
flowy_codegen::ts_event::gen(env!("CARGO_PKG_NAME"), flowy_codegen::Project::Tauri);
flowy_codegen::protobuf_file::ts_gen(env!("CARGO_PKG_NAME"), flowy_codegen::Project::Tauri);
}
}

View File

@ -17,7 +17,7 @@ flowy-document-pub = { workspace = true }
flowy-storage = { workspace = true }
flowy-derive.workspace = true
flowy-notification = { workspace = true }
flowy-error = { path = "../flowy-error", features = ["impl_from_serde", "impl_from_dispatch_error", "impl_from_collab_document"] }
flowy-error = { path = "../flowy-error", features = ["impl_from_serde", "impl_from_dispatch_error", "impl_from_collab_document", "impl_from_collab_persistence"] }
lib-dispatch = { workspace = true }
lib-infra = { workspace = true }
validator = { version = "0.16.0", features = ["derive"] }
@ -50,16 +50,12 @@ collab-integrate = { workspace = true }
flowy-codegen.workspace = true
[features]
dart = ["flowy-codegen/dart", "flowy-notification/dart"]
ts = ["flowy-codegen/ts", "flowy-notification/ts"]
wasm_build = [
dart = ["flowy-codegen/dart"]
tauri_ts = ["flowy-codegen/ts"]
web_ts = [
"flowy-codegen/ts",
"flowy-notification/ts",
"collab-plugins/wasm_build",
"collab/wasm_build",
"lib-dispatch/wasm_build",
"collab-document/wasm_build",
"flowy-error/wasm_build",
"flowy-notification/wasm_build"
]

View File

@ -1,10 +1,29 @@
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);
{
flowy_codegen::protobuf_file::dart_gen(env!("CARGO_PKG_NAME"));
flowy_codegen::dart_event::gen(env!("CARGO_PKG_NAME"));
}
#[cfg(feature = "ts")]
flowy_codegen::ts_event::gen(crate_name);
#[cfg(feature = "tauri_ts")]
{
flowy_codegen::ts_event::gen(env!("CARGO_PKG_NAME"), flowy_codegen::Project::Tauri);
flowy_codegen::protobuf_file::ts_gen(env!("CARGO_PKG_NAME"), flowy_codegen::Project::Tauri);
}
#[cfg(feature = "web_ts")]
{
flowy_codegen::ts_event::gen(
env!("CARGO_PKG_NAME"),
flowy_codegen::Project::Web {
relative_path: "../../".to_string(),
},
);
flowy_codegen::protobuf_file::ts_gen(
env!("CARGO_PKG_NAME"),
flowy_codegen::Project::Web {
relative_path: "../../".to_string(),
},
);
}
}

View File

@ -25,6 +25,7 @@ use collab_integrate::CollabPersistenceConfig;
use flowy_document_pub::cloud::DocumentCloudService;
use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult};
use flowy_storage::ObjectStorageService;
use lib_dispatch::prelude::af_spawn;
use crate::document::MutexDocument;
use crate::entities::{
@ -260,7 +261,7 @@ impl DocumentManager {
// let the upload happen in the background
let clone_url = url.clone();
tokio::spawn(async move {
af_spawn(async move {
if let Err(e) = storage_service.put_object(clone_url, object_value).await {
error!("upload file failed: {}", e);
}
@ -300,7 +301,7 @@ impl DocumentManager {
// delete from cloud
let storage_service = self.storage_service_upgrade()?;
tokio::spawn(async move {
af_spawn(async move {
if let Err(e) = storage_service.delete_object(url).await {
// TODO: add WAL to log the delete operation.
// keep a list of files to be deleted, and retry later

View File

@ -20,4 +20,4 @@ base64 = "0.21.2"
getrandom = { version = "0.2", features = ["js"]}
[features]
wasm_build = []
enable_wasm = []

View File

@ -36,15 +36,16 @@ client-api = { version = "0.1.0", optional = true }
impl_from_dispatch_error = ["lib-dispatch"]
impl_from_serde = []
impl_from_reqwest = ["reqwest"]
impl_from_collab_persistence = ["collab-plugins"]
impl_from_collab_document = ["collab-document", "impl_from_reqwest", "collab-plugins"]
impl_from_collab_database= ["collab-database"]
impl_from_url = ["url"]
wasm_build = ["lib-dispatch/wasm_build"]
impl_from_sqlite = ["flowy-sqlite", "r2d2"]
impl_from_appflowy_cloud = ["client-api"]
dart = ["flowy-codegen/dart"]
ts = ["flowy-codegen/ts"]
tauri_ts = ["flowy-codegen/ts"]
web_ts = ["flowy-codegen/ts"]
[build-dependencies]
flowy-codegen = { workspace = true, features = [

View File

@ -1,3 +1,15 @@
fn main() {
flowy_codegen::protobuf_file::gen("flowy-error");
#[cfg(feature = "dart")]
flowy_codegen::protobuf_file::dart_gen(env!("CARGO_PKG_NAME"));
#[cfg(feature = "tauri_ts")]
flowy_codegen::protobuf_file::ts_gen(env!("CARGO_PKG_NAME"), flowy_codegen::Project::Tauri);
#[cfg(feature = "web_ts")]
flowy_codegen::protobuf_file::ts_gen(
env!("CARGO_PKG_NAME"),
flowy_codegen::Project::Web {
relative_path: "../../".to_string(),
},
);
}

View File

@ -1,28 +1,10 @@
use crate::FlowyError;
#[cfg(feature = "impl_from_collab_database")]
use collab_database::error::DatabaseError;
#[cfg(feature = "impl_from_collab_document")]
use collab_document::error::DocumentError;
#[cfg(feature = "impl_from_collab_document")]
use collab_plugins::local_storage::kv::PersistenceError;
use crate::{ErrorCode, FlowyError};
#[cfg(feature = "impl_from_collab_document")]
impl From<PersistenceError> for FlowyError {
fn from(err: PersistenceError) -> Self {
match err {
PersistenceError::UnexpectedEmptyUpdates => FlowyError::new(ErrorCode::RecordNotFound, err),
#[cfg(not(target_arch = "wasm32"))]
PersistenceError::RocksdbCorruption(_) => FlowyError::new(ErrorCode::RocksdbCorruption, err),
#[cfg(not(target_arch = "wasm32"))]
PersistenceError::RocksdbIOError(_) => FlowyError::new(ErrorCode::RocksdbIOError, err),
_ => FlowyError::new(ErrorCode::RocksdbInternal, err),
}
}
}
#[cfg(feature = "impl_from_collab_database")]
impl From<DatabaseError> for FlowyError {
fn from(error: DatabaseError) -> Self {

View File

@ -0,0 +1,17 @@
use crate::{ErrorCode, FlowyError};
#[cfg(feature = "impl_from_collab_persistence")]
use collab_plugins::local_storage::kv::PersistenceError;
#[cfg(feature = "impl_from_collab_persistence")]
impl From<PersistenceError> for FlowyError {
fn from(err: PersistenceError) -> Self {
match err {
PersistenceError::UnexpectedEmptyUpdates => FlowyError::new(ErrorCode::RecordNotFound, err),
#[cfg(not(target_arch = "wasm32"))]
PersistenceError::RocksdbCorruption(_) => FlowyError::new(ErrorCode::RocksdbCorruption, err),
#[cfg(not(target_arch = "wasm32"))]
PersistenceError::RocksdbIOError(_) => FlowyError::new(ErrorCode::RocksdbIOError, err),
_ => FlowyError::new(ErrorCode::RocksdbInternal, err),
}
}
}

View File

@ -16,7 +16,11 @@ pub mod database;
#[cfg(feature = "impl_from_collab_document")]
pub mod collab;
#[cfg(feature = "impl_from_collab_persistence")]
mod collab_persistence;
#[cfg(feature = "impl_from_appflowy_cloud")]
mod cloud;
#[cfg(feature = "impl_from_url")]
mod url;

View File

@ -38,6 +38,6 @@ flowy-codegen.workspace = true
[features]
dart = ["flowy-codegen/dart", "flowy-notification/dart"]
ts = ["flowy-codegen/ts", "flowy-notification/ts"]
ts = ["flowy-codegen/ts", "flowy-notification/tauri_ts"]
test_helper = []
wasm_build = ["collab-plugins/wasm_build"]
enable_wasm = ["collab-plugins/wasm_build"]

View File

@ -1,10 +1,13 @@
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);
{
flowy_codegen::protobuf_file::dart_gen(env!("CARGO_PKG_NAME"));
flowy_codegen::dart_event::gen(env!("CARGO_PKG_NAME"));
}
#[cfg(feature = "ts")]
flowy_codegen::ts_event::gen(crate_name);
{
flowy_codegen::ts_event::gen(env!("CARGO_PKG_NAME"), flowy_codegen::Project::Tauri);
flowy_codegen::protobuf_file::ts_gen(env!("CARGO_PKG_NAME"), flowy_codegen::Project::Tauri);
}
}

Some files were not shown because too many files have changed in this diff Show More