From 28e3cb35b8d246fab6cc5e5f3ea0bb1963888106 Mon Sep 17 00:00:00 2001 From: MikeWallaceDev Date: Sat, 5 Feb 2022 12:32:12 -0500 Subject: [PATCH 1/9] fix: makefile for commitlint Fixed the Linux part. --- Makefile.toml | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/Makefile.toml b/Makefile.toml index 72e65cad91..868694219a 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -1,19 +1,19 @@ -[tasks.install-commitlint] -mac_alias = "install-commitlint-macos" -windows_alias = "install-commitlint-windows" -linux_alias = "install-commitlint-linux" +# [tasks.install-commitlint] +# mac_alias = "install-commitlint-macos" +# windows_alias = "install-commitlint-windows" +# linux_alias = "install-commitlint-linux" -[tasks.install-commitlint-macos] +[tasks.install-commitlint.mac] script = [ """ brew install npm - yarn install - yarn husky install + yarn install + yarn husky install """, ] script_runner = "@shell" -[tasks.install-commitlint-windows] +[tasks.install-commitlint.windows] script = [ """ echo "WIP" @@ -21,12 +21,20 @@ script = [ ] script_runner = "@duckscript" -[tasks.install-commitlint-linux] +[tasks.install-commitlint.linux] script = [ """ - sudo apt install nodejs - yarn install - yarn husky install + if command -v apt &> /dev/null + then + echo "Installing node.js and yarn (sudo apt install nodejs yarn)" + sudo apt install nodejs yarn + else + echo "Installing node.js and yarn (sudo pacman -S nodejs yarn)" + sudo pacman -S nodejs yarn + fi + + yarn install + yarn husky install """, ] -script_runner = "@duckscript" \ No newline at end of file +script_runner = "@shell" From f1a8919772d60463b0cfcae10d1f97570ebe1c32 Mon Sep 17 00:00:00 2001 From: MikeWallaceDev Date: Sat, 5 Feb 2022 12:39:35 -0500 Subject: [PATCH 2/9] fix: removed comments --- Makefile.toml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Makefile.toml b/Makefile.toml index 868694219a..2e75c10277 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -1,8 +1,3 @@ -# [tasks.install-commitlint] -# mac_alias = "install-commitlint-macos" -# windows_alias = "install-commitlint-windows" -# linux_alias = "install-commitlint-linux" - [tasks.install-commitlint.mac] script = [ """ From 1796d0a448de3c8f07ca97163ed457d6900f9076 Mon Sep 17 00:00:00 2001 From: MikeWallaceDev Date: Sat, 5 Feb 2022 13:13:48 -0500 Subject: [PATCH 3/9] fix: put back Russian language set EasyLocalization to not save language locally. --- .../app_flowy/lib/startup/tasks/application_widget.dart | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/frontend/app_flowy/lib/startup/tasks/application_widget.dart b/frontend/app_flowy/lib/startup/tasks/application_widget.dart index d9523fc49e..1b929e2a42 100644 --- a/frontend/app_flowy/lib/startup/tasks/application_widget.dart +++ b/frontend/app_flowy/lib/startup/tasks/application_widget.dart @@ -29,13 +29,16 @@ class AppWidgetTask extends LaunchTask { runApp( EasyLocalization( supportedLocales: const [ + // In alphabetical order Locale('en'), - Locale('zh', 'CN'), - Locale('it', 'IT'), Locale('fr', 'CA'), + Locale('it', 'IT'), + Locale('ru', 'RU'), + Locale('zh', 'CN'), ], path: 'assets/translations', fallbackLocale: const Locale('en'), + saveLocale: false, child: app, ), ); From 67dff1720bbb868f6307920f55fa0e2576809cb2 Mon Sep 17 00:00:00 2001 From: appflowy Date: Sun, 6 Feb 2022 11:48:43 +0800 Subject: [PATCH 4/9] feat: remove backend --- .dockerignore | 1 - .github/workflows/backend_general.yml | 156 - .gitignore | 3 - backend/.dockerignore | 9 - backend/.env | 1 - backend/Cargo.lock | 4131 ----------------- backend/Cargo.toml | 109 - backend/Dockerfile | 23 - backend/Makefile | 23 - backend/configuration/base.yaml | 9 - backend/configuration/local.yaml | 5 - backend/configuration/production.yaml | 6 - backend/doc/database_setup.md | 70 - backend/doc/database_struct.md | 200 - backend/doc/img_1.png | Bin 218966 -> 0 bytes backend/doc/img_2.png | Bin 49053 -> 0 bytes backend/docker-compose.yml | 23 - backend/migrations/20210819065837_user.sql | 9 - .../migrations/20210824033032_workspace.sql | 10 - backend/migrations/20210824033742_app.sql | 14 - backend/migrations/20210824033748_view.sql | 12 - backend/migrations/20210909115140_doc.sql | 6 - backend/migrations/20211015065001_trash.sql | 7 - backend/migrations/20211221061753_kv.sql | 6 - backend/rust-toolchain.toml | 2 - backend/rustfmt.toml | 18 - backend/scripts/docker_env.sh | 10 - backend/scripts/docker_test.sh | 2 - backend/scripts/init_database.sh | 61 - backend/sqlx-data.json | 19 - backend/src/application.rs | 166 - backend/src/config/configuration.rs | 106 - backend/src/config/const_define.rs | 7 - backend/src/config/env.rs | 17 - backend/src/config/mod.rs | 6 - backend/src/context.rs | 92 - backend/src/entities/logged_user.rs | 118 - backend/src/entities/mod.rs | 3 - backend/src/entities/token.rs | 94 - backend/src/entities/user.rs | 13 - backend/src/lib.rs | 7 - backend/src/main.rs | 14 - backend/src/middleware/auth_middleware.rs | 105 - backend/src/middleware/cors_middleware.rs | 16 - backend/src/middleware/mod.rs | 5 - backend/src/services/document/mod.rs | 6 - backend/src/services/document/persistence.rs | 63 - backend/src/services/document/router.rs | 48 - backend/src/services/document/ws_actor.rs | 159 - backend/src/services/document/ws_receiver.rs | 176 - backend/src/services/folder/app/controller.rs | 113 - backend/src/services/folder/app/mod.rs | 5 - .../src/services/folder/app/persistence.rs | 140 - backend/src/services/folder/app/router.rs | 114 - backend/src/services/folder/mod.rs | 6 - backend/src/services/folder/trash/mod.rs | 6 - .../src/services/folder/trash/persistence.rs | 40 - backend/src/services/folder/trash/router.rs | 106 - backend/src/services/folder/trash/trash.rs | 180 - .../src/services/folder/view/controller.rs | 170 - backend/src/services/folder/view/mod.rs | 6 - .../src/services/folder/view/persistence.rs | 134 - backend/src/services/folder/view/router.rs | 128 - .../services/folder/workspace/controller.rs | 144 - backend/src/services/folder/workspace/mod.rs | 6 - .../services/folder/workspace/persistence.rs | 98 - .../src/services/folder/workspace/router.rs | 149 - backend/src/services/folder/ws_actor.rs | 148 - backend/src/services/folder/ws_receiver.rs | 169 - backend/src/services/kv/kv.rs | 210 - backend/src/services/kv/mod.rs | 41 - backend/src/services/kv/revision_kv.rs | 128 - backend/src/services/log.rs | 50 - backend/src/services/mod.rs | 6 - backend/src/services/user/controller.rs | 236 - backend/src/services/user/mod.rs | 4 - backend/src/services/user/router.rs | 64 - .../services/web_socket/entities/connect.rs | 50 - .../services/web_socket/entities/message.rs | 27 - .../src/services/web_socket/entities/mod.rs | 5 - backend/src/services/web_socket/mod.rs | 8 - backend/src/services/web_socket/router.rs | 61 - backend/src/services/web_socket/ws_client.rs | 181 - backend/src/services/web_socket/ws_server.rs | 65 - backend/src/util/mod.rs | 3 - backend/src/util/serde_ext.rs | 46 - backend/src/util/sqlx_ext/mod.rs | 6 - backend/src/util/sqlx_ext/query.rs | 159 - backend/src/util/sqlx_ext/utils.rs | 11 - backend/src/util/user_ext.rs | 31 - backend/tests/api_test/auth_test.rs | 126 - backend/tests/api_test/kv_test.rs | 79 - backend/tests/api_test/mod.rs | 3 - backend/tests/api_test/workspace_test.rs | 282 -- backend/tests/document_test/edit_script.rs | 179 - backend/tests/document_test/edit_test.rs | 207 - backend/tests/document_test/mod.rs | 2 - backend/tests/main.rs | 3 - backend/tests/util/helper.rs | 388 -- backend/tests/util/mod.rs | 1 - 100 files changed, 10690 deletions(-) delete mode 100644 .github/workflows/backend_general.yml delete mode 100644 backend/.dockerignore delete mode 100644 backend/.env delete mode 100644 backend/Cargo.lock delete mode 100644 backend/Cargo.toml delete mode 100644 backend/Dockerfile delete mode 100644 backend/Makefile delete mode 100644 backend/configuration/base.yaml delete mode 100644 backend/configuration/local.yaml delete mode 100644 backend/configuration/production.yaml delete mode 100644 backend/doc/database_setup.md delete mode 100644 backend/doc/database_struct.md delete mode 100644 backend/doc/img_1.png delete mode 100644 backend/doc/img_2.png delete mode 100644 backend/docker-compose.yml delete mode 100644 backend/migrations/20210819065837_user.sql delete mode 100644 backend/migrations/20210824033032_workspace.sql delete mode 100644 backend/migrations/20210824033742_app.sql delete mode 100644 backend/migrations/20210824033748_view.sql delete mode 100644 backend/migrations/20210909115140_doc.sql delete mode 100644 backend/migrations/20211015065001_trash.sql delete mode 100644 backend/migrations/20211221061753_kv.sql delete mode 100644 backend/rust-toolchain.toml delete mode 100644 backend/rustfmt.toml delete mode 100644 backend/scripts/docker_env.sh delete mode 100644 backend/scripts/docker_test.sh delete mode 100755 backend/scripts/init_database.sh delete mode 100644 backend/sqlx-data.json delete mode 100644 backend/src/application.rs delete mode 100644 backend/src/config/configuration.rs delete mode 100644 backend/src/config/const_define.rs delete mode 100644 backend/src/config/env.rs delete mode 100644 backend/src/config/mod.rs delete mode 100644 backend/src/context.rs delete mode 100644 backend/src/entities/logged_user.rs delete mode 100644 backend/src/entities/mod.rs delete mode 100644 backend/src/entities/token.rs delete mode 100644 backend/src/entities/user.rs delete mode 100644 backend/src/lib.rs delete mode 100644 backend/src/main.rs delete mode 100644 backend/src/middleware/auth_middleware.rs delete mode 100644 backend/src/middleware/cors_middleware.rs delete mode 100644 backend/src/middleware/mod.rs delete mode 100644 backend/src/services/document/mod.rs delete mode 100644 backend/src/services/document/persistence.rs delete mode 100644 backend/src/services/document/router.rs delete mode 100644 backend/src/services/document/ws_actor.rs delete mode 100644 backend/src/services/document/ws_receiver.rs delete mode 100644 backend/src/services/folder/app/controller.rs delete mode 100644 backend/src/services/folder/app/mod.rs delete mode 100644 backend/src/services/folder/app/persistence.rs delete mode 100644 backend/src/services/folder/app/router.rs delete mode 100644 backend/src/services/folder/mod.rs delete mode 100644 backend/src/services/folder/trash/mod.rs delete mode 100644 backend/src/services/folder/trash/persistence.rs delete mode 100644 backend/src/services/folder/trash/router.rs delete mode 100644 backend/src/services/folder/trash/trash.rs delete mode 100644 backend/src/services/folder/view/controller.rs delete mode 100644 backend/src/services/folder/view/mod.rs delete mode 100644 backend/src/services/folder/view/persistence.rs delete mode 100644 backend/src/services/folder/view/router.rs delete mode 100644 backend/src/services/folder/workspace/controller.rs delete mode 100644 backend/src/services/folder/workspace/mod.rs delete mode 100644 backend/src/services/folder/workspace/persistence.rs delete mode 100644 backend/src/services/folder/workspace/router.rs delete mode 100644 backend/src/services/folder/ws_actor.rs delete mode 100644 backend/src/services/folder/ws_receiver.rs delete mode 100644 backend/src/services/kv/kv.rs delete mode 100644 backend/src/services/kv/mod.rs delete mode 100644 backend/src/services/kv/revision_kv.rs delete mode 100644 backend/src/services/log.rs delete mode 100644 backend/src/services/mod.rs delete mode 100644 backend/src/services/user/controller.rs delete mode 100644 backend/src/services/user/mod.rs delete mode 100644 backend/src/services/user/router.rs delete mode 100644 backend/src/services/web_socket/entities/connect.rs delete mode 100644 backend/src/services/web_socket/entities/message.rs delete mode 100644 backend/src/services/web_socket/entities/mod.rs delete mode 100644 backend/src/services/web_socket/mod.rs delete mode 100644 backend/src/services/web_socket/router.rs delete mode 100644 backend/src/services/web_socket/ws_client.rs delete mode 100644 backend/src/services/web_socket/ws_server.rs delete mode 100644 backend/src/util/mod.rs delete mode 100644 backend/src/util/serde_ext.rs delete mode 100644 backend/src/util/sqlx_ext/mod.rs delete mode 100644 backend/src/util/sqlx_ext/query.rs delete mode 100644 backend/src/util/sqlx_ext/utils.rs delete mode 100644 backend/src/util/user_ext.rs delete mode 100644 backend/tests/api_test/auth_test.rs delete mode 100644 backend/tests/api_test/kv_test.rs delete mode 100644 backend/tests/api_test/mod.rs delete mode 100644 backend/tests/api_test/workspace_test.rs delete mode 100644 backend/tests/document_test/edit_script.rs delete mode 100644 backend/tests/document_test/edit_test.rs delete mode 100644 backend/tests/document_test/mod.rs delete mode 100644 backend/tests/main.rs delete mode 100644 backend/tests/util/helper.rs delete mode 100644 backend/tests/util/mod.rs diff --git a/.dockerignore b/.dockerignore index cfeb9009a9..18f578ca48 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,4 @@ frontend/app_flowy/ frontend/scripts/ frontend/rust-lib/target -backend/target/ shared-lib/target/ \ No newline at end of file diff --git a/.github/workflows/backend_general.yml b/.github/workflows/backend_general.yml deleted file mode 100644 index 0532e9a724..0000000000 --- a/.github/workflows/backend_general.yml +++ /dev/null @@ -1,156 +0,0 @@ -name: Backend - -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - -jobs: - test: - name: Test - runs-on: ubuntu-latest - services: - postgres: - image: postgres:12 - env: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: password - POSTGRES_DB: postgres - ports: - - 5433:5432 - env: - SQLX_VERSION: 0.5.7 - SQLX_FEATURES: postgres - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Cache dependencies - id: cache-dependencies - uses: actions/cache@v2 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - - name: Install stable toolchain - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - - - name: Cache sqlx-cli - uses: actions/cache@v2 - id: cache-sqlx - with: - path: | - ~/.cargo/bin/sqlx - ~/.cargo/bin/cargo-sqlx - key: ${{ runner.os }}-sqlx-${{ env.SQLX_VERSION }}-${{ env.SQLX_FEATURES }} - - - name: Install sqlx-cli - uses: actions-rs/cargo@v1 - if: steps.cache-sqlx.outputs.cache-hit == false - with: - command: install - args: > - sqlx-cli - --force - --version=${{ env.SQLX_VERSION }} - --features=${{ env.SQLX_FEATURES }} - --no-default-features - --locked - - - name: Migrate database - working-directory: backend/ - run: | - sudo apt-get install libpq-dev -y - SKIP_DOCKER=true POSTGRES_PORT=5433 ./scripts/init_database.sh - - - name: Check sqlx-data.json is up-to-date - working-directory: backend/ - run: | - cargo sqlx prepare --check -- --bin backend - - - name: Run cargo test - working-directory: backend/ - run: cargo test - - fmt: - name: Rustfmt - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - override: true - - run: rustup component add rustfmt - working-directory: backend/ - - run: cargo fmt --all -- --check - working-directory: backend/ - - - clippy: - name: Clippy - runs-on: ubuntu-latest - services: - postgres: - image: postgres:12 - env: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: password - POSTGRES_DB: postgres - ports: - - 5433:5432 - env: - SQLX_VERSION: 0.5.7 - SQLX_FEATURES: postgres - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Install stable toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - components: clippy - override: true - - - name: Cache sqlx-cli - uses: actions/cache@v2 - id: cache-sqlx - with: - path: | - ~/.cargo/bin/sqlx - key: ${{ runner.os }}-sqlx-${{ env.SQLX_VERSION }}-${{ env.SQLX_FEATURES }} - - - name: Install sqlx-cli - uses: actions-rs/cargo@v1 - if: steps.cache-sqlx.outputs.cache-hit == false - with: - command: install - args: > - sqlx-cli - --force - --version=${{ env.SQLX_VERSION }} - --features=${{ env.SQLX_FEATURES }} - --no-default-features - --locked - - - name: Migrate database - working-directory: backend/ - run: | - sudo apt-get install libpq-dev -y - SKIP_DOCKER=true POSTGRES_PORT=5433 ./scripts/init_database.sh - - - run: rustup component add clippy - working-directory: backend/ - - run: cargo clippy -- -D warnings - working-directory: backend/ - - diff --git a/.gitignore b/.gitignore index 8f201fa699..517b5e2f75 100644 --- a/.gitignore +++ b/.gitignore @@ -4,9 +4,6 @@ # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html # backend /target/ -./backend/.env -./backend/configuration/base.yaml -Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk diff --git a/backend/.dockerignore b/backend/.dockerignore deleted file mode 100644 index 6c23612e00..0000000000 --- a/backend/.dockerignore +++ /dev/null @@ -1,9 +0,0 @@ -.env -.dockerignore -spec.yaml -target/ -deploy/ -tests/ -Dockerfile -scripts/ -migrations/ \ No newline at end of file diff --git a/backend/.env b/backend/.env deleted file mode 100644 index 8ee6c6b1d5..0000000000 --- a/backend/.env +++ /dev/null @@ -1 +0,0 @@ -DATABASE_URL="postgres://postgres:password@localhost:5433/flowy" \ No newline at end of file diff --git a/backend/Cargo.lock b/backend/Cargo.lock deleted file mode 100644 index a5049a77ff..0000000000 --- a/backend/Cargo.lock +++ /dev/null @@ -1,4131 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "actix" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3720d0064a0ce5c0de7bd93bdb0a6caebab2a9b5668746145d7b3b0c5da02914" -dependencies = [ - "actix-rt", - "actix_derive", - "bitflags", - "bytes", - "crossbeam-channel", - "futures-core", - "futures-sink", - "futures-task", - "futures-util", - "log", - "once_cell", - "parking_lot", - "pin-project-lite", - "smallvec", - "tokio", - "tokio-util", -] - -[[package]] -name = "actix-codec" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d5dbeb2d9e51344cb83ca7cc170f1217f9fe25bfc50160e6e200b5c31c1019a" -dependencies = [ - "bitflags", - "bytes", - "futures-core", - "futures-sink", - "log", - "pin-project-lite", - "tokio", - "tokio-util", -] - -[[package]] -name = "actix-cors" -version = "0.6.0-beta.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa59d36d7ad063401d94ada05264a303791796ad5222d0954cc21f2e1052e99b" -dependencies = [ - "actix-service", - "actix-web", - "derive_more", - "futures-util", - "log", - "once_cell", - "smallvec", -] - -[[package]] -name = "actix-http" -version = "3.0.0-beta.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afaeb3d3fcb06b775ac62f05d580aae4afe5a149513333a73f688fdf26c06639" -dependencies = [ - "actix-codec", - "actix-rt", - "actix-service", - "actix-utils", - "ahash", - "base64 0.13.0", - "bitflags", - "brotli2", - "bytes", - "bytestring", - "derive_more", - "encoding_rs", - "flate2", - "futures-core", - "futures-util", - "h2", - "http", - "httparse", - "httpdate", - "itoa", - "language-tags", - "local-channel", - "log", - "mime", - "percent-encoding", - "pin-project 1.0.8", - "pin-project-lite", - "rand", - "sha-1", - "smallvec", - "zstd", -] - -[[package]] -name = "actix-identity" -version = "0.4.0-beta.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bfff0a087e890d3b9bed74951a554dc709c3ac92f855b07a6767d42bca9c7c2" -dependencies = [ - "actix-service", - "actix-web", - "futures-util", - "serde", - "serde_json", - "time 0.2.27", -] - -[[package]] -name = "actix-macros" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" -dependencies = [ - "quote", - "syn", -] - -[[package]] -name = "actix-router" -version = "0.5.0-beta.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36b95ce0d76d1aa2f98b681702807475ade0f99bd4552546a6843a966d42ea3d" -dependencies = [ - "bytestring", - "firestorm", - "http", - "log", - "regex", - "serde", -] - -[[package]] -name = "actix-rt" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a0c218d0a17c120f10ee0c69c9f0c45d87319e8f66b1f065e8412b612fc3e24" -dependencies = [ - "actix-macros", - "futures-core", - "tokio", -] - -[[package]] -name = "actix-server" -version = "2.0.0-beta.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "411dd3296dd317ff5eff50baa13f31923ea40ec855dd7f2d3ed8639948f0195f" -dependencies = [ - "actix-rt", - "actix-service", - "actix-utils", - "futures-core", - "futures-util", - "log", - "mio", - "num_cpus", - "socket2", - "tokio", -] - -[[package]] -name = "actix-service" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d3dc6a618b082974a08d7a4781d24d4691cba51500059bfebe6656a61ebfe1e" -dependencies = [ - "futures-core", - "paste", - "pin-project-lite", -] - -[[package]] -name = "actix-utils" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e491cbaac2e7fc788dfff99ff48ef317e23b3cf63dbaf7aaab6418f40f92aa94" -dependencies = [ - "local-waker", - "pin-project-lite", -] - -[[package]] -name = "actix-web" -version = "4.0.0-beta.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e85aa9bb018d83a0db70f557ba0cde9c6170a5d1de4fede02e377f68c1ac5aa9" -dependencies = [ - "actix-codec", - "actix-http", - "actix-macros", - "actix-router", - "actix-rt", - "actix-server", - "actix-service", - "actix-utils", - "actix-web-codegen", - "ahash", - "bytes", - "cfg-if", - "cookie", - "derive_more", - "either", - "encoding_rs", - "futures-core", - "futures-util", - "itoa", - "language-tags", - "log", - "mime", - "once_cell", - "paste", - "pin-project 1.0.8", - "regex", - "serde", - "serde_json", - "serde_urlencoded", - "smallvec", - "socket2", - "time 0.3.5", - "url", -] - -[[package]] -name = "actix-web-actors" -version = "4.0.0-beta.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b43c8e03f1877bc010316de455ee479aa0de0ae0717d49041db5f38fa192de" -dependencies = [ - "actix", - "actix-codec", - "actix-http", - "actix-web", - "bytes", - "bytestring", - "futures-core", - "pin-project 1.0.8", - "tokio", -] - -[[package]] -name = "actix-web-codegen" -version = "0.5.0-beta.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfe80a8828fa88a0420dc8fdd4c16b8207326c917f17701881b063eadc2a8d3b" -dependencies = [ - "actix-router", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "actix_derive" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d44b8fee1ced9671ba043476deddef739dd0959bf77030b26b738cc591737a7" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "addr2line" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61f2b7f93d2c7d2b08263acaa4a363b3e276806c68af6134c44f523bf1aacd" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "aead" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331" -dependencies = [ - "generic-array", -] - -[[package]] -name = "aes" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" -dependencies = [ - "aes-soft", - "aesni", - "cipher 0.2.5", -] - -[[package]] -name = "aes-gcm" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5278b5fabbb9bd46e24aa69b2fdea62c99088e0a950a9be40e3e0101298f88da" -dependencies = [ - "aead", - "aes", - "cipher 0.2.5", - "ctr", - "ghash", - "subtle", -] - -[[package]] -name = "aes-soft" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" -dependencies = [ - "cipher 0.2.5", - "opaque-debug", -] - -[[package]] -name = "aesni" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" -dependencies = [ - "cipher 0.2.5", - "opaque-debug", -] - -[[package]] -name = "ahash" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43bb833f0bf979d8475d38fbf09ed3b8a55e1885fe93ad3f93239fc6a4f17b98" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - -[[package]] -name = "aho-corasick" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" -dependencies = [ - "memchr", -] - -[[package]] -name = "allo-isolate" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff09da612ca86c794c3c8f70613a3e6652b5f1313b938937b32cae80df28fdb1" -dependencies = [ - "pin-project 0.4.28", -] - -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - -[[package]] -name = "anyhow" -version = "1.0.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28ae2b3dec75a406790005a200b1bd89785afc02517a00ca99ecfe093ee9e6cf" - -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - -[[package]] -name = "async-stream" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "171374e7e3b2504e0e5236e3b59260560f9fe94bfe9ac39ba5e4e929c5590625" -dependencies = [ - "async-stream-impl", - "futures-core", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "648ed8c8d2ce5409ccd57453d9d1b214b342a0d69376a6feda1fd6cae3299308" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "async-trait" -version = "0.1.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "atoi" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616896e05fc0e2649463a93a15183c6a16bf03413a7af88ef1285ddedfa9cda5" -dependencies = [ - "num-traits", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" - -[[package]] -name = "backend" -version = "0.1.0" -dependencies = [ - "actix", - "actix-codec", - "actix-cors", - "actix-http", - "actix-identity", - "actix-rt", - "actix-service", - "actix-web", - "actix-web-actors", - "anyhow", - "async-stream", - "async-trait", - "backend", - "backend-service", - "bcrypt", - "byteorder", - "bytes", - "chrono", - "config", - "dashmap", - "derive_more", - "flowy-collaboration", - "flowy-document", - "flowy-folder-data-model", - "flowy-net", - "flowy-sdk", - "flowy-test", - "flowy-user", - "flowy-user-data-model", - "futures", - "futures-core", - "futures-util", - "jsonwebtoken", - "lazy_static", - "lib-infra", - "lib-ot", - "lib-ws", - "linkify", - "log", - "md5", - "once_cell", - "ormx", - "parking_lot", - "pin-project 1.0.8", - "protobuf", - "serde", - "serde-aux", - "serde_json", - "serde_repr", - "sql-builder", - "sqlx", - "thiserror", - "tokio", - "toml", - "tracing", - "tracing-appender", - "tracing-bunyan-formatter", - "tracing-core", - "tracing-futures", - "tracing-log", - "tracing-subscriber", - "uuid", -] - -[[package]] -name = "backend-service" -version = "0.1.0" -dependencies = [ - "actix-web", - "anyhow", - "bytes", - "config", - "derive_more", - "flowy-collaboration", - "flowy-folder-data-model", - "flowy-user-data-model", - "hyper", - "lazy_static", - "log", - "protobuf", - "reqwest", - "serde", - "serde-aux", - "serde_json", - "serde_repr", - "thiserror", - "tokio", - "tracing", - "uuid", -] - -[[package]] -name = "backtrace" -version = "0.3.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7a905d892734eea339e896738c14b9afce22b5318f64b951e70bf3844419b01" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base-x" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" - -[[package]] -name = "base64" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" - -[[package]] -name = "base64" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" - -[[package]] -name = "bcrypt" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f691e63585950d8c1c43644d11bab9073e40f5060dd2822734ae7c3dc69a3a80" -dependencies = [ - "base64 0.13.0", - "blowfish", - "getrandom", -] - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bit-set" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitvec" -version = "0.19.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8942c8d352ae1838c9dda0b0ca2ab657696ef2232a20147cf1b30ae1a9cb4321" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - -[[package]] -name = "blowfish" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe3ff3fc1de48c1ac2e3341c4df38b0d1bfb8fdf04632a187c8b75aaa319a7ab" -dependencies = [ - "byteorder", - "cipher 0.3.0", - "opaque-debug", -] - -[[package]] -name = "brotli-sys" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4445dea95f4c2b41cde57cc9fee236ae4dbae88d8fcbdb4750fc1bb5d86aaecd" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "brotli2" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cb036c3eade309815c15ddbacec5b22c4d1f3983a774ab2eac2e3e9ea85568e" -dependencies = [ - "brotli-sys", - "libc", -] - -[[package]] -name = "bumpalo" -version = "3.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" - -[[package]] -name = "bytecount" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" -dependencies = [ - "serde", -] - -[[package]] -name = "bytestring" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90706ba19e97b90786e19dc0d5e2abd80008d99d4c0c5d1ad0b5e72cec7c494d" -dependencies = [ - "bytes", -] - -[[package]] -name = "cc" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2" -dependencies = [ - "jobserver", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" -dependencies = [ - "libc", - "num-integer", - "num-traits", - "serde", - "time 0.1.44", - "winapi", -] - -[[package]] -name = "cipher" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" -dependencies = [ - "generic-array", -] - -[[package]] -name = "cipher" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" -dependencies = [ - "generic-array", -] - -[[package]] -name = "claim" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81099d6bb72e1df6d50bb2347224b666a670912bb7f06dbe867a4a070ab3ce8" -dependencies = [ - "autocfg", -] - -[[package]] -name = "color-eyre" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f1885697ee8a177096d42f158922251a41973117f6d8a234cee94b9509157b7" -dependencies = [ - "backtrace", - "eyre", - "indenter", - "once_cell", - "owo-colors", -] - -[[package]] -name = "config" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b076e143e1d9538dde65da30f8481c2a6c44040edb8e02b9bf1351edb92ce3" -dependencies = [ - "lazy_static", - "nom 5.1.2", - "serde", - "yaml-rust", -] - -[[package]] -name = "const_fn" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f92cfa0fd5690b3cf8c1ef2cabbd9b7ef22fa53cf5e1f92b05103f6d5d1cf6e7" - -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - -[[package]] -name = "cookie" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f1c7727e460397e56abc4bddc1d49e07a1ad78fc98eb2e1c8f032a58a2f80d" -dependencies = [ - "aes-gcm", - "base64 0.13.0", - "hkdf", - "hmac 0.10.1", - "percent-encoding", - "rand", - "sha2", - "subtle", - "time 0.2.27", - "version_check", -] - -[[package]] -name = "core-foundation" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" - -[[package]] -name = "cpufeatures" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66c99696f6c9dd7f35d486b9d04d7e6e202aa3e8c40d553f2fdf5e7e0c6a71ef" -dependencies = [ - "libc", -] - -[[package]] -name = "cpuid-bool" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" - -[[package]] -name = "crc" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10c2722795460108a7872e1cd933a85d6ec38abc4baecad51028f702da28889f" -dependencies = [ - "crc-catalog", -] - -[[package]] -name = "crc-catalog" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccaeedb56da03b09f598226e25e80088cb4cd25f316e6e4df7d695f0feeb1403" - -[[package]] -name = "crc32fast" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae5588f6b3c3cb05239e90bd110f257254aecd01e4635400391aeae07497845" -dependencies = [ - "cfg-if", - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-epoch", - "crossbeam-queue", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" -dependencies = [ - "cfg-if", - "crossbeam-utils", - "lazy_static", - "memoffset", - "scopeguard", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b10ddc024425c88c2ad148c1b0fd53f4c6d38db9697c9f1588381212fa657c9" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" -dependencies = [ - "cfg-if", - "lazy_static", -] - -[[package]] -name = "crypto-mac" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "crypto-mac" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "ctr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f" -dependencies = [ - "cipher 0.2.5", -] - -[[package]] -name = "darling" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "757c0ded2af11d8e739c4daea1ac623dd1624b06c844cf3f5a39f1bdbd99bb12" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c34d8efb62d0c2d7f60ece80f75e5c63c1588ba68032740494b0b9a996466e3" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn", -] - -[[package]] -name = "darling_macro" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade7bff147130fe5e6d39f089c6bd49ec0250f35d70b2eebf72afdfc919f15cc" -dependencies = [ - "darling_core", - "quote", - "syn", -] - -[[package]] -name = "dart-notify" -version = "0.1.0" -dependencies = [ - "allo-isolate", - "bytes", - "flowy-derive", - "lazy_static", - "lib-dispatch", - "log", - "protobuf", -] - -[[package]] -name = "dashmap" -version = "4.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" -dependencies = [ - "cfg-if", - "num_cpus", -] - -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "derive_more" -version = "0.99.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40eebddd2156ce1bb37b20bbe5151340a31828b1f2d22ba4141f3531710e38df" -dependencies = [ - "convert_case", - "proc-macro2", - "quote", - "rustc_version 0.3.3", - "syn", -] - -[[package]] -name = "diesel" -version = "1.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b28135ecf6b7d446b43e27e225622a038cc4e2930a1022f51cdb97ada19b8e4d" -dependencies = [ - "byteorder", - "diesel_derives", - "libsqlite3-sys", -] - -[[package]] -name = "diesel_derives" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "diesel_migrations" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf3cde8413353dc7f5d72fa8ce0b99a560a359d2c5ef1e5817ca731cd9008f4c" -dependencies = [ - "migrations_internals", - "migrations_macros", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - -[[package]] -name = "dirs" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "discard" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" - -[[package]] -name = "dissimilar" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31ad93652f40969dead8d4bf897a41e9462095152eb21c56e5830537e41179dd" - -[[package]] -name = "dotenv" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" - -[[package]] -name = "dyn-clone" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee2626afccd7561a06cf1367e2950c4718ea04565e20fb5029b6c7d8ad09abcf" - -[[package]] -name = "either" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" -dependencies = [ - "serde", -] - -[[package]] -name = "encoding_rs" -version = "0.8.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80df024fbc5ac80f87dfef0d9f5209a252f2a497f7f42944cff24d8253cac065" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "env_logger" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "error-chain" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e791d3be96241c77c43846b665ef1384606da2cd2a48730abe606a12906e02" -dependencies = [ - "backtrace", -] - -[[package]] -name = "error-code" -version = "0.1.0" -dependencies = [ - "derive_more", - "flowy-derive", - "protobuf", -] - -[[package]] -name = "eyre" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "221239d1d5ea86bf5d6f91c9d6bc3646ffe471b08ff9b0f91c44f115ac969d2b" -dependencies = [ - "indenter", - "once_cell", -] - -[[package]] -name = "fancy-regex" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe09872bd11351a75f22b24c3769fc863e8212d926d6db46b94ad710d14cc5cc" -dependencies = [ - "bit-set", - "regex", -] - -[[package]] -name = "firestorm" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31586bda1b136406162e381a3185a506cdfc1631708dd40cba2f6628d8634499" - -[[package]] -name = "flate2" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0" -dependencies = [ - "cfg-if", - "crc32fast", - "libc", - "miniz_oxide", -] - -[[package]] -name = "flowy-ast" -version = "0.1.0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "flowy-collaboration" -version = "0.1.0" -dependencies = [ - "async-stream", - "bytes", - "chrono", - "dashmap", - "dissimilar", - "flowy-derive", - "flowy-folder-data-model", - "futures", - "lib-infra", - "lib-ot", - "log", - "md5", - "parking_lot", - "protobuf", - "serde", - "serde_json", - "strum", - "strum_macros", - "tokio", - "tracing", - "url", -] - -[[package]] -name = "flowy-database" -version = "0.1.0" -dependencies = [ - "diesel", - "diesel_derives", - "diesel_migrations", - "lazy_static", - "lib-sqlite", - "log", -] - -[[package]] -name = "flowy-derive" -version = "0.1.0" -dependencies = [ - "flowy-ast", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "flowy-document" -version = "0.1.0" -dependencies = [ - "async-stream", - "async-trait", - "bytecount", - "byteorder", - "bytes", - "chrono", - "dart-notify", - "dashmap", - "derive_more", - "diesel", - "diesel_derives", - "flowy-collaboration", - "flowy-database", - "flowy-derive", - "flowy-error", - "flowy-sync", - "futures", - "futures-util", - "lib-dispatch", - "lib-infra", - "lib-ot", - "lib-ws", - "log", - "parking_lot", - "pin-project 1.0.8", - "protobuf", - "serde", - "serde_json", - "strum", - "strum_macros", - "tokio", - "tracing", - "unicode-segmentation", - "url", -] - -[[package]] -name = "flowy-error" -version = "0.1.0" -dependencies = [ - "backend-service", - "bytes", - "error-code", - "flowy-collaboration", - "flowy-database", - "flowy-derive", - "lib-dispatch", - "lib-ot", - "lib-sqlite", - "protobuf", - "r2d2", - "serde_json", -] - -[[package]] -name = "flowy-folder" -version = "0.1.0" -dependencies = [ - "bincode", - "bytes", - "chrono", - "crossbeam", - "crossbeam-utils", - "dart-notify", - "derive_more", - "diesel", - "diesel_derives", - "flowy-collaboration", - "flowy-database", - "flowy-derive", - "flowy-document", - "flowy-error", - "flowy-folder-data-model", - "flowy-sync", - "futures", - "futures-core", - "lazy_static", - "lib-dispatch", - "lib-infra", - "lib-ot", - "lib-sqlite", - "log", - "parking_lot", - "pin-project 1.0.8", - "protobuf", - "serde", - "strum", - "strum_macros", - "tokio", - "tracing", -] - -[[package]] -name = "flowy-folder-data-model" -version = "0.1.0" -dependencies = [ - "bytes", - "chrono", - "derive_more", - "error-code", - "flowy-derive", - "log", - "protobuf", - "serde", - "serde_json", - "strum", - "strum_macros", - "unicode-segmentation", - "uuid", -] - -[[package]] -name = "flowy-net" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-stream", - "backend-service", - "bytes", - "dashmap", - "flowy-collaboration", - "flowy-derive", - "flowy-document", - "flowy-error", - "flowy-folder", - "flowy-folder-data-model", - "flowy-user", - "flowy-user-data-model", - "futures-util", - "lazy_static", - "lib-dispatch", - "lib-infra", - "lib-ws", - "parking_lot", - "protobuf", - "strum", - "strum_macros", - "tokio", - "tracing", -] - -[[package]] -name = "flowy-sdk" -version = "0.1.0" -dependencies = [ - "backend-service", - "bytes", - "color-eyre", - "flowy-collaboration", - "flowy-database", - "flowy-document", - "flowy-folder", - "flowy-net", - "flowy-sync", - "flowy-user", - "futures-core", - "lib-dispatch", - "lib-infra", - "lib-log", - "lib-ws", - "log", - "parking_lot", - "tokio", - "tracing", -] - -[[package]] -name = "flowy-sync" -version = "0.1.0" -dependencies = [ - "async-stream", - "bytes", - "dashmap", - "diesel", - "diesel_derives", - "flowy-collaboration", - "flowy-database", - "flowy-error", - "futures-util", - "lib-infra", - "lib-ot", - "lib-ws", - "parking_lot", - "protobuf", - "serde", - "serde_json", - "strum", - "strum_macros", - "tokio", - "tracing", -] - -[[package]] -name = "flowy-test" -version = "0.1.0" -dependencies = [ - "backend-service", - "bincode", - "bytes", - "claim", - "flowy-collaboration", - "flowy-folder", - "flowy-net", - "flowy-sdk", - "flowy-user", - "futures-util", - "lib-dispatch", - "lib-infra", - "lib-ot", - "log", - "protobuf", - "serde", - "serde_json", - "thread-id", - "tokio", -] - -[[package]] -name = "flowy-user" -version = "0.1.0" -dependencies = [ - "bytes", - "dart-notify", - "dashmap", - "derive_more", - "diesel", - "diesel_derives", - "flowy-database", - "flowy-derive", - "flowy-error", - "flowy-user-data-model", - "futures-core", - "lazy_static", - "lib-dispatch", - "lib-infra", - "lib-sqlite", - "log", - "once_cell", - "parking_lot", - "pin-project 1.0.8", - "protobuf", - "r2d2", - "serde", - "serde_json", - "strum", - "strum_macros", - "thread-id", - "thread_local", - "tokio", - "tracing", -] - -[[package]] -name = "flowy-user-data-model" -version = "0.1.0" -dependencies = [ - "bytes", - "derive_more", - "error-code", - "fancy-regex", - "flowy-derive", - "lazy_static", - "log", - "protobuf", - "serde", - "unicode-segmentation", - "validator", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" -dependencies = [ - "matches", - "percent-encoding", -] - -[[package]] -name = "funty" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" - -[[package]] -name = "futures" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12aa0eb539080d55c3f2d45a67c3b58b6b0773c1a3ca2dfec66d58c97fd66ca" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d" - -[[package]] -name = "futures-executor" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45025be030969d763025784f7f355043dc6bc74093e4ecc5000ca4dc50d8745c" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-intrusive" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62007592ac46aa7c2b6416f7deb9a8a8f63a01e0f1d6e1787d5630170db2b63e" -dependencies = [ - "futures-core", - "lock_api", - "parking_lot", -] - -[[package]] -name = "futures-io" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377" - -[[package]] -name = "futures-macro" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18e4a4b95cea4b4ccbcf1c5675ca7c4ee4e9e75eb79944d07defde18068f79bb" -dependencies = [ - "autocfg", - "proc-macro-hack", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "futures-sink" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36ea153c13024fe480590b3e3d4cad89a0cfacecc24577b68f86c6ced9c2bc11" - -[[package]] -name = "futures-task" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99" - -[[package]] -name = "futures-util" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481" -dependencies = [ - "autocfg", - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "proc-macro-hack", - "proc-macro-nested", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "gethostname" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e692e296bfac1d2533ef168d0b60ff5897b8b70a4009276834014dd8924cc028" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "getrandom" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "ghash" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97304e4cd182c3846f7575ced3890c53012ce534ad9114046b0a9e00bb30a375" -dependencies = [ - "opaque-debug", - "polyval", -] - -[[package]] -name = "gimli" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0a01e0497841a3b2db4f8afa483cce65f7e96a3498bd6c541734792aeac8fe7" - -[[package]] -name = "h2" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "825343c4eef0b63f541f8903f395dc5beb362a979b5799a84062527ef1e37726" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashlink" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf" -dependencies = [ - "hashbrown", -] - -[[package]] -name = "heck" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hkdf" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f" -dependencies = [ - "digest", - "hmac 0.10.1", -] - -[[package]] -name = "hmac" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" -dependencies = [ - "crypto-mac 0.10.1", - "digest", -] - -[[package]] -name = "hmac" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" -dependencies = [ - "crypto-mac 0.11.1", - "digest", -] - -[[package]] -name = "http" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "399c583b2979440c60be0821a6199eca73bc3c8dcd9d070d75ac726e2c6186e5" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" - -[[package]] -name = "httpdate" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6456b8a6c8f33fee7d958fcd1b60d55b11940a79e63ae87013e6d22e26034440" - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "hyper" -version = "0.14.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b61cf2d1aebcf6e6352c97b81dc2244ca29194be1b276f5d8ad5c6330fffb11" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "indenter" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" - -[[package]] -name = "indexmap" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" -dependencies = [ - "autocfg", - "hashbrown", -] - -[[package]] -name = "instant" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "ipnet" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" - -[[package]] -name = "itertools" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" - -[[package]] -name = "jobserver" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" -dependencies = [ - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce791b7ca6638aae45be056e068fc756d871eb3b3b10b8efa62d1c9cec616752" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "jsonwebtoken" -version = "7.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afabcc15e437a6484fc4f12d0fd63068fe457bf93f1c148d3d9649c60b103f32" -dependencies = [ - "base64 0.12.3", - "pem", - "ring", - "serde", - "serde_json", - "simple_asn1", -] - -[[package]] -name = "language-tags" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "lexical-core" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" -dependencies = [ - "arrayvec", - "bitflags", - "cfg-if", - "ryu", - "static_assertions", -] - -[[package]] -name = "lib-dispatch" -version = "0.1.0" -dependencies = [ - "bytes", - "dashmap", - "derivative", - "dyn-clone", - "env_logger", - "futures", - "futures-channel", - "futures-core", - "futures-util", - "lazy_static", - "log", - "paste", - "pin-project 1.0.8", - "protobuf", - "serde", - "serde_json", - "serde_with", - "thread-id", - "tokio", - "tracing", - "uuid", -] - -[[package]] -name = "lib-infra" -version = "0.1.0" -dependencies = [ - "bytes", - "chrono", - "futures-core", - "log", - "pin-project 1.0.8", - "rand", - "tokio", - "uuid", -] - -[[package]] -name = "lib-log" -version = "0.1.0" -dependencies = [ - "chrono", - "lazy_static", - "log", - "serde", - "serde_json", - "tracing", - "tracing-appender", - "tracing-bunyan-formatter", - "tracing-core", - "tracing-futures", - "tracing-log", - "tracing-subscriber", -] - -[[package]] -name = "lib-ot" -version = "0.1.0" -dependencies = [ - "anyhow", - "bytecount", - "bytes", - "dashmap", - "derive_more", - "lazy_static", - "log", - "md5", - "serde", - "serde_json", - "strum", - "strum_macros", - "thiserror", - "tokio", - "tracing", -] - -[[package]] -name = "lib-sqlite" -version = "0.1.0" -dependencies = [ - "diesel", - "diesel_derives", - "diesel_migrations", - "error-chain", - "lazy_static", - "libsqlite3-sys", - "log", - "r2d2", - "scheduled-thread-pool", -] - -[[package]] -name = "lib-ws" -version = "0.1.0" -dependencies = [ - "backend-service", - "bytes", - "dashmap", - "flowy-derive", - "futures", - "futures-channel", - "futures-core", - "futures-util", - "lib-infra", - "log", - "parking_lot", - "paste", - "pin-project 1.0.8", - "protobuf", - "strum", - "strum_macros", - "tokio", - "tokio-tungstenite", - "tracing", - "url", -] - -[[package]] -name = "libc" -version = "0.2.99" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765" - -[[package]] -name = "libsqlite3-sys" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e9eb7b8e152b6a01be6a4a2917248381875758250dc3df5d46caf9250341dda" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "linked-hash-map" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" - -[[package]] -name = "linkify" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78d59d732ba6d7eeefc418aab8057dc8e3da4374bd5802ffa95bebc04b4d1dfb" -dependencies = [ - "memchr", -] - -[[package]] -name = "local-channel" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6246c68cf195087205a0512559c97e15eaf95198bf0e206d662092cdcb03fe9f" -dependencies = [ - "futures-core", - "futures-sink", - "futures-util", - "local-waker", -] - -[[package]] -name = "local-waker" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84f9a2d3e27ce99ce2c3aad0b09b1a7b916293ea9b2bf624c13fe646fadd8da4" - -[[package]] -name = "lock_api" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "maplit" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" - -[[package]] -name = "matchers" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" -dependencies = [ - "regex-automata", -] - -[[package]] -name = "matches" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" - -[[package]] -name = "md-5" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" -dependencies = [ - "block-buffer", - "digest", - "opaque-debug", -] - -[[package]] -name = "md5" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" - -[[package]] -name = "memchr" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" - -[[package]] -name = "memoffset" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" -dependencies = [ - "autocfg", -] - -[[package]] -name = "migrations_internals" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b4fc84e4af020b837029e017966f86a1c2d5e83e64b589963d5047525995860" -dependencies = [ - "diesel", -] - -[[package]] -name = "migrations_macros" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9753f12909fd8d923f75ae5c3258cae1ed3c8ec052e1b38c93c21a6d157f789c" -dependencies = [ - "migrations_internals", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "mime" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" - -[[package]] -name = "miniz_oxide" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" -dependencies = [ - "adler", - "autocfg", -] - -[[package]] -name = "mio" -version = "0.7.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16" -dependencies = [ - "libc", - "log", - "miow", - "ntapi", - "winapi", -] - -[[package]] -name = "miow" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" -dependencies = [ - "winapi", -] - -[[package]] -name = "native-tls" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "nom" -version = "5.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" -dependencies = [ - "lexical-core", - "memchr", - "version_check", -] - -[[package]] -name = "nom" -version = "6.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2" -dependencies = [ - "bitvec", - "funty", - "lexical-core", - "memchr", - "version_check", -] - -[[package]] -name = "ntapi" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" -dependencies = [ - "winapi", -] - -[[package]] -name = "num-bigint" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "object" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c55827317fb4c08822499848a14237d2874d6f139828893017237e7ab93eb386" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "openssl" -version = "0.10.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d9facdb76fec0b73c406f125d44d86fdad818d66fef0531eec9233ca425ff4a" -dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-sys", -] - -[[package]] -name = "openssl-probe" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" - -[[package]] -name = "openssl-sys" -version = "0.9.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1996d2d305e561b70d1ee0c53f1542833f4e1ac6ce9a6708b6ff2738ca67dc82" -dependencies = [ - "autocfg", - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "ormx" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d99c9ec0597b587e61d52d8dc5914845a48a5a71f412a0a20ac4edef5db4f98" -dependencies = [ - "futures", - "ormx-macros", - "sqlx", -] - -[[package]] -name = "ormx-macros" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afe154fda773c54b533b54d4c9b97949aa37ebe5d77cdabc497e494f3ec05418" -dependencies = [ - "itertools", - "once_cell", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "owo-colors" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2386b4ebe91c2f7f51082d4cefa145d030e33a1842a96b12e4885cc3c01f7a55" - -[[package]] -name = "parking_lot" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall 0.2.10", - "smallvec", - "winapi", -] - -[[package]] -name = "paste" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf547ad0c65e31259204bd90935776d1c693cec2f4ff7abb7a1bbbd40dfe58" - -[[package]] -name = "pem" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" -dependencies = [ - "base64 0.13.0", - "once_cell", - "regex", -] - -[[package]] -name = "percent-encoding" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" - -[[package]] -name = "pest" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" -dependencies = [ - "ucd-trie", -] - -[[package]] -name = "pin-project" -version = "0.4.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "918192b5c59119d51e0cd221f4d49dde9112824ba717369e903c97d076083d0f" -dependencies = [ - "pin-project-internal 0.4.28", -] - -[[package]] -name = "pin-project" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "576bc800220cc65dac09e99e97b08b358cfab6e17078de8dc5fee223bd2d0c08" -dependencies = [ - "pin-project-internal 1.0.8", -] - -[[package]] -name = "pin-project-internal" -version = "0.4.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be26700300be6d9d23264c73211d8190e755b6b5ca7a1b28230025511b52a5e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "pin-project-internal" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e8fe8163d14ce7f0cdac2e040116f22eac817edabff0be91e8aff7e9accf389" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" - -[[package]] -name = "polyval" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eebcc4aa140b9abd2bc40d9c3f7ccec842679cd79045ac3a7ac698c1a064b7cd" -dependencies = [ - "cpuid-bool", - "opaque-debug", - "universal-hash", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro-hack" -version = "0.5.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" - -[[package]] -name = "proc-macro-nested" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" - -[[package]] -name = "proc-macro2" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "protobuf" -version = "2.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020f86b07722c5c4291f7c723eac4676b3892d47d9a7708dc2779696407f039b" - -[[package]] -name = "quote" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r2d2" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545c5bc2b880973c9c10e4067418407a0ccaa3091781d1671d46eb35107cb26f" -dependencies = [ - "log", - "parking_lot", - "scheduled-thread-pool", -] - -[[package]] -name = "radium" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8" - -[[package]] -name = "rand" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", - "rand_hc", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_hc" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" -dependencies = [ - "rand_core", -] - -[[package]] -name = "redox_syscall" -version = "0.1.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" - -[[package]] -name = "redox_syscall" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" -dependencies = [ - "bitflags", -] - -[[package]] -name = "redox_users" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" -dependencies = [ - "getrandom", - "redox_syscall 0.2.10", -] - -[[package]] -name = "regex" -version = "1.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.6.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" - -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - -[[package]] -name = "reqwest" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "246e9f61b9bb77df069a947682be06e31ac43ea37862e244a69f177694ea6d22" -dependencies = [ - "base64 0.13.0", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "http", - "http-body", - "hyper", - "hyper-tls", - "ipnet", - "js-sys", - "lazy_static", - "log", - "mime", - "native-tls", - "percent-encoding", - "pin-project-lite", - "serde", - "serde_urlencoded", - "tokio", - "tokio-native-tls", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg", -] - -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin", - "untrusted", - "web-sys", - "winapi", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dead70b0b5e03e9c814bcb6b01e03e68f7c57a80aa48c72ec92152ab3e818d49" - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver 0.9.0", -] - -[[package]] -name = "rustc_version" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" -dependencies = [ - "semver 0.11.0", -] - -[[package]] -name = "rustls" -version = "0.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" -dependencies = [ - "base64 0.13.0", - "log", - "ring", - "sct", - "webpki", -] - -[[package]] -name = "rustversion" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088" - -[[package]] -name = "ryu" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" - -[[package]] -name = "schannel" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" -dependencies = [ - "lazy_static", - "winapi", -] - -[[package]] -name = "scheduled-thread-pool" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc6f74fd1204073fa02d5d5d68bec8021be4c38690b61264b2fdb48083d0e7d7" -dependencies = [ - "parking_lot", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "sct" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "security-framework" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23a2ac85147a3a11d77ecf1bc7166ec0b92febfa4461c37944e180f319ece467" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4effb91b4b8b6fb7732e670b6cee160278ff8e6bf485c7805d9e319d76e284" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser 0.7.0", -] - -[[package]] -name = "semver" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser 0.10.2", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] - -[[package]] -name = "serde" -version = "1.0.127" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f03b9878abf6d14e6779d3f24f07b2cfa90352cfec4acc5aab8f1ac7f146fae8" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-aux" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "905f2fc9f3d1574e8b5923a58118240021f01d4e239673937ffb9f42707a4f22" -dependencies = [ - "chrono", - "serde", - "serde_json", -] - -[[package]] -name = "serde_derive" -version = "1.0.127" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a024926d3432516606328597e0f224a51355a493b49fdd67e9209187cbe55ecc" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "336b10da19a12ad094b59d870ebde26a45402e5b470add4b5fd03c5048a32127" -dependencies = [ - "indexmap", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_repr" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98d0516900518c29efa217c298fa1f4e6c6ffc85ae29fd7f4ee48f176e1a9ed5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_with" -version = "1.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad9fdbb69badc8916db738c25efd04f0a65297d26c2f8de4b62e57b8c12bc72" -dependencies = [ - "rustversion", - "serde", - "serde_with_macros", -] - -[[package]] -name = "serde_with_macros" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1569374bd54623ec8bd592cf22ba6e03c0f177ff55fbc8c29a49e296e7adecf" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "sha-1" -version = "0.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a0c8611594e2ab4ebbf06ec7cbbf0a99450b8570e96cbf5188b5d5f6ef18d81" -dependencies = [ - "block-buffer", - "cfg-if", - "cpufeatures", - "digest", - "opaque-debug", -] - -[[package]] -name = "sha1" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" - -[[package]] -name = "sha2" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b362ae5752fd2137731f9fa25fd4d9058af34666ca1966fb969119cc35719f12" -dependencies = [ - "block-buffer", - "cfg-if", - "cpufeatures", - "digest", - "opaque-debug", -] - -[[package]] -name = "sharded-slab" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740223c51853f3145fe7c90360d2d4232f2b62e3449489c207eccde818979982" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" -dependencies = [ - "libc", -] - -[[package]] -name = "simple_asn1" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692ca13de57ce0613a363c8c2f1de925adebc81b04c923ac60c5488bb44abe4b" -dependencies = [ - "chrono", - "num-bigint", - "num-traits", -] - -[[package]] -name = "slab" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c307a32c1c5c437f38c7fd45d753050587732ba8628319fbdf12a7e289ccc590" - -[[package]] -name = "smallvec" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" - -[[package]] -name = "socket2" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "sql-builder" -version = "3.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1008d95d2ec2d062959352527be30e10fec42a1aa5e5a48d990a5ff0fb9bdc0" -dependencies = [ - "anyhow", - "thiserror", -] - -[[package]] -name = "sqlformat" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d86e3c77ff882a828346ba401a7ef4b8e440df804491c6064fe8295765de71c" -dependencies = [ - "lazy_static", - "maplit", - "nom 6.1.2", - "regex", - "unicode_categories", -] - -[[package]] -name = "sqlx" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7911b0031a0247af40095838002999c7a52fba29d9739e93326e71a5a1bc9d43" -dependencies = [ - "sqlx-core", - "sqlx-macros", -] - -[[package]] -name = "sqlx-core" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aec89bfaca8f7737439bad16d52b07f1ccd0730520d3bf6ae9d069fe4b641fb1" -dependencies = [ - "ahash", - "atoi", - "base64 0.13.0", - "bitflags", - "byteorder", - "bytes", - "chrono", - "crc", - "crossbeam-channel", - "crossbeam-queue", - "crossbeam-utils", - "dirs", - "either", - "futures-channel", - "futures-core", - "futures-intrusive", - "futures-util", - "hashlink", - "hex", - "hmac 0.11.0", - "indexmap", - "itoa", - "libc", - "log", - "md-5", - "memchr", - "once_cell", - "parking_lot", - "percent-encoding", - "rand", - "rustls", - "serde", - "serde_json", - "sha-1", - "sha2", - "smallvec", - "sqlformat", - "sqlx-rt", - "stringprep", - "thiserror", - "tokio-stream", - "url", - "uuid", - "webpki", - "webpki-roots", - "whoami", -] - -[[package]] -name = "sqlx-macros" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "584866c833511b1a152e87a7ee20dee2739746f60c858b3c5209150bc4b466f5" -dependencies = [ - "dotenv", - "either", - "heck", - "hex", - "once_cell", - "proc-macro2", - "quote", - "serde", - "serde_json", - "sha2", - "sqlx-core", - "sqlx-rt", - "syn", - "url", -] - -[[package]] -name = "sqlx-rt" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d1bd069de53442e7a320f525a6d4deb8bb0621ac7a55f7eccbc2b58b57f43d0" -dependencies = [ - "actix-rt", - "once_cell", - "tokio", - "tokio-rustls", -] - -[[package]] -name = "standback" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" -dependencies = [ - "version_check", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "stdweb" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" -dependencies = [ - "discard", - "rustc_version 0.2.3", - "stdweb-derive", - "stdweb-internal-macros", - "stdweb-internal-runtime", - "wasm-bindgen", -] - -[[package]] -name = "stdweb-derive" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "serde_derive", - "syn", -] - -[[package]] -name = "stdweb-internal-macros" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" -dependencies = [ - "base-x", - "proc-macro2", - "quote", - "serde", - "serde_derive", - "serde_json", - "sha1", - "syn", -] - -[[package]] -name = "stdweb-internal-runtime" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" - -[[package]] -name = "stringprep" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "strum" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaf86bbcfd1fa9670b7a129f64fc0c9fcbbfe4f1bc4210e9e98fe71ffc12cde2" - -[[package]] -name = "strum_macros" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "syn" -version = "1.0.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "tempfile" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" -dependencies = [ - "cfg-if", - "libc", - "rand", - "redox_syscall 0.2.10", - "remove_dir_all", - "winapi", -] - -[[package]] -name = "termcolor" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "thiserror" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "thread-id" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1" -dependencies = [ - "libc", - "redox_syscall 0.1.57", - "winapi", -] - -[[package]] -name = "thread_local" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" -dependencies = [ - "once_cell", -] - -[[package]] -name = "time" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" -dependencies = [ - "libc", - "wasi", - "winapi", -] - -[[package]] -name = "time" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242" -dependencies = [ - "const_fn", - "libc", - "standback", - "stdweb", - "time-macros", - "version_check", - "winapi", -] - -[[package]] -name = "time" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41effe7cfa8af36f439fac33861b66b049edc6f9a32331e2312660529c1c24ad" -dependencies = [ - "itoa", - "libc", -] - -[[package]] -name = "time-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" -dependencies = [ - "proc-macro-hack", - "time-macros-impl", -] - -[[package]] -name = "time-macros-impl" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" -dependencies = [ - "proc-macro-hack", - "proc-macro2", - "quote", - "standback", - "syn", -] - -[[package]] -name = "tinyvec" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "848a1e1181b9f6753b5e96a092749e29b11d19ede67dfbbd6c7dc7e0f49b5338" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" - -[[package]] -name = "tokio" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cf844b23c6131f624accf65ce0e4e9956a8bb329400ea5bcc26ae3a5c20b0b" -dependencies = [ - "autocfg", - "bytes", - "libc", - "memchr", - "mio", - "num_cpus", - "once_cell", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "tokio-macros", - "winapi", -] - -[[package]] -name = "tokio-macros" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54473be61f4ebe4efd09cec9bd5d16fa51d70ea0192213d754d2d500457db110" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" -dependencies = [ - "rustls", - "tokio", - "webpki", -] - -[[package]] -name = "tokio-stream" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b2f3f698253f03119ac0102beaa64f67a67e08074d03a22d18784104543727f" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "511de3f85caf1c98983545490c3d09685fa8eb634e57eec22bb4db271f46cbd8" -dependencies = [ - "futures-util", - "log", - "pin-project 1.0.8", - "tokio", - "tungstenite", -] - -[[package]] -name = "tokio-util" -version = "0.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1caa0b0c8d94a049db56b5acf8cba99dc0623aab1b26d5b5f5e2d945846b3592" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "log", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "toml" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" -dependencies = [ - "serde", -] - -[[package]] -name = "tower-service" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" - -[[package]] -name = "tracing" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d" -dependencies = [ - "cfg-if", - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-appender" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9965507e507f12c8901432a33e31131222abac31edd90cabbcf85cf544b7127a" -dependencies = [ - "chrono", - "crossbeam-channel", - "tracing-subscriber", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42e6fa53307c8a17e4ccd4dc81cf5ec38db9209f59b222210375b54ee40d1e2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tracing-bunyan-formatter" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c408910c9b7eabc0215fe2b4a89f8ec95581a91cea1f7619f7c78caf14cbc2a1" -dependencies = [ - "chrono", - "gethostname", - "log", - "serde", - "serde_json", - "tracing", - "tracing-core", - "tracing-log", - "tracing-subscriber", -] - -[[package]] -name = "tracing-core" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ca517f43f0fb96e0c3072ed5c275fe5eece87e8cb52f4a77b69226d3b1c9df8" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project 1.0.8", - "tracing", -] - -[[package]] -name = "tracing-log" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6923477a48e41c1951f1999ef8bb5a3023eb723ceadafe78ffb65dc366761e3" -dependencies = [ - "lazy_static", - "log", - "tracing-core", -] - -[[package]] -name = "tracing-serde" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b" -dependencies = [ - "serde", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9cbe87a2fa7e35900ce5de20220a582a9483a7063811defce79d7cbd59d4cfe" -dependencies = [ - "ansi_term", - "chrono", - "lazy_static", - "matchers", - "regex", - "serde", - "serde_json", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", - "tracing-serde", -] - -[[package]] -name = "try-lock" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" - -[[package]] -name = "tungstenite" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0b2d8558abd2e276b0a8df5c05a2ec762609344191e5fd23e292c910e9165b5" -dependencies = [ - "base64 0.13.0", - "byteorder", - "bytes", - "http", - "httparse", - "log", - "rand", - "sha-1", - "thiserror", - "url", - "utf-8", -] - -[[package]] -name = "typenum" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" - -[[package]] -name = "ucd-trie" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" - -[[package]] -name = "unicode-bidi" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "246f4c42e67e7a4e3c6106ff716a5d067d4132a642840b242e357e468a2a0085" - -[[package]] -name = "unicode-normalization" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-segmentation" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" - -[[package]] -name = "unicode-xid" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - -[[package]] -name = "unicode_categories" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" - -[[package]] -name = "universal-hash" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - -[[package]] -name = "url" -version = "2.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" -dependencies = [ - "form_urlencoded", - "idna", - "matches", - "percent-encoding", -] - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "uuid" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" -dependencies = [ - "getrandom", - "serde", -] - -[[package]] -name = "validator" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d6937c33ec6039d8071bcf72933146b5bbe378d645d8fa59bdadabfc2a249" -dependencies = [ - "idna", - "lazy_static", - "regex", - "serde", - "serde_derive", - "serde_json", - "url", - "validator_types", -] - -[[package]] -name = "validator_types" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad9680608df133af2c1ddd5eaf1ddce91d60d61b6bc51494ef326458365a470a" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" - -[[package]] -name = "want" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" -dependencies = [ - "log", - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - -[[package]] -name = "wasm-bindgen" -version = "0.2.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b608ecc8f4198fe8680e2ed18eccab5f0cd4caaf3d83516fa5fb2e927fda2586" -dependencies = [ - "cfg-if", - "serde", - "serde_json", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "580aa3a91a63d23aac5b6b267e2d13cb4f363e31dce6c352fca4752ae12e479f" -dependencies = [ - "bumpalo", - "lazy_static", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16646b21c3add8e13fdb8f20172f8a28c3dbf62f45406bcff0233188226cfe0c" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "171ebf0ed9e1458810dfcb31f2e766ad6b3a89dbda42d8901f2b268277e5f09c" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c2657dd393f03aa2a659c25c6ae18a13a4048cebd220e147933ea837efc589f" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e0c4a743a309662d45f4ede961d7afa4ba4131a59a639f29b0069c3798bbcc2" - -[[package]] -name = "web-sys" -version = "0.3.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01c70a82d842c9979078c772d4a1344685045f1a5628f677c2b2eab4dd7d2696" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki" -version = "0.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "webpki-roots" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" -dependencies = [ - "webpki", -] - -[[package]] -name = "whoami" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7741161a40200a867c96dfa5574544efa4178cf4c8f770b62dd1cc0362d7ae1" -dependencies = [ - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "winreg" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" -dependencies = [ - "winapi", -] - -[[package]] -name = "wyz" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" - -[[package]] -name = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] - -[[package]] -name = "zstd" -version = "0.7.0+zstd.1.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9428752481d8372e15b1bf779ea518a179ad6c771cca2d2c60e4fbff3cc2cd52" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "3.1.0+zstd.1.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa1926623ad7fe406e090555387daf73db555b948134b4d73eac5eb08fb666d" -dependencies = [ - "libc", - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "1.5.0+zstd.1.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e6c094340240369025fc6b731b054ee2a834328fa584310ac96aa4baebdc465" -dependencies = [ - "cc", - "libc", -] diff --git a/backend/Cargo.toml b/backend/Cargo.toml deleted file mode 100644 index f16966df15..0000000000 --- a/backend/Cargo.toml +++ /dev/null @@ -1,109 +0,0 @@ -[package] -name = "backend" -version = "0.1.0" -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -actix = "0.12" -#actix-web = "3" -#actix-http = "2.2.1" -#actix-web-actors = "3" -actix-codec = "0.4" -actix-web = "4.0.0-beta.11" -actix-http = "3.0.0-beta.12" -actix-rt = "2" -actix-web-actors = { version = "4.0.0-beta.7" } -actix-service = "2.0.1" -actix-identity = "0.4.0-beta.3" -actix-cors = "0.6.0-beta.3" - -futures = "0.3.15" -bytes = "1" -toml = "0.5.8" -dashmap = "4.0" -log = "0.4.14" -async-trait = "0.1.52" - -# tracing -tracing = { version = "0.1", features = ["log"] } -tracing-futures = "0.2.4" -tracing-subscriber = { version = "0.2.12", features = ["registry", "env-filter", "ansi", "json"] } -tracing-bunyan-formatter = "0.2.2" -tracing-appender = "0.1" -tracing-core = "0.1" -tracing-log = { version = "0.1.1"} - - -# serde -serde_json = "1.0" -serde = { version = "1.0", features = ["derive"] } -serde_repr = "0.1" -serde-aux = "1.0.1" - -derive_more = {version = "0.99"} -protobuf = {version = "2.20.0"} -uuid = { version = "0.8", features = ["serde", "v4"] } -config = { version = "0.10.1", default-features = false, features = ["yaml"] } -chrono = { version = "0.4", features = ["serde"] } -anyhow = "1.0.40" -thiserror = "1.0.24" -bcrypt = "0.10" -jsonwebtoken = "7.2" -sql-builder = "3.1.1" -lazy_static = "1.4" -tokio = { version = "1", features = ["full"] } -parking_lot = "0.11" -md5 = "0.7.0" -futures-core = { version = "0.3", default-features = false } -pin-project = "1.0.0" -byteorder = {version = "1.3.4"} -async-stream = "0.3.2" - -flowy-user-data-model = { path = "../shared-lib/flowy-user-data-model" } -flowy-folder-data-model = { path = "../shared-lib/flowy-folder-data-model" } -flowy-collaboration = { path = "../shared-lib/flowy-collaboration" } -lib-ws = { path = "../shared-lib/lib-ws" } -lib-ot = { path = "../shared-lib/lib-ot" } -lib-infra = { path = "../shared-lib/lib-infra" } -backend-service = { path = "../shared-lib/backend-service", features = ["http_server"] } - -ormx = { version = "0.7", features = ["postgres"]} -[dependencies.sqlx] -version = "0.5.7" -default-features = false -features = [ - "runtime-actix-rustls", - "macros", - "postgres", - "uuid", - "chrono", - "migrate", - "offline", -] - - -[lib] -path = "src/lib.rs" - -[[bin]] -name = "backend" -path = "src/main.rs" - -[features] -flowy_test = [] -ignore_auth = [] - -[dev-dependencies] -parking_lot = "0.11" -once_cell = "1.7.2" -linkify = "0.5.0" -futures-util = "0.3.15" -backend = { path = ".", features = ["flowy_test"]} -flowy-sdk = { path = "../frontend/rust-lib/flowy-sdk", features = ["http_server"] } -flowy-user = { path = "../frontend/rust-lib/flowy-user", features = ["http_server"] } -flowy-document = { path = "../frontend/rust-lib/flowy-document", features = ["flowy_unit_test", "http_server"] } -flowy-test = { path = "../frontend/rust-lib/flowy-test" } -flowy-net = { path = "../frontend/rust-lib/flowy-net", features = ["http_server"] } - diff --git a/backend/Dockerfile b/backend/Dockerfile deleted file mode 100644 index 38c62d794d..0000000000 --- a/backend/Dockerfile +++ /dev/null @@ -1,23 +0,0 @@ -FROM rust:1.56.1 as builder -WORKDIR /app - -COPY . . -WORKDIR /app/backend -ENV SQLX_OFFLINE true -RUN RUSTFLAGS="-C opt-level=2" cargo build --release --bin backend -# Size optimization -#RUN strip ./target/release/backend - -FROM debian:bullseye-slim AS runtime -WORKDIR /app -RUN apt-get update -y \ - && apt-get install -y --no-install-recommends openssl \ - # Clean up - && apt-get autoremove -y \ - && apt-get clean -y \ - && rm -rf /var/lib/apt/lists/* - -COPY --from=builder /app/backend/target/release/backend /usr/local/bin/backend -COPY --from=builder /app/backend/configuration configuration -ENV APP_ENVIRONMENT production -CMD ["backend"] diff --git a/backend/Makefile b/backend/Makefile deleted file mode 100644 index e5e7138e22..0000000000 --- a/backend/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -ROOT = "./scripts" -SEMVER_VERSION=$(shell grep version Cargo.toml | awk -F"\"" '{print $$2}' | head -n 1) - -.PHONY: init_database run_docker run_test - -init_database: - POSTGRES_PORT=5433 ${ROOT}/init_database.sh - -docker_image: - source $(ROOT)/docker_env.sh && docker-compose up -d db - source $(ROOT)/docker_env.sh && docker-compose up -d backend - -local_server: - cargo run - -docker_test: - sh $(ROOT)/docker_test.sh - -local_test: - # 🔥 Must run init_database first - SQLX_OFFLINE=true cargo test - - diff --git a/backend/configuration/base.yaml b/backend/configuration/base.yaml deleted file mode 100644 index 4b78eb1b42..0000000000 --- a/backend/configuration/base.yaml +++ /dev/null @@ -1,9 +0,0 @@ -application: - port: 8000 - host: 0.0.0.0 -database: - host: "localhost" - port: 5433 - username: "postgres" - password: "password" - database_name: "flowy" diff --git a/backend/configuration/local.yaml b/backend/configuration/local.yaml deleted file mode 100644 index d7f541e28d..0000000000 --- a/backend/configuration/local.yaml +++ /dev/null @@ -1,5 +0,0 @@ -application: - host: 127.0.0.1 - base_url: "http://127.0.0.1" -database: - require_ssl: false diff --git a/backend/configuration/production.yaml b/backend/configuration/production.yaml deleted file mode 100644 index 936886c05e..0000000000 --- a/backend/configuration/production.yaml +++ /dev/null @@ -1,6 +0,0 @@ -application: - host: 0.0.0.0 -database: - host: "db" - port: 5432 - require_ssl: false diff --git a/backend/doc/database_setup.md b/backend/doc/database_setup.md deleted file mode 100644 index 3a89974cc9..0000000000 --- a/backend/doc/database_setup.md +++ /dev/null @@ -1,70 +0,0 @@ - - - -### Docker - -1. follow the [instructions](https://docs.docker.com/desktop/mac/install/) to install docker. -2. open terminal and run: `docker pull postgres` -3run `make init_database`. It will create the database scheme on remote specified by DATABASE_URL. You can connect you database using -pgAdmin. - -![img_2.png](img_2.png) - -The information you enter must be the same as the `make init_postgres`. e.g. -``` -export DB_USER=postgres -export DB_PASSWORD=password -export DB_NAME=flowy -export DB_PORT=5432 -``` - -![img_1.png](img_1.png) - -[Docker command](https://docs.docker.com/engine/reference/commandline/builder_prune/) - -### Run -By default, Docker images do not expose their ports to the underlying host machine. We need to do it explicitly using the -p flag. -`docker run -p 8000:8000 backend` - - -### Sqlx - -**sqlx-cli** -* [install sqlx-cli](https://github.com/launchbadge/sqlx/tree/master/sqlx-cli) - -**Sqlx and Diesel commands** -* create migration - * sqlx: sqlx migrate add $(table) - * diesel: diesel migration generation $(table) - -* run migration - * sqlx: sqlx migrate run - * diesel: diesel migration run - -* reset database - * sqlx: sqlx database reset - * diesel: diesel database reset - -**offline mode** - -`cargo sqlx prepare -- --bin backend` - -**Type mapping** -* [postgres type map](https://docs.rs/sqlx/0.5.7/sqlx/postgres/types/index.html) -* [postgres and diesel type map](https://kotiri.com/2018/01/31/postgresql-diesel-rust-types.html) - - -## Q&A -1. Receive` { code: 24, kind: Other, message: "Too many open files" } on arbiter` after running cargo test on backend. -> This is due to a limit enforced by the operating system on the maximum number of open file descriptors (including sockets) for each process. -> Raising the file descriptor limit using `ulimit -n 2048` to solve this issue. It won't stay after reboot so check on google how to persist -> that value if you want to. -> -> or you can try: -> `launchctl limit maxfiles 2048 2048` -> `launchctl limit maxfiles` -> -> Don't forget to relaunch your terminal. - -## More -* [11-database-drivers](https://blog.logrocket.com/11-database-drivers-and-orms-for-rust-that-are-ready-for-production/) diff --git a/backend/doc/database_struct.md b/backend/doc/database_struct.md deleted file mode 100644 index 6a8ef645c1..0000000000 --- a/backend/doc/database_struct.md +++ /dev/null @@ -1,200 +0,0 @@ - -# Table Struct - -## Table: user_table - -- `Name`: UserTable -- `Comment`: UserTable - -### `Primary Key` - -- `Columns`: id - -### `Indexes[]` - -| `Columns` | `Unique` | -| --------- | -------- | -| email | `true` | - -### `Foreign Keys[]` - -| `Columns` | `Ref Table` | `Ref Columns` | `Options` | -| --------- | ----------- | ------------- | --------- | - - -### `Columns[]` - -| `Label` | `Name` | `Type` | `Nullable` | `Default` | `Comment` | -| ----------- | ----------- | ----------- | ---------- | --------- | --------- | -| id | id | uuid | `false` | | | -| email | email | text | `false` | | | -| name | name | text | `false` | | | -| password | password | text | `false` | | | -| create_time | create_time | timestamptz | `false` | | | - - -## Table: workspace_table - -- `Name`: WorkspaceTable -- `Comment`: WorkspaceTable - -### `Primary Key` - -- `Columns`: id - -### `Indexes[]` - -| `Columns` | `Unique` | -| --------- | -------- | - -### `Foreign Keys[]` - -| `Columns` | `Ref Table` | `Ref Columns` | `Options` | -| --------- | ----------- | ------------- | --------- | -| user_id | user_table | id | | - -### `Columns[]` - -| `Label` | `Name` | `Type` | `Nullable` | `Default` | `Comment` | -| ------------- | ------------- | ----------- | ---------- | --------- | --------- | -| id | id | uuid | `false` | | | -| user_id | user_id | text | `false` | | | -| name | name | text | `false` | | | -| description | description | text | `false` | | | -| create_time | create_time | timestamptz | `false` | | | -| modified_time | modified_time | timestamptz | `false` | | | - - -## Table: app_table - -- `Name`: AppTable -- `Comment`: AppTable - -### `Primary Key` - -- `Columns`: id - -### `Indexes[]` - -| `Columns` | `Unique` | -| --------- | -------- | - -### `Foreign Keys[]` - -| `Columns` | `Ref Table` | `Ref Columns` | `Options` | -| ------------ | --------------- | ------------- | --------- | -| user_id | user_table | id | | -| workspace_id | workspace_table | id | | -| last_view_id | view_table | id | | - -### `Columns[]` - -| `Label` | `Name` | `Type` | `Nullable` | `Default` | `Comment` | -| ------------- | ------------- | ----------- | ---------- | --------- | --------- | -| id | id | uuid | `false` | | | -| user_id | user_id | text | `false` | | | -| workspace_id | workspace_id | text | `false` | | | -| last_view_id | workspace_id | text | `false` | | | -| name | name | text | `false` | | | -| description | description | text | `false` | | | -| color_style | color_style | text | `false` | | | -| is_trash | is_trash | bool | `false` | `false` | | -| create_time | create_time | timestamptz | `false` | | | -| modified_time | modified_time | timestamptz | `false` | | | - - -## Table: view_table - -- `Name`: ViewTable -- `Comment`: ViewTable - -### `Primary Key` - -- `Columns`: id - -### `Indexes[]` - -| `Columns` | `Unique` | -| --------- | -------- | - -### `Foreign Keys[]` - -| `Columns` | `Ref Table` | `Ref Columns` | `Options` | -| ------------ | ----------- | ------------- | --------- | -| user_id | user_table | id | | -| belong_to_id | app_table | id | | - -### `Columns[]` - -| `Label` | `Name` | `Type` | `Nullable` | `Default` | `Comment` | -| ------------- | ------------- | ----------- | ---------- | --------- | --------- | -| id | id | uuid | `false` | | | -| belong_to_id | belong_to_id | text | `false` | | | -| name | name | text | `false` | | | -| description | description | text | `false` | | | -| thumbnail | thumbnail | text | `false` | | | -| view_type | view_type | int | `false` | | | -| create_time | create_time | timestamptz | `false` | | | -| modified_time | modified_time | timestamptz | `false` | | | - - -## Table: doc_table - -- `Name`: DocTable -- `Comment`: DocTable - -### `Primary Key` - -- `Columns`: id - -### `Indexes[]` - -| `Columns` | `Unique` | -| --------- | -------- | - -### `Foreign Keys[]` - -| `Columns` | `Ref Table` | `Ref Columns` | `Options` | -| --------- | ----------- | ------------- | --------- | -| rev_id | doc_table | id | | - - - -### `Columns[]` - -| `Label` | `Name` | `Type` | `Nullable` | `Default` | `Comment` | -| ------- | ------ | ------ | ---------- | --------- | --------- | -| id | id | uuid | `false` | | | -| rev_id | rev_id | text | `false` | | | -| data | data | text | `false` | | | - - -## Table: trash_table - -- `Name`: TrashTable -- `Comment`: TrashTable - -### `Primary Key` - -- `Columns`: id - -### `Indexes[]` - -| `Columns` | `Unique` | -| --------- | -------- | - -### `Foreign Keys[]` - -| `Columns` | `Ref Table` | `Ref Columns` | `Options` | -| --------- | ----------- | ------------- | --------- | -| user_id | user_table | id | | - - -### `Columns[]` - -| `Label` | `Name` | `Type` | `Nullable` | `Default` | `Comment` | -| ------- | ------- | ------ | ---------- | --------- | --------- | -| id | id | uuid | `false` | | | -| user_id | user_id | text | `false` | | | -| ty | ty | int4 | `false` | 0 | | - diff --git a/backend/doc/img_1.png b/backend/doc/img_1.png deleted file mode 100644 index 64fcbddfd9292ce23937396f0256cc7197679ede..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 218966 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 A7=HPpGw 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

sB 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 diff --git a/backend/doc/img_2.png b/backend/doc/img_2.png deleted file mode 100644 index 1a9ddc53a2e2fa26ce2368c49c2f489beaeb9796..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49053 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 mBiAT3l8ZAU|mQ0pv(>r+XL?CUg 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 o2rR)w#Os|hSVVulDDvQ_{U%Qe_C&2 "Error: `psql` is not installed." - echo >&2 "install using brew: brew install libpq." - echo >&2 "link to /usr/local/bin: brew link --force libpq ail" - - exit 1 -fi - -if ! [ -x "$(command -v sqlx)" ]; then - echo >&2 "Error: `sqlx` is not installed." - echo >&2 "Use:" - echo >&2 " cargo install --version=^0.5.7 sqlx-cli --no-default-features --features postgres" - echo >&2 "to install it." - exit 1 -fi - -DB_USER="${POSTGRES_USER:=postgres}" -DB_PASSWORD="${POSTGRES_PASSWORD:=password}" -DB_PORT="${POSTGRES_PORT:=5432}" -DB_HOST="${POSTGRES_HOST:=localhost}" -DB_NAME="${POSTGRES_DB:=flowy}" - -if [[ -z "${SKIP_DOCKER}" ]] -then - RUNNING_POSTGRES_CONTAINER=$(docker ps --filter 'name=postgres' --format '{{.ID}}') - if [[ -n $RUNNING_POSTGRES_CONTAINER ]]; then - echo >&2 "there is a postgres container already running, kill it with" - echo >&2 " docker kill ${RUNNING_POSTGRES_CONTAINER}" - exit 1 - fi - - docker run \ - -e POSTGRES_USER=${DB_USER} \ - -e POSTGRES_PASSWORD=${DB_PASSWORD} \ - -e POSTGRES_DB="${DB_NAME}" \ - -p "${DB_PORT}":5432 \ - -d \ - --name "flowy_postgres_$(date '+%s')" \ - postgres -N 1000 -fi - - -# Keep pinging Postgres until it's ready to accept commands -until PGPASSWORD="${DB_PASSWORD}" psql -h "${DB_HOST}" -U "${DB_USER}" -p "${DB_PORT}" -d "postgres" -c '\q'; do - - >&2 echo "Postgres is still unavailable - sleeping" - sleep 1 -done - ->&2 echo "Postgres is up and running on port ${DB_PORT} - running migrations now!" - -export DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@localhost:${DB_PORT}/${DB_NAME} -sqlx database create -sqlx migrate run - ->&2 echo "Postgres has been migrated, ready to go!" - diff --git a/backend/sqlx-data.json b/backend/sqlx-data.json deleted file mode 100644 index 12665e96ba..0000000000 --- a/backend/sqlx-data.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "db": "PostgreSQL", - "e8c487b4314c267f6da2667b95f6c8003fabc2461c10df2d6d39d081e74e167f": { - "query": "\n INSERT INTO user_table (id, email, name, create_time, password)\n VALUES ($1, $2, $3, $4, $5)\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Text", - "Text", - "Timestamptz", - "Text" - ] - }, - "nullable": [] - } - } -} \ No newline at end of file diff --git a/backend/src/application.rs b/backend/src/application.rs deleted file mode 100644 index 5092f22751..0000000000 --- a/backend/src/application.rs +++ /dev/null @@ -1,166 +0,0 @@ -use actix::Actor; -use actix_identity::{CookieIdentityPolicy, IdentityService}; -use actix_web::{dev::Server, middleware, web, web::Data, App, HttpServer, Scope}; -use sqlx::{postgres::PgPoolOptions, PgPool}; -use std::{net::TcpListener, time::Duration}; -use tokio::time::interval; - -use crate::{ - config::{ - env::{domain, secret, use_https}, - DatabaseSettings, Settings, - }, - context::AppContext, - services::{ - document::router as doc, - folder::{app::router as app, trash::router as trash, view::router as view, workspace::router as workspace}, - user::router as user, - web_socket::WSServer, - }, -}; - -pub struct Application { - port: u16, - server: Server, -} - -impl Application { - pub async fn build(configuration: Settings, app_ctx: AppContext) -> Result { - let address = format!("{}:{}", configuration.application.host, configuration.application.port); - let listener = TcpListener::bind(&address)?; - let port = listener.local_addr().unwrap().port(); - let server = run(listener, app_ctx)?; - Ok(Self { port, server }) - } - - pub async fn run_until_stopped(self) -> Result<(), std::io::Error> { - self.server.await - } - - pub fn port(&self) -> u16 { - self.port - } -} - -pub fn run(listener: TcpListener, app_ctx: AppContext) -> Result { - let domain = domain(); - let secret: String = secret(); - actix_rt::spawn(period_check(app_ctx.persistence.pg_pool())); - - let server = HttpServer::new(move || { - App::new() - .wrap(middleware::Logger::default()) - .wrap(identify_service(&domain, &secret)) - .wrap(crate::middleware::default_cors()) - .wrap(crate::middleware::AuthenticationService) - .app_data(web::JsonConfig::default().limit(4096)) - .service(ws_scope()) - .service(user_scope()) - .app_data(app_ctx.ws_server.clone()) - .app_data(app_ctx.persistence.clone()) - .app_data(Data::new(app_ctx.persistence.pg_pool())) - .app_data(app_ctx.ws_receivers.clone()) - .app_data(app_ctx.document_manager.clone()) - }) - .listen(listener)? - .run(); - Ok(server) -} - -#[allow(dead_code)] -async fn period_check(_pool: PgPool) { - let mut i = interval(Duration::from_secs(60)); - loop { - i.tick().await; - } -} - -fn ws_scope() -> Scope { - web::scope("/ws").service(crate::services::web_socket::router::establish_ws_connection) -} - -fn user_scope() -> Scope { - // https://developer.mozilla.org/en-US/docs/Web/HTTP - // TODO: replace GET body with query params - web::scope("/api") - // authentication - .service( - web::resource("/auth") - .route(web::post().to(user::sign_in_handler)) - .route(web::delete().to(user::sign_out_handler)), - ) - .service( - web::resource("/user") - .route(web::patch().to(user::set_user_profile_handler)) - .route(web::get().to(user::get_user_profile_handler)), - ) - .service(web::resource("/register").route(web::post().to(user::register_handler))) - .service( - web::resource("/workspace") - .route(web::post().to(workspace::create_handler)) - .route(web::delete().to(workspace::delete_handler)) - .route(web::get().to(workspace::read_handler)) - .route(web::patch().to(workspace::update_handler)), - ) - .service(web::resource("/workspace_list/{user_id}").route(web::get().to(workspace::workspace_list))) - .service( - web::resource("/app") - .route(web::post().to(app::create_handler)) - .route(web::get().to(app::read_handler)) - .route(web::delete().to(app::delete_handler)) - .route(web::patch().to(app::update_handler)), - ) - .service( - web::resource("/view") - .route(web::post().to(view::create_handler)) - .route(web::delete().to(view::delete_handler)) - .route(web::get().to(view::read_handler)) - .route(web::patch().to(view::update_handler)), - ) - .service( - web::resource("/doc") - .route(web::post().to(doc::create_document_handler)) - .route(web::get().to(doc::read_document_handler)) - .route(web::patch().to(doc::reset_document_handler)), - ) - .service( - web::resource("/trash") - .route(web::post().to(trash::create_handler)) - .route(web::delete().to(trash::delete_handler)) - .route(web::get().to(trash::read_handler)), - ) - .service(web::resource("/sync").route(web::post().to(trash::create_handler))) - // password - .service(web::resource("/password_change").route(web::post().to(user::change_password))) -} - -pub async fn init_app_context(configuration: &Settings) -> AppContext { - let level = std::env::var("RUST_LOG").unwrap_or_else(|_| "info".to_owned()); - let _ = crate::services::log::Builder::new("flowy-server") - .env_filter(&level) - .build(); - let pg_pool = get_connection_pool(&configuration.database) - .await - .unwrap_or_else(|_| panic!("Failed to connect to Postgres at {:?}.", configuration.database)); - - let ws_server = WSServer::new().start(); - AppContext::new(ws_server, pg_pool) -} - -pub fn identify_service(domain: &str, secret: &str) -> IdentityService { - IdentityService::new( - CookieIdentityPolicy::new(secret.as_bytes()) - .name("auth") - .path("/") - .domain(domain) - .max_age_secs(24 * 3600) - .secure(use_https()), - ) -} - -pub async fn get_connection_pool(configuration: &DatabaseSettings) -> Result { - PgPoolOptions::new() - .connect_timeout(std::time::Duration::from_secs(5)) - .connect_with(configuration.with_db()) - .await -} diff --git a/backend/src/config/configuration.rs b/backend/src/config/configuration.rs deleted file mode 100644 index d18522a2dd..0000000000 --- a/backend/src/config/configuration.rs +++ /dev/null @@ -1,106 +0,0 @@ -use serde_aux::field_attributes::deserialize_number_from_string; -use sqlx::postgres::{PgConnectOptions, PgSslMode}; -use std::convert::{TryFrom, TryInto}; - -#[derive(serde::Deserialize, Clone, Debug)] -pub struct Settings { - pub database: DatabaseSettings, - pub application: ApplicationSettings, -} - -// We are using 127.0.0.1 as our host in address, we are instructing our -// application to only accept connections coming from the same machine. However, -// request from the hose machine which is not seen as local by our Docker image. -// -// Using 0.0.0.0 as host to instruct our application to accept connections from -// any network interface. So using 127.0.0.1 for our local development and set -// it to 0.0.0.0 in our Docker images. -// -#[derive(serde::Deserialize, Clone, Debug)] -pub struct ApplicationSettings { - #[serde(deserialize_with = "deserialize_number_from_string")] - pub port: u16, - pub host: String, -} - -#[derive(serde::Deserialize, Clone, Debug)] -pub struct DatabaseSettings { - pub username: String, - pub password: String, - #[serde(deserialize_with = "deserialize_number_from_string")] - pub port: u16, - pub host: String, - pub database_name: String, - pub require_ssl: bool, -} - -impl DatabaseSettings { - pub fn without_db(&self) -> PgConnectOptions { - let ssl_mode = if self.require_ssl { - PgSslMode::Require - } else { - PgSslMode::Prefer - }; - PgConnectOptions::new() - .host(&self.host) - .username(&self.username) - .password(&self.password) - .port(self.port) - .ssl_mode(ssl_mode) - } - - pub fn with_db(&self) -> PgConnectOptions { - self.without_db().database(&self.database_name) - } -} - -pub fn get_configuration() -> Result { - let mut settings = config::Config::default(); - let base_path = std::env::current_dir().expect("Failed to determine the current directory"); - let configuration_dir = base_path.join("configuration"); - settings.merge(config::File::from(configuration_dir.join("base")).required(true))?; - - let environment: Environment = std::env::var("APP_ENVIRONMENT") - .unwrap_or_else(|_| "local".into()) - .try_into() - .expect("Failed to parse APP_ENVIRONMENT."); - - settings.merge(config::File::from(configuration_dir.join(environment.as_str())).required(true))?; - - // Add in settings from environment variables (with a prefix of APP and '__' as - // separator) E.g. `APP_APPLICATION__PORT=5001 would set - // `Settings.application.port` - settings.merge(config::Environment::with_prefix("app").separator("__"))?; - - settings.try_into() -} - -/// The possible runtime environment for our application. -pub enum Environment { - Local, - Production, -} - -impl Environment { - pub fn as_str(&self) -> &'static str { - match self { - Environment::Local => "local", - Environment::Production => "production", - } - } -} - -impl TryFrom for Environment { - type Error = String; - - fn try_from(s: String) -> Result { - match s.to_lowercase().as_str() { - "local" => Ok(Self::Local), - "production" => Ok(Self::Production), - other => Err(format!( - "{} is not a supported environment. Use either `local` or `production`.", - other - )), - } - } -} diff --git a/backend/src/config/const_define.rs b/backend/src/config/const_define.rs deleted file mode 100644 index 33e81e22a0..0000000000 --- a/backend/src/config/const_define.rs +++ /dev/null @@ -1,7 +0,0 @@ -use std::time::Duration; - -pub const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(8); -pub const PING_TIMEOUT: Duration = Duration::from_secs(60); -pub const MAX_PAYLOAD_SIZE: usize = 262_144; // max payload size is 256k - -pub const IGNORE_ROUTES: [&str; 3] = ["/api/register", "/api/auth", "/ws"]; diff --git a/backend/src/config/env.rs b/backend/src/config/env.rs deleted file mode 100644 index b4e1c2e376..0000000000 --- a/backend/src/config/env.rs +++ /dev/null @@ -1,17 +0,0 @@ -use std::env; - -pub fn domain() -> String { - env::var("DOMAIN").unwrap_or_else(|_| "localhost".to_string()) -} - -pub fn jwt_secret() -> String { - env::var("JWT_SECRET").unwrap_or_else(|_| "my secret".into()) -} - -pub fn secret() -> String { - env::var("SECRET_KEY").unwrap_or_else(|_| "0123".repeat(8)) -} - -pub fn use_https() -> bool { - false -} diff --git a/backend/src/config/mod.rs b/backend/src/config/mod.rs deleted file mode 100644 index af27ede86d..0000000000 --- a/backend/src/config/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -mod configuration; -mod const_define; -pub mod env; - -pub use configuration::*; -pub use const_define::*; diff --git a/backend/src/context.rs b/backend/src/context.rs deleted file mode 100644 index d3b80af564..0000000000 --- a/backend/src/context.rs +++ /dev/null @@ -1,92 +0,0 @@ -use crate::services::{ - kv::PostgresKV, - web_socket::{WSServer, WebSocketReceivers}, -}; -use actix::Addr; -use actix_web::web::Data; - -use crate::services::{ - document::ws_receiver::{make_document_ws_receiver, HttpDocumentCloudPersistence}, - folder::ws_receiver::{make_folder_ws_receiver, HttpFolderCloudPersistence}, - kv::revision_kv::RevisionKVPersistence, -}; -use flowy_collaboration::{server_document::ServerDocumentManager, server_folder::ServerFolderManager}; -use lib_ws::WSChannel; -use sqlx::PgPool; -use std::sync::Arc; - -#[derive(Clone)] -pub struct AppContext { - pub ws_server: Data>, - pub persistence: Data>, - pub ws_receivers: Data, - pub document_manager: Data>, - pub folder_manager: Data>, -} - -impl AppContext { - pub fn new(ws_server: Addr, pg_pool: PgPool) -> Self { - let ws_server = Data::new(ws_server); - let mut ws_receivers = WebSocketReceivers::new(); - - let document_store = make_document_kv_store(pg_pool.clone()); - let folder_store = make_folder_kv_store(pg_pool.clone()); - let flowy_persistence = Arc::new(FlowyPersistence { - pg_pool, - document_store, - folder_store, - }); - - let document_persistence = Arc::new(HttpDocumentCloudPersistence(flowy_persistence.document_kv_store())); - let document_manager = Arc::new(ServerDocumentManager::new(document_persistence)); - let document_ws_receiver = make_document_ws_receiver(flowy_persistence.clone(), document_manager.clone()); - ws_receivers.set(WSChannel::Document, document_ws_receiver); - - let folder_persistence = Arc::new(HttpFolderCloudPersistence(flowy_persistence.folder_kv_store())); - let folder_manager = Arc::new(ServerFolderManager::new(folder_persistence)); - let folder_ws_receiver = make_folder_ws_receiver(flowy_persistence.clone(), folder_manager.clone()); - ws_receivers.set(WSChannel::Folder, folder_ws_receiver); - - AppContext { - ws_server, - persistence: Data::new(flowy_persistence), - ws_receivers: Data::new(ws_receivers), - document_manager: Data::new(document_manager), - folder_manager: Data::new(folder_manager), - } - } -} - -pub type DocumentRevisionKV = RevisionKVPersistence; -pub type FolderRevisionKV = RevisionKVPersistence; - -fn make_document_kv_store(pg_pool: PgPool) -> Arc { - let kv_impl = Arc::new(PostgresKV { pg_pool }); - Arc::new(DocumentRevisionKV::new(kv_impl)) -} - -fn make_folder_kv_store(pg_pool: PgPool) -> Arc { - let kv_impl = Arc::new(PostgresKV { pg_pool }); - Arc::new(FolderRevisionKV::new(kv_impl)) -} - -#[derive(Clone)] -pub struct FlowyPersistence { - pg_pool: PgPool, - document_store: Arc, - folder_store: Arc, -} - -impl FlowyPersistence { - pub fn pg_pool(&self) -> PgPool { - self.pg_pool.clone() - } - - pub fn document_kv_store(&self) -> Arc { - self.document_store.clone() - } - - pub fn folder_kv_store(&self) -> Arc { - self.folder_store.clone() - } -} diff --git a/backend/src/entities/logged_user.rs b/backend/src/entities/logged_user.rs deleted file mode 100644 index 9930afa035..0000000000 --- a/backend/src/entities/logged_user.rs +++ /dev/null @@ -1,118 +0,0 @@ -use crate::entities::token::{Claim, Token}; -use actix_web::http::HeaderValue; -use backend_service::errors::ServerError; -use chrono::{DateTime, Utc}; -use dashmap::DashMap; -use lazy_static::lazy_static; - -lazy_static! { - pub static ref AUTHORIZED_USERS: AuthorizedUsers = AuthorizedUsers::new(); -} - -#[derive(Debug, PartialEq, Eq, Hash, Clone)] -pub struct LoggedUser { - pub user_id: String, -} - -impl std::convert::From for LoggedUser { - fn from(c: Claim) -> Self { - Self { user_id: c.user_id() } - } -} - -impl LoggedUser { - pub fn new(user_id: &str) -> Self { - Self { - user_id: user_id.to_owned(), - } - } - - pub fn from_token(token: String) -> Result { - let user: LoggedUser = Token::decode_token(&token.into())?.into(); - Ok(user) - } - - pub fn as_uuid(&self) -> Result { - let id = uuid::Uuid::parse_str(&self.user_id)?; - Ok(id) - } -} - -use actix_web::{dev::Payload, FromRequest, HttpRequest}; - -use futures::future::{ready, Ready}; - -impl FromRequest for LoggedUser { - type Error = ServerError; - type Future = Ready>; - - fn from_request(request: &HttpRequest, _payload: &mut Payload) -> Self::Future { - match Token::parser_from_request(request) { - Ok(token) => ready(LoggedUser::from_token(token.0)), - Err(err) => ready(Err(err)), - } - } -} - -impl std::convert::TryFrom<&HeaderValue> for LoggedUser { - type Error = ServerError; - - fn try_from(header: &HeaderValue) -> Result { - match header.to_str() { - Ok(val) => LoggedUser::from_token(val.to_owned()), - Err(e) => { - log::error!("Header to string failed: {:?}", e); - Err(ServerError::unauthorized()) - } - } - } -} - -#[derive(Clone, Debug, Copy)] -enum AuthStatus { - Authorized(DateTime), - NotAuthorized, -} - -pub const EXPIRED_DURATION_DAYS: i64 = 30; - -pub struct AuthorizedUsers(DashMap); -impl std::default::Default for AuthorizedUsers { - fn default() -> Self { - Self(DashMap::new()) - } -} -impl AuthorizedUsers { - pub fn new() -> Self { - AuthorizedUsers::default() - } - - pub fn is_authorized(&self, user: &LoggedUser) -> bool { - match self.0.get(user) { - None => { - tracing::debug!("user not login yet or server was reboot"); - false - } - Some(status) => match *status { - AuthStatus::Authorized(last_time) => { - let current_time = Utc::now(); - let days = (current_time - last_time).num_days(); - days < EXPIRED_DURATION_DAYS - } - AuthStatus::NotAuthorized => { - tracing::debug!("user logout already"); - false - } - }, - } - } - - pub fn store_auth(&self, user: LoggedUser, is_auth: bool) { - let status = if is_auth { - AuthStatus::Authorized(Utc::now()) - } else { - AuthStatus::NotAuthorized - }; - self.0.insert(user, status); - } -} diff --git a/backend/src/entities/mod.rs b/backend/src/entities/mod.rs deleted file mode 100644 index 70409638ca..0000000000 --- a/backend/src/entities/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod logged_user; -pub mod token; -pub mod user; diff --git a/backend/src/entities/token.rs b/backend/src/entities/token.rs deleted file mode 100644 index 2f5630699a..0000000000 --- a/backend/src/entities/token.rs +++ /dev/null @@ -1,94 +0,0 @@ -use crate::{ - config::env::{domain, jwt_secret}, - entities::logged_user::EXPIRED_DURATION_DAYS, -}; -use actix_web::{dev::Payload, FromRequest, HttpRequest}; -use backend_service::{configuration::HEADER_TOKEN, errors::ServerError}; -use chrono::{Duration, Local}; -use derive_more::{From, Into}; -use futures::future::{ready, Ready}; -use jsonwebtoken::{decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation}; -use serde::{Deserialize, Serialize}; - -const DEFAULT_ALGORITHM: Algorithm = Algorithm::HS256; - -#[derive(Debug, Serialize, Deserialize)] -pub struct Claim { - // issuer - iss: String, - // subject - sub: String, - // issue at - iat: i64, - // expiry - exp: i64, - user_id: String, -} - -impl Claim { - pub fn with_user_id(user_id: &str) -> Self { - let domain = domain(); - Self { - iss: domain, - sub: "auth".to_string(), - user_id: user_id.to_string(), - iat: Local::now().timestamp(), - exp: (Local::now() + Duration::days(EXPIRED_DURATION_DAYS)).timestamp(), - } - } - - pub fn user_id(self) -> String { - self.user_id - } -} - -// impl From for User { -// fn from(claim: Claim) -> Self { Self { email: claim.email } } -// } - -#[derive(From, Into, Clone)] -pub struct Token(pub String); -impl Token { - pub fn create_token(user_id: &str) -> Result { - let claims = Claim::with_user_id(user_id); - encode( - &Header::new(DEFAULT_ALGORITHM), - &claims, - &EncodingKey::from_secret(jwt_secret().as_ref()), - ) - .map(Into::into) - .map_err(|err| ServerError::internal().context(err)) - } - - pub fn decode_token(token: &Self) -> Result { - decode::( - &token.0, - &DecodingKey::from_secret(jwt_secret().as_ref()), - &Validation::new(DEFAULT_ALGORITHM), - ) - .map(|data| Ok(data.claims)) - .map_err(|err| ServerError::unauthorized().context(err))? - } - - pub fn parser_from_request(request: &HttpRequest) -> Result { - match request.headers().get(HEADER_TOKEN) { - Some(header) => match header.to_str() { - Ok(val) => Ok(Token(val.to_owned())), - Err(_) => Err(ServerError::unauthorized()), - }, - None => Err(ServerError::unauthorized()), - } - } -} - -impl FromRequest for Token { - type Error = ServerError; - type Future = Ready>; - - fn from_request(request: &HttpRequest, _payload: &mut Payload) -> Self::Future { - match Token::parser_from_request(request) { - Ok(token) => ready(Ok(token)), - Err(err) => ready(Err(err)), - } - } -} diff --git a/backend/src/entities/user.rs b/backend/src/entities/user.rs deleted file mode 100644 index 7a9b27b137..0000000000 --- a/backend/src/entities/user.rs +++ /dev/null @@ -1,13 +0,0 @@ -// type mapped https://kotiri.com/2018/01/31/postgresql-diesel-rust-types.html - -use chrono::Utc; - -#[derive(Debug, Clone, sqlx::FromRow)] -pub struct UserTable { - pub(crate) id: uuid::Uuid, - pub(crate) email: String, - pub(crate) name: String, - #[allow(dead_code)] - pub(crate) create_time: chrono::DateTime, - pub(crate) password: String, -} diff --git a/backend/src/lib.rs b/backend/src/lib.rs deleted file mode 100644 index 57cdcc8ce6..0000000000 --- a/backend/src/lib.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub mod application; -pub mod config; -pub mod context; -mod entities; -pub mod middleware; -pub mod services; -pub mod util; diff --git a/backend/src/main.rs b/backend/src/main.rs deleted file mode 100644 index 06cd255713..0000000000 --- a/backend/src/main.rs +++ /dev/null @@ -1,14 +0,0 @@ -use backend::{ - application::{init_app_context, Application}, - config::get_configuration, -}; - -#[actix_web::main] -async fn main() -> std::io::Result<()> { - let configuration = get_configuration().expect("Failed to read configuration."); - let app_ctx = init_app_context(&configuration).await; - let application = Application::build(configuration, app_ctx).await?; - application.run_until_stopped().await?; - - Ok(()) -} diff --git a/backend/src/middleware/auth_middleware.rs b/backend/src/middleware/auth_middleware.rs deleted file mode 100644 index bd4b5421e3..0000000000 --- a/backend/src/middleware/auth_middleware.rs +++ /dev/null @@ -1,105 +0,0 @@ -use actix_service::{Service, Transform}; -use actix_web::{ - dev::{ServiceRequest, ServiceResponse}, - Error, HttpResponse, ResponseError, -}; - -use crate::{ - config::IGNORE_ROUTES, - entities::logged_user::{LoggedUser, AUTHORIZED_USERS}, -}; -use actix_web::{body::AnyBody, dev::MessageBody}; -use backend_service::{configuration::HEADER_TOKEN, errors::ServerError}; -use futures::future::{ok, LocalBoxFuture, Ready}; -use std::{ - convert::TryInto, - error::Error as StdError, - task::{Context, Poll}, -}; - -pub struct AuthenticationService; - -impl Transform for AuthenticationService -where - S: Service, Error = Error> + 'static, - S::Future: 'static, - B: MessageBody + 'static, - B::Error: StdError, -{ - type Response = ServiceResponse; - type Error = Error; - type Transform = AuthenticationMiddleware; - type InitError = (); - type Future = Ready>; - - fn new_transform(&self, service: S) -> Self::Future { - ok(AuthenticationMiddleware { service }) - } -} -pub struct AuthenticationMiddleware { - service: S, -} - -impl Service for AuthenticationMiddleware -where - S: Service, Error = Error> + 'static, - S::Future: 'static, - B: MessageBody + 'static, - B::Error: StdError, -{ - type Response = ServiceResponse; - type Error = Error; - type Future = LocalBoxFuture<'static, Result>; - - fn poll_ready(&self, cx: &mut Context<'_>) -> Poll> { - self.service.poll_ready(cx) - } - - fn call(&self, req: ServiceRequest) -> Self::Future { - let mut authenticate_pass: bool = false; - for ignore_route in IGNORE_ROUTES.iter() { - // tracing::info!("ignore: {}, path: {}", ignore_route, req.path()); - if req.path().starts_with(ignore_route) { - authenticate_pass = true; - break; - } - } - - if !authenticate_pass { - if let Some(header) = req.headers().get(HEADER_TOKEN) { - let result: Result = header.try_into(); - match result { - Ok(logged_user) => { - if cfg!(feature = "ignore_auth") { - authenticate_pass = true; - AUTHORIZED_USERS.store_auth(logged_user, true); - } else { - authenticate_pass = AUTHORIZED_USERS.is_authorized(&logged_user); - if authenticate_pass { - AUTHORIZED_USERS.store_auth(logged_user, true); - } - } - } - Err(e) => log::error!("{:?}", e), - } - } else { - tracing::debug!("Can't find any token from request: {:?}", req); - } - } - - if authenticate_pass { - let fut = self.service.call(req); - Box::pin(async move { - let res = fut.await?; - Ok(res.map_body(|_, body| AnyBody::from_message(body))) - }) - } else { - Box::pin(async move { Ok(req.into_response(unauthorized_response())) }) - } - } -} - -fn unauthorized_response() -> HttpResponse { - let error = ServerError::unauthorized(); - error.error_response() -} diff --git a/backend/src/middleware/cors_middleware.rs b/backend/src/middleware/cors_middleware.rs deleted file mode 100644 index 516c16c9df..0000000000 --- a/backend/src/middleware/cors_middleware.rs +++ /dev/null @@ -1,16 +0,0 @@ -use actix_cors::Cors; -use actix_web::http; - -// https://javascript.info/fetch-crossorigin#cors-for-safe-requests -// https://docs.rs/actix-cors/0.5.4/actix_cors/index.html -// http://www.ruanyifeng.com/blog/2016/04/cors.html -// Cors short for Cross-Origin Resource Sharing. -pub fn default_cors() -> Cors { - Cors::default() // allowed_origin return access-control-allow-origin: * by default - // .allowed_origin("http://127.0.0.1:8080") - .send_wildcard() - .allowed_methods(vec!["GET", "POST", "PUT", "DELETE"]) - .allowed_headers(vec![http::header::ACCEPT]) - .allowed_header(http::header::CONTENT_TYPE) - .max_age(3600) -} diff --git a/backend/src/middleware/mod.rs b/backend/src/middleware/mod.rs deleted file mode 100644 index 28e0ccaac9..0000000000 --- a/backend/src/middleware/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod auth_middleware; -mod cors_middleware; - -pub use auth_middleware::*; -pub use cors_middleware::*; diff --git a/backend/src/services/document/mod.rs b/backend/src/services/document/mod.rs deleted file mode 100644 index b9d15fe4a6..0000000000 --- a/backend/src/services/document/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -#![allow(clippy::module_inception)] - -pub mod persistence; -pub(crate) mod router; -pub(crate) mod ws_actor; -pub(crate) mod ws_receiver; diff --git a/backend/src/services/document/persistence.rs b/backend/src/services/document/persistence.rs deleted file mode 100644 index 2fffcad697..0000000000 --- a/backend/src/services/document/persistence.rs +++ /dev/null @@ -1,63 +0,0 @@ -use anyhow::Context; -use backend_service::errors::{internal_error, ServerError}; - -use flowy_collaboration::{ - protobuf::{CreateDocParams, DocumentId, DocumentInfo, ResetDocumentParams}, - server_document::ServerDocumentManager, - util::make_document_info_pb_from_revisions_pb, -}; - -use crate::services::kv::revision_kv::RevisionKVPersistence; -use std::sync::Arc; -use uuid::Uuid; - -#[tracing::instrument(level = "trace", skip(document_store, params), err)] -pub(crate) async fn create_document( - document_store: &Arc, - mut params: CreateDocParams, -) -> Result<(), ServerError> { - let revisions = params.take_revisions().take_items(); - let _ = document_store.set_revision(revisions.into()).await?; - Ok(()) -} - -#[tracing::instrument(level = "trace", skip(document_store), err)] -pub async fn read_document( - document_store: &Arc, - params: DocumentId, -) -> Result { - let _ = Uuid::parse_str(¶ms.doc_id).context("Parse document id to uuid failed")?; - let revisions = document_store.get_revisions(¶ms.doc_id, None).await?; - match make_document_info_pb_from_revisions_pb(¶ms.doc_id, revisions) { - Ok(Some(document_info)) => Ok(document_info), - Ok(None) => Err(ServerError::record_not_found().context(format!("{} not exist", params.doc_id))), - Err(e) => Err(ServerError::internal().context(e)), - } -} - -#[tracing::instrument(level = "debug", skip(document_manager, params), err)] -pub async fn reset_document( - document_manager: &Arc, - mut params: ResetDocumentParams, -) -> Result<(), ServerError> { - let repeated_revision = params.take_revisions(); - if repeated_revision.get_items().is_empty() { - return Err(ServerError::payload_none().context("Revisions should not be empty when reset the document")); - } - let doc_id = params.doc_id.clone(); - let _ = document_manager - .handle_document_reset(&doc_id, repeated_revision) - .await - .map_err(internal_error)?; - Ok(()) -} - -#[tracing::instrument(level = "trace", skip(document_store), err)] -pub(crate) async fn delete_document( - document_store: &Arc, - doc_id: Uuid, -) -> Result<(), ServerError> { - // TODO: delete revisions may cause time issue. Maybe delete asynchronously? - let _ = document_store.delete_revisions(&doc_id.to_string(), None).await?; - Ok(()) -} diff --git a/backend/src/services/document/router.rs b/backend/src/services/document/router.rs deleted file mode 100644 index e83a391659..0000000000 --- a/backend/src/services/document/router.rs +++ /dev/null @@ -1,48 +0,0 @@ -use crate::{ - context::FlowyPersistence, - services::document::persistence::{create_document, read_document, reset_document}, - util::serde_ext::parse_from_payload, -}; -use actix_web::{ - web::{Data, Payload}, - HttpResponse, -}; -use backend_service::{errors::ServerError, response::FlowyResponse}; -use flowy_collaboration::{ - protobuf::{ - CreateDocParams as CreateDocParamsPB, DocumentId as DocumentIdPB, ResetDocumentParams as ResetDocumentParamsPB, - }, - server_document::ServerDocumentManager, -}; -use std::sync::Arc; - -pub async fn create_document_handler( - payload: Payload, - persistence: Data>, -) -> Result { - let params: CreateDocParamsPB = parse_from_payload(payload).await?; - let kv_store = persistence.document_kv_store(); - let _ = create_document(&kv_store, params).await?; - Ok(FlowyResponse::success().into()) -} - -#[tracing::instrument(level = "debug", skip(payload, persistence), err)] -pub async fn read_document_handler( - payload: Payload, - persistence: Data>, -) -> Result { - let params: DocumentIdPB = parse_from_payload(payload).await?; - let kv_store = persistence.document_kv_store(); - let doc = read_document(&kv_store, params).await?; - let response = FlowyResponse::success().pb(doc)?; - Ok(response.into()) -} - -pub async fn reset_document_handler( - payload: Payload, - document_manager: Data>, -) -> Result { - let params: ResetDocumentParamsPB = parse_from_payload(payload).await?; - let _ = reset_document(document_manager.get_ref(), params).await?; - Ok(FlowyResponse::success().into()) -} diff --git a/backend/src/services/document/ws_actor.rs b/backend/src/services/document/ws_actor.rs deleted file mode 100644 index d48055a4a3..0000000000 --- a/backend/src/services/document/ws_actor.rs +++ /dev/null @@ -1,159 +0,0 @@ -use crate::{ - context::FlowyPersistence, - services::web_socket::{entities::Socket, WSClientData, WSUser, WebSocketMessage}, - util::serde_ext::{md5, parse_from_bytes}, -}; -use actix_rt::task::spawn_blocking; -use async_stream::stream; -use backend_service::errors::{internal_error, Result, ServerError}; - -use crate::services::web_socket::revision_data_to_ws_message; -use flowy_collaboration::{ - protobuf::{ - ClientRevisionWSData as ClientRevisionWSDataPB, ClientRevisionWSDataType as ClientRevisionWSDataTypePB, - Revision as RevisionPB, - }, - server_document::ServerDocumentManager, - synchronizer::{RevisionSyncResponse, RevisionUser}, -}; -use futures::stream::StreamExt; -use lib_ws::WSChannel; -use std::sync::Arc; -use tokio::sync::{mpsc, oneshot}; - -pub enum DocumentWSActorMessage { - ClientData { - client_data: WSClientData, - persistence: Arc, - ret: oneshot::Sender>, - }, -} - -pub struct DocumentWebSocketActor { - actor_msg_receiver: Option>, - doc_manager: Arc, -} - -impl DocumentWebSocketActor { - pub fn new(receiver: mpsc::Receiver, manager: Arc) -> Self { - Self { - actor_msg_receiver: Some(receiver), - doc_manager: manager, - } - } - - pub async fn run(mut self) { - let mut actor_msg_receiver = self - .actor_msg_receiver - .take() - .expect("DocumentWebSocketActor's receiver should only take one time"); - - let stream = stream! { - loop { - match actor_msg_receiver.recv().await { - Some(msg) => yield msg, - None => break, - } - } - }; - - stream.for_each(|msg| self.handle_message(msg)).await; - } - - async fn handle_message(&self, msg: DocumentWSActorMessage) { - match msg { - DocumentWSActorMessage::ClientData { - client_data, - persistence: _, - ret, - } => { - let _ = ret.send(self.handle_document_data(client_data).await); - } - } - } - - async fn handle_document_data(&self, client_data: WSClientData) -> Result<()> { - let WSClientData { user, socket, data } = client_data; - let document_client_data = spawn_blocking(move || parse_from_bytes::(&data)) - .await - .map_err(internal_error)??; - - tracing::trace!( - "[DocumentWebSocketActor]: receive: {}:{}, {:?}", - document_client_data.object_id, - document_client_data.data_id, - document_client_data.ty - ); - - let user = Arc::new(DocumentRevisionUser { user, socket }); - match &document_client_data.ty { - ClientRevisionWSDataTypePB::ClientPushRev => { - let _ = self - .doc_manager - .handle_client_revisions(user, document_client_data) - .await - .map_err(internal_error)?; - } - ClientRevisionWSDataTypePB::ClientPing => { - let _ = self - .doc_manager - .handle_client_ping(user, document_client_data) - .await - .map_err(internal_error)?; - } - } - - Ok(()) - } -} - -#[allow(dead_code)] -fn verify_md5(revision: &RevisionPB) -> Result<()> { - if md5(&revision.delta_data) != revision.md5 { - return Err(ServerError::internal().context("RevisionPB md5 not match")); - } - Ok(()) -} - -#[derive(Clone)] -pub struct DocumentRevisionUser { - pub user: Arc, - pub(crate) socket: Socket, -} - -impl std::fmt::Debug for DocumentRevisionUser { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - f.debug_struct("DocumentRevisionUser") - .field("user", &self.user) - .field("socket", &self.socket) - .finish() - } -} - -impl RevisionUser for DocumentRevisionUser { - fn user_id(&self) -> String { - self.user.id().to_string() - } - - fn receive(&self, resp: RevisionSyncResponse) { - let result = match resp { - RevisionSyncResponse::Pull(data) => { - let msg: WebSocketMessage = revision_data_to_ws_message(data, WSChannel::Document); - self.socket.try_send(msg).map_err(internal_error) - } - RevisionSyncResponse::Push(data) => { - let msg: WebSocketMessage = revision_data_to_ws_message(data, WSChannel::Document); - self.socket.try_send(msg).map_err(internal_error) - } - RevisionSyncResponse::Ack(data) => { - let msg: WebSocketMessage = revision_data_to_ws_message(data, WSChannel::Document); - self.socket.try_send(msg).map_err(internal_error) - } - }; - - match result { - Ok(_) => {} - Err(e) => log::error!("[DocumentRevisionUser]: {}", e), - } - } -} diff --git a/backend/src/services/document/ws_receiver.rs b/backend/src/services/document/ws_receiver.rs deleted file mode 100644 index 4faa7c85ee..0000000000 --- a/backend/src/services/document/ws_receiver.rs +++ /dev/null @@ -1,176 +0,0 @@ -use crate::{ - context::{DocumentRevisionKV, FlowyPersistence}, - services::{ - document::{ - persistence::{create_document, read_document}, - ws_actor::{DocumentWSActorMessage, DocumentWebSocketActor}, - }, - kv::revision_kv::revisions_to_key_value_items, - web_socket::{WSClientData, WebSocketReceiver}, - }, -}; -use backend_service::errors::ServerError; -use flowy_collaboration::{ - entities::document_info::DocumentInfo, - errors::CollaborateError, - protobuf::{ - CreateDocParams as CreateDocParamsPB, DocumentId, RepeatedRevision as RepeatedRevisionPB, - Revision as RevisionPB, - }, - server_document::{DocumentCloudPersistence, ServerDocumentManager}, - util::make_document_info_from_revisions_pb, -}; -use lib_infra::future::BoxResultFuture; -use std::{ - convert::TryInto, - fmt::{Debug, Formatter}, - sync::Arc, -}; -use tokio::sync::{mpsc, oneshot}; - -pub fn make_document_ws_receiver( - persistence: Arc, - document_manager: Arc, -) -> Arc { - let (actor_msg_sender, rx) = tokio::sync::mpsc::channel(1000); - let actor = DocumentWebSocketActor::new(rx, document_manager); - tokio::task::spawn(actor.run()); - - Arc::new(DocumentWebSocketReceiver::new(persistence, actor_msg_sender)) -} - -pub struct DocumentWebSocketReceiver { - actor_msg_sender: mpsc::Sender, - persistence: Arc, -} - -impl DocumentWebSocketReceiver { - pub fn new(persistence: Arc, actor_msg_sender: mpsc::Sender) -> Self { - Self { - actor_msg_sender, - persistence, - } - } -} - -impl WebSocketReceiver for DocumentWebSocketReceiver { - fn receive(&self, data: WSClientData) { - let (ret, rx) = oneshot::channel(); - let actor_msg_sender = self.actor_msg_sender.clone(); - let persistence = self.persistence.clone(); - - actix_rt::spawn(async move { - let msg = DocumentWSActorMessage::ClientData { - client_data: data, - persistence, - ret, - }; - - match actor_msg_sender.send(msg).await { - Ok(_) => {} - Err(e) => tracing::error!("[DocumentWebSocketReceiver]: send message to actor failed: {}", e), - } - match rx.await { - Ok(_) => {} - Err(e) => tracing::error!("[DocumentWebSocketReceiver]: message ret failed {:?}", e), - }; - }); - } -} - -pub struct HttpDocumentCloudPersistence(pub Arc); -impl Debug for HttpDocumentCloudPersistence { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str("HttpDocumentCloudPersistence") - } -} - -impl DocumentCloudPersistence for HttpDocumentCloudPersistence { - fn read_document(&self, doc_id: &str) -> BoxResultFuture { - let params = DocumentId { - doc_id: doc_id.to_string(), - ..Default::default() - }; - let document_store = self.0.clone(); - Box::pin(async move { - let mut pb_doc = read_document(&document_store, params) - .await - .map_err(|e| e.to_collaborate_error())?; - let doc = (&mut pb_doc) - .try_into() - .map_err(|e| CollaborateError::internal().context(e))?; - Ok(doc) - }) - } - - fn create_document( - &self, - doc_id: &str, - repeated_revision: RepeatedRevisionPB, - ) -> BoxResultFuture, CollaborateError> { - let document_store = self.0.clone(); - let doc_id = doc_id.to_owned(); - Box::pin(async move { - let document_info = make_document_info_from_revisions_pb(&doc_id, repeated_revision.clone())?; - let doc_id = doc_id.to_owned(); - let mut params = CreateDocParamsPB::new(); - params.set_id(doc_id); - params.set_revisions(repeated_revision); - let _ = create_document(&document_store, params) - .await - .map_err(|e| e.to_collaborate_error())?; - Ok(document_info) - }) - } - - fn read_document_revisions( - &self, - doc_id: &str, - rev_ids: Option>, - ) -> BoxResultFuture, CollaborateError> { - let document_store = self.0.clone(); - let doc_id = doc_id.to_owned(); - let f = || async move { - let mut repeated_revision = document_store.get_revisions(&doc_id, rev_ids).await?; - Ok::, ServerError>(repeated_revision.take_items().into()) - }; - - Box::pin(async move { f().await.map_err(|e| e.to_collaborate_error()) }) - } - - fn save_document_revisions( - &self, - mut repeated_revision: RepeatedRevisionPB, - ) -> BoxResultFuture<(), CollaborateError> { - let document_store = self.0.clone(); - let f = || async move { - let revisions = repeated_revision.take_items().into(); - let _ = document_store.set_revision(revisions).await?; - Ok::<(), ServerError>(()) - }; - - Box::pin(async move { f().await.map_err(|e| e.to_collaborate_error()) }) - } - - fn reset_document( - &self, - doc_id: &str, - mut repeated_revision: RepeatedRevisionPB, - ) -> BoxResultFuture<(), CollaborateError> { - let document_store = self.0.clone(); - let doc_id = doc_id.to_owned(); - let f = || async move { - document_store - .transaction(|mut transaction| { - Box::pin(async move { - let _ = transaction.batch_delete_key_start_with(&doc_id).await?; - let items = revisions_to_key_value_items(repeated_revision.take_items().into())?; - let _ = transaction.batch_set(items).await?; - Ok(()) - }) - }) - .await - }; - Box::pin(async move { f().await.map_err(|e| e.to_collaborate_error()) }) - } -} diff --git a/backend/src/services/folder/app/controller.rs b/backend/src/services/folder/app/controller.rs deleted file mode 100644 index ba997580df..0000000000 --- a/backend/src/services/folder/app/controller.rs +++ /dev/null @@ -1,113 +0,0 @@ -use crate::services::folder::view::read_view_belong_to_id; - -use crate::{ - entities::logged_user::LoggedUser, - services::folder::{app::persistence::*, trash::read_trash_ids}, - util::sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder}, -}; -use backend_service::errors::{invalid_params, ServerError}; -use chrono::Utc; -use flowy_folder_data_model::{ - parser::{ - app::{AppDesc, AppName}, - workspace::WorkspaceIdentify, - }, - protobuf::{App as AppPB, CreateAppParams as CreateAppParamsPB, RepeatedView as RepeatedViewPB}, -}; -use sqlx::{postgres::PgArguments, Postgres}; -use uuid::Uuid; - -pub(crate) async fn create_app( - transaction: &mut DBTransaction<'_>, - mut params: CreateAppParamsPB, - logged_user: LoggedUser, -) -> Result { - let name = AppName::parse(params.take_name()).map_err(invalid_params)?; - let workspace_id = WorkspaceIdentify::parse(params.take_workspace_id()).map_err(invalid_params)?; - let user_id = logged_user.as_uuid()?.to_string(); - let desc = AppDesc::parse(params.take_desc()).map_err(invalid_params)?; - - let (sql, args, app) = NewAppSqlBuilder::new(&user_id, workspace_id.as_ref()) - .name(name.as_ref()) - .desc(desc.as_ref()) - .color_style(params.take_color_style()) - .build()?; - - let _ = sqlx::query_with(&sql, args) - .execute(transaction) - .await - .map_err(map_sqlx_error)?; - Ok(app) -} - -pub(crate) async fn read_app( - transaction: &mut DBTransaction<'_>, - app_id: Uuid, - user: &LoggedUser, -) -> Result { - let table = read_app_table(app_id, transaction).await?; - - let read_trash_ids = read_trash_ids(user, transaction).await?; - if read_trash_ids.contains(&table.id.to_string()) { - return Err(ServerError::record_not_found()); - } - - let mut views = RepeatedViewPB::default(); - views.set_items( - read_view_belong_to_id(&table.id.to_string(), user, transaction as &mut DBTransaction<'_>) - .await? - .into(), - ); - - let mut app: AppPB = table.into(); - app.set_belongings(views); - Ok(app) -} - -pub(crate) async fn read_app_table(app_id: Uuid, transaction: &mut DBTransaction<'_>) -> Result { - let (sql, args) = SqlBuilder::select(APP_TABLE) - .add_field("*") - .and_where_eq("id", app_id) - .build()?; - - let table = sqlx::query_as_with::(&sql, args) - .fetch_one(transaction as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - - Ok(table) -} - -pub(crate) async fn update_app( - transaction: &mut DBTransaction<'_>, - app_id: Uuid, - name: Option, - desc: Option, - color_style: Option>, -) -> Result<(), ServerError> { - let (sql, args) = SqlBuilder::update(APP_TABLE) - .add_some_arg("name", name) - .add_some_arg("color_style", color_style) - .add_some_arg("description", desc) - .add_some_arg("modified_time", Some(Utc::now())) - .and_where_eq("id", app_id) - .build()?; - - sqlx::query_with(&sql, args) - .execute(transaction) - .await - .map_err(map_sqlx_error)?; - - Ok(()) -} - -#[tracing::instrument(skip(transaction), err)] -pub(crate) async fn delete_app(transaction: &mut DBTransaction<'_>, app_id: Uuid) -> Result<(), ServerError> { - let (sql, args) = SqlBuilder::delete(APP_TABLE).and_where_eq("id", app_id).build()?; - let _ = sqlx::query_with(&sql, args) - .execute(transaction) - .await - .map_err(map_sqlx_error)?; - - Ok(()) -} diff --git a/backend/src/services/folder/app/mod.rs b/backend/src/services/folder/app/mod.rs deleted file mode 100644 index 6ff67bd1b4..0000000000 --- a/backend/src/services/folder/app/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -#![allow(clippy::module_inception)] -pub mod controller; -pub mod router; - -pub mod persistence; diff --git a/backend/src/services/folder/app/persistence.rs b/backend/src/services/folder/app/persistence.rs deleted file mode 100644 index 58832eccd0..0000000000 --- a/backend/src/services/folder/app/persistence.rs +++ /dev/null @@ -1,140 +0,0 @@ -use crate::util::sqlx_ext::SqlBuilder; -use backend_service::errors::{invalid_params, ServerError}; -use chrono::{DateTime, NaiveDateTime, Utc}; -use flowy_folder_data_model::{ - parser::app::AppIdentify, - protobuf::{App as AppPB, ColorStyle as ColorStylePB, RepeatedView as RepeatedViewPB}, -}; -use protobuf::Message; -use sqlx::postgres::PgArguments; -use uuid::Uuid; - -pub(crate) const APP_TABLE: &str = "app_table"; - -pub struct NewAppSqlBuilder { - table: AppTable, -} - -impl NewAppSqlBuilder { - pub fn new(user_id: &str, workspace_id: &str) -> Self { - let uuid = uuid::Uuid::new_v4(); - let time = Utc::now(); - - let table = AppTable { - id: uuid, - workspace_id: workspace_id.to_string(), - name: "".to_string(), - description: "".to_string(), - color_style: default_color_style(), - last_view_id: "".to_string(), - modified_time: time, - create_time: time, - user_id: user_id.to_string(), - }; - - Self { table } - } - - pub fn from_app(user_id: &str, app: AppPB) -> Result { - let app_id = check_app_id(app.id)?; - let create_time = DateTime::::from_utc(NaiveDateTime::from_timestamp(app.create_time, 0), Utc); - let modified_time = DateTime::::from_utc(NaiveDateTime::from_timestamp(app.modified_time, 0), Utc); - - let table = AppTable { - id: app_id, - workspace_id: app.workspace_id, - name: app.name, - description: app.desc, - color_style: default_color_style(), - last_view_id: "".to_string(), - modified_time, - create_time, - user_id: user_id.to_string(), - }; - - Ok(Self { table }) - } - - pub fn name(mut self, name: &str) -> Self { - self.table.name = name.to_string(); - self - } - - #[allow(dead_code)] - pub fn last_view_id(mut self, view_id: &str) -> Self { - self.table.last_view_id = view_id.to_string(); - self - } - - pub fn desc(mut self, desc: &str) -> Self { - self.table.description = desc.to_owned(); - self - } - - pub fn color_style(mut self, color_style: ColorStylePB) -> Self { - self.table.color_style = color_style.write_to_bytes().unwrap_or_else(|_| default_color_style()); - self - } - - pub fn build(self) -> Result<(String, PgArguments, AppPB), ServerError> { - let app: AppPB = self.table.clone().into(); - - let (sql, args) = SqlBuilder::create(APP_TABLE) - .add_field_with_arg("id", self.table.id) - .add_field_with_arg("workspace_id", self.table.workspace_id) - .add_field_with_arg("name", self.table.name) - .add_field_with_arg("description", self.table.description) - .add_field_with_arg("color_style", self.table.color_style) - .add_field_with_arg("modified_time", self.table.modified_time) - .add_field_with_arg("create_time", self.table.create_time) - .add_field_with_arg("user_id", self.table.user_id) - .build()?; - - Ok((sql, args, app)) - } -} - -fn default_color_style() -> Vec { - let style = ColorStylePB::default(); - match style.write_to_bytes() { - Ok(bytes) => bytes, - Err(e) => { - log::error!("Serialize color style failed: {:?}", e); - vec![] - } - } -} - -pub(crate) fn check_app_id(id: String) -> Result { - let app_id = AppIdentify::parse(id).map_err(invalid_params)?; - let app_id = Uuid::parse_str(app_id.as_ref())?; - Ok(app_id) -} - -#[derive(Debug, Clone, sqlx::FromRow)] -pub struct AppTable { - pub(crate) id: uuid::Uuid, - pub(crate) workspace_id: String, - pub(crate) name: String, - pub(crate) description: String, - pub(crate) color_style: Vec, - pub(crate) last_view_id: String, - pub(crate) modified_time: chrono::DateTime, - pub(crate) create_time: chrono::DateTime, - #[allow(dead_code)] - pub(crate) user_id: String, -} -impl std::convert::From for AppPB { - fn from(table: AppTable) -> Self { - let mut app = AppPB::default(); - app.set_id(table.id.to_string()); - app.set_workspace_id(table.workspace_id.to_string()); - app.set_name(table.name.clone()); - app.set_desc(table.description.clone()); - app.set_belongings(RepeatedViewPB::default()); - app.set_modified_time(table.modified_time.timestamp()); - app.set_create_time(table.create_time.timestamp()); - - app - } -} diff --git a/backend/src/services/folder/app/router.rs b/backend/src/services/folder/app/router.rs deleted file mode 100644 index 54aa42983c..0000000000 --- a/backend/src/services/folder/app/router.rs +++ /dev/null @@ -1,114 +0,0 @@ -use crate::{ - entities::logged_user::LoggedUser, - services::folder::app::{ - controller::{create_app, delete_app, read_app, update_app}, - persistence::check_app_id, - }, - util::serde_ext::parse_from_payload, -}; -use actix_web::{ - web::{Data, Payload}, - HttpResponse, -}; -use anyhow::Context; -use backend_service::{ - errors::{invalid_params, ServerError}, - response::FlowyResponse, -}; -use flowy_folder_data_model::{ - parser::app::{AppDesc, AppName}, - protobuf::{AppId as AppIdPB, CreateAppParams as CreateAppParamsPB, UpdateAppParams as UpdateAppParamsPB}, -}; -use protobuf::Message; -use sqlx::PgPool; - -pub async fn create_handler( - payload: Payload, - pool: Data, - logged_user: LoggedUser, -) -> Result { - let params: CreateAppParamsPB = parse_from_payload(payload).await?; - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to create app")?; - - let app = create_app(&mut transaction, params, logged_user).await?; - - transaction - .commit() - .await - .context("Failed to commit SQL transaction to create app.")?; - - Ok(FlowyResponse::success().pb(app)?.into()) -} - -pub async fn read_handler(payload: Payload, pool: Data, user: LoggedUser) -> Result { - let params: AppIdPB = parse_from_payload(payload).await?; - let app_id = check_app_id(params.app_id)?; - - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to read app")?; - let app = read_app(&mut transaction, app_id, &user).await?; - transaction - .commit() - .await - .context("Failed to commit SQL transaction to read app.")?; - - Ok(FlowyResponse::success().pb(app)?.into()) -} - -pub async fn update_handler(payload: Payload, pool: Data) -> Result { - let params: UpdateAppParamsPB = parse_from_payload(payload).await?; - let app_id = check_app_id(params.get_app_id().to_string())?; - let name = match params.has_name() { - false => None, - true => Some(AppName::parse(params.get_name().to_owned()).map_err(invalid_params)?.0), - }; - - let color_style = match params.has_color_style() { - false => None, - true => { - let color_bytes = params.get_color_style().write_to_bytes()?; - Some(color_bytes) - } - }; - - let desc = match params.has_desc() { - false => None, - true => Some(AppDesc::parse(params.get_desc().to_owned()).map_err(invalid_params)?.0), - }; - - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to update app")?; - - let _ = update_app(&mut transaction, app_id, name, desc, color_style).await?; - - transaction - .commit() - .await - .context("Failed to commit SQL transaction to update app.")?; - Ok(FlowyResponse::success().into()) -} - -pub async fn delete_handler(payload: Payload, pool: Data) -> Result { - let params: AppIdPB = parse_from_payload(payload).await?; - let app_id = check_app_id(params.app_id.to_owned())?; - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to delete app")?; - - let _ = delete_app(&mut transaction, app_id).await?; - - transaction - .commit() - .await - .context("Failed to commit SQL transaction to delete app.")?; - - Ok(FlowyResponse::success().into()) -} diff --git a/backend/src/services/folder/mod.rs b/backend/src/services/folder/mod.rs deleted file mode 100644 index 6168695a8f..0000000000 --- a/backend/src/services/folder/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod app; -pub mod trash; -pub mod view; -pub mod workspace; -pub(crate) mod ws_actor; -pub(crate) mod ws_receiver; diff --git a/backend/src/services/folder/trash/mod.rs b/backend/src/services/folder/trash/mod.rs deleted file mode 100644 index 90b217882c..0000000000 --- a/backend/src/services/folder/trash/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -#![allow(clippy::module_inception)] -mod persistence; -pub mod router; -mod trash; - -pub(crate) use trash::*; diff --git a/backend/src/services/folder/trash/persistence.rs b/backend/src/services/folder/trash/persistence.rs deleted file mode 100644 index 20faf2e417..0000000000 --- a/backend/src/services/folder/trash/persistence.rs +++ /dev/null @@ -1,40 +0,0 @@ -use crate::services::folder::{app::persistence::AppTable, view::persistence::ViewTable}; -use flowy_folder_data_model::protobuf::{Trash, TrashType}; - -pub(crate) const TRASH_TABLE: &str = "trash_table"; - -#[derive(Debug, Clone, sqlx::FromRow)] -pub struct TrashTable { - pub(crate) id: uuid::Uuid, - #[allow(dead_code)] - pub(crate) user_id: String, - pub(crate) ty: i32, -} - -impl std::convert::From for Trash { - fn from(table: AppTable) -> Self { - Trash { - id: table.id.to_string(), - name: table.name, - modified_time: table.modified_time.timestamp(), - create_time: table.create_time.timestamp(), - ty: TrashType::App, - unknown_fields: Default::default(), - cached_size: Default::default(), - } - } -} - -impl std::convert::From for Trash { - fn from(table: ViewTable) -> Self { - Trash { - id: table.id.to_string(), - name: table.name, - modified_time: table.modified_time.timestamp(), - create_time: table.create_time.timestamp(), - ty: TrashType::View, - unknown_fields: Default::default(), - cached_size: Default::default(), - } - } -} diff --git a/backend/src/services/folder/trash/router.rs b/backend/src/services/folder/trash/router.rs deleted file mode 100644 index d943c4d1a0..0000000000 --- a/backend/src/services/folder/trash/router.rs +++ /dev/null @@ -1,106 +0,0 @@ -use crate::{ - context::FlowyPersistence, - entities::logged_user::LoggedUser, - services::folder::trash::{create_trash, delete_all_trash, delete_trash, read_trash}, - util::serde_ext::parse_from_payload, -}; -use ::protobuf::ProtobufEnum; -use actix_web::{ - web::{Data, Payload}, - HttpResponse, -}; -use anyhow::Context; -use backend_service::{ - errors::{invalid_params, ServerError}, - response::FlowyResponse, -}; -use flowy_folder_data_model::{parser::trash::TrashIdentify, protobuf::RepeatedTrashId}; -use sqlx::PgPool; -use std::sync::Arc; -use uuid::Uuid; - -#[tracing::instrument(skip(payload, pool, logged_user), err)] -pub async fn create_handler( - payload: Payload, - pool: Data, - logged_user: LoggedUser, -) -> Result { - let params: RepeatedTrashId = parse_from_payload(payload).await?; - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to create trash")?; - - let _ = create_trash(&mut transaction, make_records(params)?, logged_user).await?; - - transaction - .commit() - .await - .context("Failed to commit SQL transaction to create trash.")?; - - Ok(FlowyResponse::success().into()) -} - -#[tracing::instrument(skip(payload, persistence, logged_user), fields(delete_trash), err)] -pub async fn delete_handler( - payload: Payload, - persistence: Data>, - logged_user: LoggedUser, -) -> Result { - let pool = persistence.pg_pool(); - let kv_store = persistence.document_kv_store(); - let params: RepeatedTrashId = parse_from_payload(payload).await?; - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to delete trash")?; - - if params.delete_all { - tracing::Span::current().record("delete_trash", &"all"); - let _ = delete_all_trash(&mut transaction, &kv_store, &logged_user).await?; - } else { - let records = make_records(params)?; - let _ = delete_trash(&mut transaction, &kv_store, records).await?; - } - - transaction - .commit() - .await - .context("Failed to commit SQL transaction to delete trash.")?; - - Ok(FlowyResponse::success().into()) -} - -pub async fn read_handler(pool: Data, logged_user: LoggedUser) -> Result { - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to read trash")?; - - let repeated_trash = read_trash(&mut transaction, &logged_user).await?; - - transaction - .commit() - .await - .context("Failed to commit SQL transaction to read view.")?; - - Ok(FlowyResponse::success().pb(repeated_trash)?.into()) -} - -fn check_trash_id(id: String) -> Result { - let trash_id = TrashIdentify::parse(id).map_err(invalid_params)?; - let trash_id = Uuid::parse_str(trash_id.as_ref())?; - Ok(trash_id) -} - -fn make_records(identifiers: RepeatedTrashId) -> Result, ServerError> { - let mut records = vec![]; - for identifier in identifiers.items { - // match TrashType::from_i32(identifier.ty.value()) { - // None => {} - // Some(ty) => {} - // } - records.push((check_trash_id(identifier.id.to_owned())?, identifier.ty.value())); - } - Ok(records) -} diff --git a/backend/src/services/folder/trash/trash.rs b/backend/src/services/folder/trash/trash.rs deleted file mode 100644 index 9df45779ba..0000000000 --- a/backend/src/services/folder/trash/trash.rs +++ /dev/null @@ -1,180 +0,0 @@ -use crate::{ - context::DocumentRevisionKV, - entities::logged_user::LoggedUser, - services::folder::{ - app::controller::{delete_app, read_app_table}, - trash::persistence::{TrashTable, TRASH_TABLE}, - view::{delete_view, read_view_table}, - }, - util::sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder}, -}; -use ::protobuf::ProtobufEnum; -use backend_service::errors::ServerError; -use flowy_folder_data_model::protobuf::{RepeatedTrash, Trash, TrashType}; -use sqlx::{postgres::PgArguments, Postgres, Row}; -use std::sync::Arc; -use uuid::Uuid; - -#[tracing::instrument(skip(transaction, user), err)] -pub(crate) async fn create_trash( - transaction: &mut DBTransaction<'_>, - records: Vec<(Uuid, i32)>, - user: LoggedUser, -) -> Result<(), ServerError> { - for (trash_id, ty) in records { - let (sql, args) = SqlBuilder::create(TRASH_TABLE) - .add_field_with_arg("id", trash_id) - .add_field_with_arg("user_id", &user.user_id) - .add_field_with_arg("ty", ty) - .build()?; - - let _ = sqlx::query_with(&sql, args) - .execute(transaction as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - } - - Ok(()) -} - -#[tracing::instrument(skip(transaction, document_store, user), fields(delete_rows), err)] -pub(crate) async fn delete_all_trash( - transaction: &mut DBTransaction<'_>, - document_store: &Arc, - user: &LoggedUser, -) -> Result<(), ServerError> { - let (sql, args) = SqlBuilder::select(TRASH_TABLE) - .and_where_eq("user_id", &user.user_id) - .build()?; - let rows = sqlx::query_with(&sql, args) - .fetch_all(transaction as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)? - .into_iter() - .map(|row| (row.get("id"), row.get("ty"))) - .collect::>(); - tracing::Span::current().record("delete_rows", &format!("{:?}", rows).as_str()); - let affected_row_count = rows.len(); - let _ = delete_trash_associate_targets(transaction as &mut DBTransaction<'_>, document_store, rows).await?; - - let (sql, args) = SqlBuilder::delete(TRASH_TABLE) - .and_where_eq("user_id", &user.user_id) - .build()?; - let result = sqlx::query_with(&sql, args) - .execute(transaction as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - tracing::Span::current().record("affected_row", &result.rows_affected()); - debug_assert_eq!(affected_row_count as u64, result.rows_affected()); - - Ok(()) -} - -#[tracing::instrument(skip(transaction, document_store), err)] -pub(crate) async fn delete_trash( - transaction: &mut DBTransaction<'_>, - document_store: &Arc, - records: Vec<(Uuid, i32)>, -) -> Result<(), ServerError> { - for (trash_id, _) in records { - // Read the trash_table and delete the original table according to the TrashType - let (sql, args) = SqlBuilder::select(TRASH_TABLE) - .add_field("*") - .and_where_eq("id", trash_id) - .build()?; - - let trash_table = sqlx::query_as_with::(&sql, args) - .fetch_one(transaction as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - - let _ = delete_trash_associate_targets( - transaction as &mut DBTransaction<'_>, - document_store, - vec![(trash_table.id, trash_table.ty)], - ) - .await?; - - // Delete the trash table - let (sql, args) = SqlBuilder::delete(TRASH_TABLE).and_where_eq("id", &trash_id).build()?; - let _ = sqlx::query_with(&sql, args) - .execute(transaction as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - } - Ok(()) -} - -#[tracing::instrument(skip(transaction, document_store, targets), err)] -async fn delete_trash_associate_targets( - transaction: &mut DBTransaction<'_>, - document_store: &Arc, - targets: Vec<(Uuid, i32)>, -) -> Result<(), ServerError> { - for (id, ty) in targets { - match TrashType::from_i32(ty) { - None => log::error!("Parser trash type with value: {} failed", ty), - Some(ty) => match ty { - TrashType::Unknown => {} - TrashType::View => { - let _ = delete_view(transaction as &mut DBTransaction<'_>, document_store, vec![id]).await; - } - TrashType::App => { - let _ = delete_app(transaction as &mut DBTransaction<'_>, id).await; - } - }, - } - } - - Ok(()) -} - -pub(crate) async fn read_trash_ids( - user: &LoggedUser, - transaction: &mut DBTransaction<'_>, -) -> Result, ServerError> { - let repeated_trash = read_trash(transaction, user).await?.take_items().into_vec(); - let ids = repeated_trash - .into_iter() - .map(|trash| trash.id) - .collect::>(); - - Ok(ids) -} - -#[tracing::instrument(skip(transaction, user), err)] -pub(crate) async fn read_trash( - transaction: &mut DBTransaction<'_>, - user: &LoggedUser, -) -> Result { - let (sql, args) = SqlBuilder::select(TRASH_TABLE) - .add_field("*") - .and_where_eq("user_id", &user.user_id) - .build()?; - - let tables = sqlx::query_as_with::(&sql, args) - .fetch_all(transaction as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - - let mut trash: Vec = vec![]; - for table in tables { - match TrashType::from_i32(table.ty) { - None => log::error!("Parser trash type with value: {} failed", table.ty), - Some(ty) => match ty { - TrashType::Unknown => {} - TrashType::View => { - trash.push(read_view_table(table.id, transaction).await?.into()); - } - TrashType::App => { - trash.push(read_app_table(table.id, transaction).await?.into()); - } - }, - } - } - - let mut repeated_trash = RepeatedTrash::default(); - repeated_trash.set_items(trash.into()); - - Ok(repeated_trash) -} diff --git a/backend/src/services/folder/view/controller.rs b/backend/src/services/folder/view/controller.rs deleted file mode 100644 index b9883d1286..0000000000 --- a/backend/src/services/folder/view/controller.rs +++ /dev/null @@ -1,170 +0,0 @@ -use crate::{ - entities::logged_user::LoggedUser, - services::{ - document::persistence::{create_document, delete_document}, - folder::{trash::read_trash_ids, view::persistence::*}, - }, - util::sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder}, -}; -use backend_service::errors::{invalid_params, ServerError}; - -use crate::context::DocumentRevisionKV; -use chrono::Utc; -use flowy_collaboration::{ - client_document::default::initial_delta, - entities::revision::{RepeatedRevision, Revision}, - protobuf::CreateDocParams as CreateDocParamsPB, -}; -use flowy_folder_data_model::{ - parser::{ - app::AppIdentify, - view::{ViewDesc, ViewName, ViewThumbnail}, - }, - protobuf::{CreateViewParams as CreateViewParamsPB, RepeatedView as RepeatedViewPB, View as ViewPB}, -}; -use sqlx::{postgres::PgArguments, Postgres}; -use std::{convert::TryInto, sync::Arc}; -use uuid::Uuid; - -pub(crate) async fn update_view( - transaction: &mut DBTransaction<'_>, - view_id: Uuid, - name: Option, - desc: Option, - thumbnail: Option, -) -> Result<(), ServerError> { - let (sql, args) = SqlBuilder::update(VIEW_TABLE) - .add_some_arg("name", name) - .add_some_arg("description", desc) - .add_some_arg("thumbnail", thumbnail) - .add_some_arg("modified_time", Some(Utc::now())) - .and_where_eq("id", view_id) - .build()?; - - sqlx::query_with(&sql, args) - .execute(transaction) - .await - .map_err(map_sqlx_error)?; - - Ok(()) -} - -#[tracing::instrument(skip(transaction, document_store), err)] -pub(crate) async fn delete_view( - transaction: &mut DBTransaction<'_>, - document_store: &Arc, - view_ids: Vec, -) -> Result<(), ServerError> { - for view_id in view_ids { - let (sql, args) = SqlBuilder::delete(VIEW_TABLE).and_where_eq("id", &view_id).build()?; - let _ = sqlx::query_with(&sql, args) - .execute(transaction as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - - let _ = delete_document(document_store, view_id).await?; - } - Ok(()) -} - -#[tracing::instrument(name = "create_view", level = "debug", skip(transaction, document_store), err)] -pub(crate) async fn create_view( - transaction: &mut DBTransaction<'_>, - document_store: Arc, - params: CreateViewParamsPB, - user_id: &str, -) -> Result { - let view_id = check_view_id(params.view_id.clone())?; - let name = ViewName::parse(params.name).map_err(invalid_params)?; - let belong_to_id = AppIdentify::parse(params.belong_to_id).map_err(invalid_params)?; - let thumbnail = ViewThumbnail::parse(params.thumbnail).map_err(invalid_params)?; - let desc = ViewDesc::parse(params.desc).map_err(invalid_params)?; - - let (sql, args, view) = NewViewSqlBuilder::new(view_id, belong_to_id.as_ref()) - .name(name.as_ref()) - .desc(desc.as_ref()) - .thumbnail(thumbnail.as_ref()) - .view_type(params.view_type) - .build()?; - - let _ = sqlx::query_with(&sql, args) - .execute(transaction as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - - let initial_delta_data = initial_delta().to_bytes(); - let md5 = format!("{:x}", md5::compute(&initial_delta_data)); - let revision = Revision::new(&view.id, 0, 0, initial_delta_data, user_id, md5); - let repeated_revision = RepeatedRevision::new(vec![revision]); - let mut create_doc_params = CreateDocParamsPB::new(); - create_doc_params.set_revisions(repeated_revision.try_into().unwrap()); - create_doc_params.set_id(view.id.clone()); - let _ = create_document(&document_store, create_doc_params).await?; - - Ok(view) -} - -pub(crate) async fn read_view( - user: &LoggedUser, - view_id: Uuid, - transaction: &mut DBTransaction<'_>, -) -> Result { - let table = read_view_table(view_id, transaction as &mut DBTransaction<'_>).await?; - - let read_trash_ids = read_trash_ids(user, transaction).await?; - if read_trash_ids.contains(&table.id.to_string()) { - return Err(ServerError::record_not_found()); - } - - let mut views = RepeatedViewPB::default(); - views.set_items( - read_view_belong_to_id(&table.id.to_string(), user, transaction) - .await? - .into(), - ); - let mut view: ViewPB = table.into(); - view.set_belongings(views); - Ok(view) -} - -pub(crate) async fn read_view_table( - view_id: Uuid, - transaction: &mut DBTransaction<'_>, -) -> Result { - let (sql, args) = SqlBuilder::select(VIEW_TABLE) - .add_field("*") - .and_where_eq("id", view_id) - .build()?; - - let table = sqlx::query_as_with::(&sql, args) - .fetch_one(transaction as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - - Ok(table) -} - -// transaction must be commit from caller -pub(crate) async fn read_view_belong_to_id<'c>( - id: &str, - user: &LoggedUser, - transaction: &mut DBTransaction<'_>, -) -> Result, ServerError> { - // TODO: add index for app_table - let (sql, args) = SqlBuilder::select(VIEW_TABLE) - .add_field("*") - .and_where_eq("belong_to_id", id) - .build()?; - - let mut tables = sqlx::query_as_with::(&sql, args) - .fetch_all(transaction as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - - let read_trash_ids = read_trash_ids(user, transaction).await?; - tables.retain(|table| !read_trash_ids.contains(&table.id.to_string())); - - let views = tables.into_iter().map(|table| table.into()).collect::>(); - - Ok(views) -} diff --git a/backend/src/services/folder/view/mod.rs b/backend/src/services/folder/view/mod.rs deleted file mode 100644 index 775b5d116f..0000000000 --- a/backend/src/services/folder/view/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -#![allow(clippy::module_inception)] -mod controller; -pub mod persistence; -pub mod router; - -pub(crate) use controller::*; diff --git a/backend/src/services/folder/view/persistence.rs b/backend/src/services/folder/view/persistence.rs deleted file mode 100644 index f43e1573bb..0000000000 --- a/backend/src/services/folder/view/persistence.rs +++ /dev/null @@ -1,134 +0,0 @@ -use crate::util::sqlx_ext::SqlBuilder; -use backend_service::errors::{invalid_params, ServerError}; -use chrono::{DateTime, NaiveDateTime, Utc}; -use flowy_folder_data_model::{ - parser::view::ViewIdentify, - protobuf::{RepeatedView as RepeatedViewPB, View as ViewPB, ViewType as ViewTypePB}, -}; -use protobuf::ProtobufEnum; -use sqlx::postgres::PgArguments; -use uuid::Uuid; - -pub(crate) const VIEW_TABLE: &str = "view_table"; - -pub struct NewViewSqlBuilder { - table: ViewTable, -} - -impl NewViewSqlBuilder { - pub fn new(view_id: Uuid, belong_to_id: &str) -> Self { - let time = Utc::now(); - - let table = ViewTable { - id: view_id, - belong_to_id: belong_to_id.to_string(), - name: "".to_string(), - description: "".to_string(), - modified_time: time, - create_time: time, - thumbnail: "".to_string(), - view_type: ViewTypePB::Doc.value(), - }; - - Self { table } - } - - pub fn from_view(view: ViewPB) -> Result { - let view_id = ViewIdentify::parse(view.id).map_err(invalid_params)?; - let view_id = Uuid::parse_str(view_id.as_ref())?; - let create_time = DateTime::::from_utc(NaiveDateTime::from_timestamp(view.create_time, 0), Utc); - let modified_time = DateTime::::from_utc(NaiveDateTime::from_timestamp(view.modified_time, 0), Utc); - - let table = ViewTable { - id: view_id, - belong_to_id: view.belong_to_id, - name: view.name, - description: view.desc, - modified_time, - create_time, - thumbnail: "".to_string(), - view_type: view.view_type.value(), - }; - Ok(Self { table }) - } - - pub fn name(mut self, name: &str) -> Self { - self.table.name = name.to_string(); - self - } - - pub fn desc(mut self, desc: &str) -> Self { - self.table.description = desc.to_owned(); - self - } - - pub fn thumbnail(mut self, thumbnail: &str) -> Self { - self.table.thumbnail = thumbnail.to_owned(); - self - } - - pub fn view_type(mut self, view_type: ViewTypePB) -> Self { - self.table.view_type = view_type.value(); - self - } - - pub fn build(self) -> Result<(String, PgArguments, ViewPB), ServerError> { - let view: ViewPB = self.table.clone().into(); - - let (sql, args) = SqlBuilder::create(VIEW_TABLE) - .add_field_with_arg("id", self.table.id) - .add_field_with_arg("belong_to_id", self.table.belong_to_id) - .add_field_with_arg("name", self.table.name) - .add_field_with_arg("description", self.table.description) - .add_field_with_arg("modified_time", self.table.modified_time) - .add_field_with_arg("create_time", self.table.create_time) - .add_field_with_arg("thumbnail", self.table.thumbnail) - .add_field_with_arg("view_type", self.table.view_type) - .build()?; - - Ok((sql, args, view)) - } -} - -pub(crate) fn check_view_ids(ids: Vec) -> Result, ServerError> { - let mut view_ids = vec![]; - for id in ids { - view_ids.push(check_view_id(id)?); - } - Ok(view_ids) -} - -pub(crate) fn check_view_id(id: String) -> Result { - let view_id = ViewIdentify::parse(id).map_err(invalid_params)?; - let view_id = Uuid::parse_str(view_id.as_ref())?; - Ok(view_id) -} - -#[derive(Debug, Clone, sqlx::FromRow)] -pub struct ViewTable { - pub(crate) id: uuid::Uuid, - pub(crate) belong_to_id: String, - pub(crate) name: String, - pub(crate) description: String, - pub(crate) modified_time: chrono::DateTime, - pub(crate) create_time: chrono::DateTime, - pub(crate) thumbnail: String, - pub(crate) view_type: i32, -} -impl std::convert::From for ViewPB { - fn from(table: ViewTable) -> Self { - let view_type = ViewTypePB::from_i32(table.view_type).unwrap_or(ViewTypePB::Doc); - - let mut view = ViewPB::default(); - view.set_id(table.id.to_string()); - view.set_belong_to_id(table.belong_to_id); - view.set_name(table.name); - view.set_desc(table.description); - view.set_view_type(view_type); - view.set_belongings(RepeatedViewPB::default()); - view.set_create_time(table.create_time.timestamp()); - view.set_modified_time(table.modified_time.timestamp()); - - view - } -} diff --git a/backend/src/services/folder/view/router.rs b/backend/src/services/folder/view/router.rs deleted file mode 100644 index ce66dda39d..0000000000 --- a/backend/src/services/folder/view/router.rs +++ /dev/null @@ -1,128 +0,0 @@ -use crate::{ - context::FlowyPersistence, - entities::logged_user::LoggedUser, - services::folder::view::{ - create_view, delete_view, - persistence::{check_view_id, check_view_ids}, - read_view, update_view, - }, - util::serde_ext::parse_from_payload, -}; -use actix_web::{ - web::{Data, Payload}, - HttpResponse, -}; -use anyhow::Context; -use backend_service::{ - errors::{invalid_params, ServerError}, - response::FlowyResponse, -}; -use flowy_folder_data_model::{ - parser::view::{ViewDesc, ViewName, ViewThumbnail}, - protobuf::{ - CreateViewParams as CreateViewParamsPB, QueryViewRequest as QueryViewRequestPB, - UpdateViewParams as UpdateViewParamsPB, ViewId as ViewIdPB, - }, -}; -use sqlx::PgPool; -use std::sync::Arc; - -pub async fn create_handler( - payload: Payload, - persistence: Data>, - user: LoggedUser, -) -> Result { - let params: CreateViewParamsPB = parse_from_payload(payload).await?; - let kv_store = persistence.document_kv_store(); - let pool = persistence.pg_pool(); - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to create view")?; - - let view = create_view(&mut transaction, kv_store, params, &user.user_id).await?; - transaction - .commit() - .await - .context("Failed to commit SQL transaction to create view.")?; - - let resp = FlowyResponse::success().pb(view)?; - Ok(resp.into()) -} - -pub async fn read_handler(payload: Payload, pool: Data, user: LoggedUser) -> Result { - let params: ViewIdPB = parse_from_payload(payload).await?; - let view_id = check_view_ids(vec![params.view_id])?.pop().unwrap(); - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to read view")?; - let view = read_view(&user, view_id, &mut transaction).await?; - - transaction - .commit() - .await - .context("Failed to commit SQL transaction to read view.")?; - - Ok(FlowyResponse::success().pb(view)?.into()) -} - -pub async fn update_handler(payload: Payload, pool: Data) -> Result { - let params: UpdateViewParamsPB = parse_from_payload(payload).await?; - let view_id = check_view_id(params.view_id.clone())?; - let name = match params.has_name() { - false => None, - true => Some(ViewName::parse(params.get_name().to_owned()).map_err(invalid_params)?.0), - }; - - let desc = match params.has_desc() { - false => None, - true => Some(ViewDesc::parse(params.get_desc().to_owned()).map_err(invalid_params)?.0), - }; - - let thumbnail = match params.has_thumbnail() { - false => None, - true => Some( - ViewThumbnail::parse(params.get_thumbnail().to_owned()) - .map_err(invalid_params)? - .0, - ), - }; - - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to update app")?; - - let _ = update_view(&mut transaction, view_id, name, desc, thumbnail).await?; - - transaction - .commit() - .await - .context("Failed to commit SQL transaction to update view.")?; - - Ok(FlowyResponse::success().into()) -} - -pub async fn delete_handler( - payload: Payload, - persistence: Data>, -) -> Result { - let params: QueryViewRequestPB = parse_from_payload(payload).await?; - let pool = persistence.pg_pool(); - let kv_store = persistence.document_kv_store(); - let view_ids = check_view_ids(params.view_ids.to_vec())?; - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to delete view")?; - - let _ = delete_view(&mut transaction, &kv_store, view_ids).await?; - - transaction - .commit() - .await - .context("Failed to commit SQL transaction to delete view.")?; - - Ok(FlowyResponse::success().into()) -} diff --git a/backend/src/services/folder/workspace/controller.rs b/backend/src/services/folder/workspace/controller.rs deleted file mode 100644 index 6a3a6d54fc..0000000000 --- a/backend/src/services/folder/workspace/controller.rs +++ /dev/null @@ -1,144 +0,0 @@ -use super::persistence::NewWorkspaceBuilder; -use crate::{ - entities::logged_user::LoggedUser, - services::folder::{ - app::{controller::read_app, persistence::AppTable}, - workspace::persistence::*, - }, - util::sqlx_ext::*, -}; -use anyhow::Context; -use backend_service::errors::{invalid_params, ServerError}; -use flowy_folder_data_model::{ - parser::workspace::WorkspaceIdentify, - protobuf::{RepeatedApp as RepeatedAppPB, RepeatedWorkspace as RepeatedWorkspacePB, Workspace as WorkspacePB}, -}; -use sqlx::{postgres::PgArguments, Postgres}; -use uuid::Uuid; - -pub(crate) async fn create_workspace( - transaction: &mut DBTransaction<'_>, - name: &str, - desc: &str, - logged_user: LoggedUser, -) -> Result { - let user_id = logged_user.as_uuid()?.to_string(); - let (sql, args, workspace) = NewWorkspaceBuilder::new(&user_id).name(name).desc(desc).build()?; - - let _ = sqlx::query_with(&sql, args) - .execute(transaction) - .await - .map_err(map_sqlx_error)?; - - Ok(workspace) -} - -pub(crate) async fn update_workspace( - transaction: &mut DBTransaction<'_>, - workspace_id: Uuid, - name: Option, - desc: Option, -) -> Result<(), ServerError> { - let (sql, args) = SqlBuilder::update(WORKSPACE_TABLE) - .add_some_arg("name", name) - .add_some_arg("description", desc) - .and_where_eq("id", workspace_id) - .build()?; - - sqlx::query_with(&sql, args) - .execute(transaction) - .await - .map_err(map_sqlx_error)?; - - Ok(()) -} - -pub(crate) async fn delete_workspace( - transaction: &mut DBTransaction<'_>, - workspace_id: Uuid, -) -> Result<(), ServerError> { - let (sql, args) = SqlBuilder::delete(WORKSPACE_TABLE) - .and_where_eq("id", workspace_id) - .build()?; - - let _ = sqlx::query_with(&sql, args) - .execute(transaction) - .await - .map_err(map_sqlx_error)?; - - Ok(()) -} - -#[tracing::instrument(skip(transaction, logged_user), err)] -pub async fn read_workspaces( - transaction: &mut DBTransaction<'_>, - workspace_id: Option, - logged_user: LoggedUser, -) -> Result { - let user_id = logged_user.as_uuid()?.to_string(); - - let mut builder = SqlBuilder::select(WORKSPACE_TABLE) - .add_field("*") - .and_where_eq("user_id", &user_id); - - if let Some(workspace_id) = workspace_id { - let workspace_id = check_workspace_id(workspace_id)?; - builder = builder.and_where_eq("id", workspace_id); - } - - let (sql, args) = builder.build()?; - let tables = sqlx::query_as_with::(&sql, args) - .fetch_all(transaction as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - - let mut repeated_workspace = RepeatedWorkspacePB::default(); - let mut workspaces = vec![]; - // Opti: combine the query - for table in tables { - let apps = read_workspace_apps( - &logged_user, - transaction as &mut DBTransaction<'_>, - &table.id.to_string(), - ) - .await - .context("Get workspace app") - .unwrap_or_default(); - - let mut workspace: WorkspacePB = table.into(); - workspace.set_apps(apps); - workspaces.push(workspace); - } - - repeated_workspace.set_items(workspaces.into()); - Ok(repeated_workspace) -} - -#[tracing::instrument(skip(transaction, user), fields(app_count), err)] -async fn read_workspace_apps<'c>( - user: &LoggedUser, - transaction: &mut DBTransaction<'_>, - workspace_id: &str, -) -> Result { - let workspace_id = WorkspaceIdentify::parse(workspace_id.to_owned()).map_err(invalid_params)?; - let (sql, args) = SqlBuilder::select("app_table") - .add_field("*") - .and_where_eq("workspace_id", workspace_id.0) - .build()?; - - let app_tables = sqlx::query_as_with::(&sql, args) - .fetch_all(transaction as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - - tracing::Span::current().record("app_count", &app_tables.len()); - let mut apps = vec![]; - for table in app_tables { - let app = read_app(transaction, table.id, user).await?; - apps.push(app); - } - - let mut repeated_app = RepeatedAppPB::default(); - repeated_app.set_items(apps.into()); - Ok(repeated_app) -} diff --git a/backend/src/services/folder/workspace/mod.rs b/backend/src/services/folder/workspace/mod.rs deleted file mode 100644 index dce53b50a4..0000000000 --- a/backend/src/services/folder/workspace/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -#![allow(clippy::module_inception)] -mod controller; -pub mod persistence; -pub mod router; - -pub use controller::*; diff --git a/backend/src/services/folder/workspace/persistence.rs b/backend/src/services/folder/workspace/persistence.rs deleted file mode 100644 index 48957c98af..0000000000 --- a/backend/src/services/folder/workspace/persistence.rs +++ /dev/null @@ -1,98 +0,0 @@ -use crate::util::sqlx_ext::SqlBuilder; -use backend_service::errors::{invalid_params, ServerError}; -use chrono::{DateTime, NaiveDateTime, Utc}; -use flowy_folder_data_model::{parser::workspace::WorkspaceIdentify, protobuf::Workspace as WorkspacePB}; -use sqlx::postgres::PgArguments; -use uuid::Uuid; - -pub struct NewWorkspaceBuilder { - table: WorkspaceTable, -} - -impl NewWorkspaceBuilder { - pub fn new(user_id: &str) -> Self { - let uuid = uuid::Uuid::new_v4(); - let time = Utc::now(); - - let table = WorkspaceTable { - id: uuid, - name: "".to_string(), - description: "".to_string(), - modified_time: time, - create_time: time, - user_id: user_id.to_string(), - }; - Self { table } - } - - pub fn from_workspace(user_id: &str, workspace: WorkspacePB) -> Result { - let workspace_id = check_workspace_id(workspace.id)?; - let create_time = DateTime::::from_utc(NaiveDateTime::from_timestamp(workspace.create_time, 0), Utc); - let modified_time = DateTime::::from_utc(NaiveDateTime::from_timestamp(workspace.modified_time, 0), Utc); - - let table = WorkspaceTable { - id: workspace_id, - name: workspace.name, - description: workspace.desc, - modified_time, - create_time, - user_id: user_id.to_string(), - }; - - Ok(Self { table }) - } - - pub fn name(mut self, name: &str) -> Self { - self.table.name = name.to_string(); - self - } - - pub fn desc(mut self, desc: &str) -> Self { - self.table.description = desc.to_owned(); - self - } - - pub fn build(self) -> Result<(String, PgArguments, WorkspacePB), ServerError> { - let workspace: WorkspacePB = self.table.clone().into(); - // TODO: use macro to fetch each field from struct - let (sql, args) = SqlBuilder::create(WORKSPACE_TABLE) - .add_field_with_arg("id", self.table.id) - .add_field_with_arg("name", self.table.name) - .add_field_with_arg("description", self.table.description) - .add_field_with_arg("modified_time", self.table.modified_time) - .add_field_with_arg("create_time", self.table.create_time) - .add_field_with_arg("user_id", self.table.user_id) - .build()?; - - Ok((sql, args, workspace)) - } -} - -pub(crate) fn check_workspace_id(id: String) -> Result { - let workspace_id = WorkspaceIdentify::parse(id).map_err(invalid_params)?; - let workspace_id = Uuid::parse_str(workspace_id.as_ref())?; - Ok(workspace_id) -} - -pub(crate) const WORKSPACE_TABLE: &str = "workspace_table"; - -#[derive(Debug, Clone, sqlx::FromRow)] -pub struct WorkspaceTable { - pub(crate) id: uuid::Uuid, - pub(crate) name: String, - pub(crate) description: String, - pub(crate) modified_time: chrono::DateTime, - pub(crate) create_time: chrono::DateTime, - pub(crate) user_id: String, -} -impl std::convert::From for WorkspacePB { - fn from(table: WorkspaceTable) -> Self { - let mut workspace = WorkspacePB::default(); - workspace.set_id(table.id.to_string()); - workspace.set_name(table.name.clone()); - workspace.set_desc(table.description.clone()); - workspace.set_modified_time(table.modified_time.timestamp()); - workspace.set_create_time(table.create_time.timestamp()); - workspace - } -} diff --git a/backend/src/services/folder/workspace/router.rs b/backend/src/services/folder/workspace/router.rs deleted file mode 100644 index 764a1ba78c..0000000000 --- a/backend/src/services/folder/workspace/router.rs +++ /dev/null @@ -1,149 +0,0 @@ -use crate::{ - entities::logged_user::LoggedUser, - services::folder::workspace::{ - create_workspace, delete_workspace, persistence::check_workspace_id, read_workspaces, update_workspace, - }, - util::serde_ext::parse_from_payload, -}; -use actix_web::{ - web::{Data, Payload}, - HttpResponse, -}; -use anyhow::Context; -use backend_service::{ - errors::{invalid_params, ServerError}, - response::FlowyResponse, -}; -use flowy_folder_data_model::{ - parser::workspace::{WorkspaceDesc, WorkspaceName}, - protobuf::{ - CreateWorkspaceParams as CreateWorkspaceParamsPB, UpdateWorkspaceParams as UpdateWorkspaceParamsPB, - WorkspaceId as WorkspaceIdPB, - }, -}; -use sqlx::PgPool; - -pub async fn create_handler( - payload: Payload, - pool: Data, - logged_user: LoggedUser, -) -> Result { - let params: CreateWorkspaceParamsPB = parse_from_payload(payload).await?; - let name = WorkspaceName::parse(params.get_name().to_owned()).map_err(invalid_params)?; - let desc = WorkspaceDesc::parse(params.get_desc().to_owned()).map_err(invalid_params)?; - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to create workspace")?; - let workspace = create_workspace(&mut transaction, name.as_ref(), desc.as_ref(), logged_user).await?; - transaction - .commit() - .await - .context("Failed to commit SQL transaction to create workspace.")?; - - Ok(FlowyResponse::success().pb(workspace)?.into()) -} - -pub async fn read_handler( - payload: Payload, - pool: Data, - logged_user: LoggedUser, -) -> Result { - let params: WorkspaceIdPB = parse_from_payload(payload).await?; - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to read workspace")?; - - let workspace_id = if params.has_workspace_id() { - Some(params.get_workspace_id().to_owned()) - } else { - None - }; - let repeated_workspace = read_workspaces(&mut transaction, workspace_id, logged_user).await?; - - transaction - .commit() - .await - .context("Failed to commit SQL transaction to read workspace.")?; - - Ok(FlowyResponse::success().pb(repeated_workspace)?.into()) -} - -pub async fn delete_handler( - payload: Payload, - pool: Data, - _logged_user: LoggedUser, -) -> Result { - let params: WorkspaceIdPB = parse_from_payload(payload).await?; - let workspace_id = check_workspace_id(params.get_workspace_id().to_owned())?; - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to delete workspace")?; - - let _ = delete_workspace(&mut transaction, workspace_id).await?; - transaction - .commit() - .await - .context("Failed to commit SQL transaction to delete workspace.")?; - - Ok(FlowyResponse::success().into()) -} - -pub async fn update_handler( - payload: Payload, - pool: Data, - _logged_user: LoggedUser, -) -> Result { - let params: UpdateWorkspaceParamsPB = parse_from_payload(payload).await?; - let workspace_id = check_workspace_id(params.get_id().to_owned())?; - let name = match params.has_name() { - false => None, - true => { - let name = WorkspaceName::parse(params.get_name().to_owned()) - .map_err(invalid_params)? - .0; - Some(name) - } - }; - - let desc = match params.has_desc() { - false => None, - true => { - let desc = WorkspaceDesc::parse(params.get_desc().to_owned()) - .map_err(invalid_params)? - .0; - Some(desc) - } - }; - - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to update workspace")?; - - let _ = update_workspace(&mut transaction, workspace_id, name, desc).await?; - - transaction - .commit() - .await - .context("Failed to commit SQL transaction to update workspace.")?; - - Ok(FlowyResponse::success().into()) -} - -pub async fn workspace_list(pool: Data, logged_user: LoggedUser) -> Result { - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to read workspaces")?; - - let repeated_workspace = read_workspaces(&mut transaction, None, logged_user).await?; - transaction - .commit() - .await - .context("Failed to commit SQL transaction to read workspace.")?; - - Ok(FlowyResponse::success().pb(repeated_workspace)?.into()) -} diff --git a/backend/src/services/folder/ws_actor.rs b/backend/src/services/folder/ws_actor.rs deleted file mode 100644 index 3e655243c0..0000000000 --- a/backend/src/services/folder/ws_actor.rs +++ /dev/null @@ -1,148 +0,0 @@ -use crate::{ - context::FlowyPersistence, - services::web_socket::{entities::Socket, revision_data_to_ws_message, WSClientData, WSUser, WebSocketMessage}, - util::serde_ext::parse_from_bytes, -}; -use actix_rt::task::spawn_blocking; -use async_stream::stream; -use backend_service::errors::{internal_error, Result}; - -use flowy_collaboration::{ - protobuf::{ - ClientRevisionWSData as ClientRevisionWSDataPB, ClientRevisionWSDataType as ClientRevisionWSDataTypePB, - }, - server_folder::ServerFolderManager, - synchronizer::{RevisionSyncResponse, RevisionUser}, -}; -use futures::stream::StreamExt; -use lib_ws::WSChannel; -use std::sync::Arc; -use tokio::sync::{mpsc, oneshot}; - -pub enum FolderWSActorMessage { - ClientData { - client_data: WSClientData, - persistence: Arc, - ret: oneshot::Sender>, - }, -} - -pub struct FolderWebSocketActor { - actor_msg_receiver: Option>, - folder_manager: Arc, -} - -impl FolderWebSocketActor { - pub fn new(receiver: mpsc::Receiver, folder_manager: Arc) -> Self { - Self { - actor_msg_receiver: Some(receiver), - folder_manager, - } - } - - pub async fn run(mut self) { - let mut actor_msg_receiver = self - .actor_msg_receiver - .take() - .expect("FolderWebSocketActor's receiver should only take one time"); - let stream = stream! { - loop { - match actor_msg_receiver.recv().await { - Some(msg) => yield msg, - None => { - break - }, - } - } - }; - stream.for_each(|msg| self.handle_message(msg)).await; - } - - async fn handle_message(&self, msg: FolderWSActorMessage) { - match msg { - FolderWSActorMessage::ClientData { - client_data, - persistence: _, - ret, - } => { - let _ = ret.send(self.handle_folder_data(client_data).await); - } - } - } - - async fn handle_folder_data(&self, client_data: WSClientData) -> Result<()> { - let WSClientData { user, socket, data } = client_data; - let folder_client_data = spawn_blocking(move || parse_from_bytes::(&data)) - .await - .map_err(internal_error)??; - - tracing::debug!( - "[FolderWebSocketActor]: receive: {}:{}, {:?}", - folder_client_data.object_id, - folder_client_data.data_id, - folder_client_data.ty - ); - - let user = Arc::new(FolderRevisionUser { user, socket }); - match &folder_client_data.ty { - ClientRevisionWSDataTypePB::ClientPushRev => { - let _ = self - .folder_manager - .handle_client_revisions(user, folder_client_data) - .await - .map_err(internal_error)?; - } - ClientRevisionWSDataTypePB::ClientPing => { - let _ = self - .folder_manager - .handle_client_ping(user, folder_client_data) - .await - .map_err(internal_error)?; - } - } - Ok(()) - } -} - -#[derive(Clone)] -pub struct FolderRevisionUser { - pub user: Arc, - pub(crate) socket: Socket, -} - -impl std::fmt::Debug for FolderRevisionUser { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - f.debug_struct("FolderRevisionUser") - .field("user", &self.user) - .field("socket", &self.socket) - .finish() - } -} - -impl RevisionUser for FolderRevisionUser { - fn user_id(&self) -> String { - self.user.id().to_string() - } - - fn receive(&self, resp: RevisionSyncResponse) { - let result = match resp { - RevisionSyncResponse::Pull(data) => { - let msg: WebSocketMessage = revision_data_to_ws_message(data, WSChannel::Folder); - self.socket.try_send(msg).map_err(internal_error) - } - RevisionSyncResponse::Push(data) => { - let msg: WebSocketMessage = revision_data_to_ws_message(data, WSChannel::Folder); - self.socket.try_send(msg).map_err(internal_error) - } - RevisionSyncResponse::Ack(data) => { - let msg: WebSocketMessage = revision_data_to_ws_message(data, WSChannel::Folder); - self.socket.try_send(msg).map_err(internal_error) - } - }; - - match result { - Ok(_) => {} - Err(e) => log::error!("[FolderRevisionUser]: {}", e), - } - } -} diff --git a/backend/src/services/folder/ws_receiver.rs b/backend/src/services/folder/ws_receiver.rs deleted file mode 100644 index 4d77ea7ce7..0000000000 --- a/backend/src/services/folder/ws_receiver.rs +++ /dev/null @@ -1,169 +0,0 @@ -use crate::{ - context::FlowyPersistence, - services::{ - folder::ws_actor::{FolderWSActorMessage, FolderWebSocketActor}, - web_socket::{WSClientData, WebSocketReceiver}, - }, -}; -use std::fmt::{Debug, Formatter}; - -use crate::{context::FolderRevisionKV, services::kv::revision_kv::revisions_to_key_value_items}; -use flowy_collaboration::{ - entities::folder_info::FolderInfo, - errors::CollaborateError, - protobuf::{RepeatedRevision as RepeatedRevisionPB, Revision as RevisionPB}, - server_folder::{FolderCloudPersistence, ServerFolderManager}, - util::make_folder_from_revisions_pb, -}; -use lib_infra::future::BoxResultFuture; -use std::sync::Arc; -use tokio::sync::{mpsc, oneshot}; - -pub fn make_folder_ws_receiver( - persistence: Arc, - folder_manager: Arc, -) -> Arc { - let (actor_msg_sender, rx) = tokio::sync::mpsc::channel(1000); - let actor = FolderWebSocketActor::new(rx, folder_manager); - tokio::task::spawn(actor.run()); - Arc::new(FolderWebSocketReceiver::new(persistence, actor_msg_sender)) -} - -pub struct FolderWebSocketReceiver { - actor_msg_sender: mpsc::Sender, - persistence: Arc, -} - -impl FolderWebSocketReceiver { - pub fn new(persistence: Arc, actor_msg_sender: mpsc::Sender) -> Self { - Self { - actor_msg_sender, - persistence, - } - } -} - -impl WebSocketReceiver for FolderWebSocketReceiver { - fn receive(&self, data: WSClientData) { - let (ret, rx) = oneshot::channel(); - let actor_msg_sender = self.actor_msg_sender.clone(); - let persistence = self.persistence.clone(); - - actix_rt::spawn(async move { - let msg = FolderWSActorMessage::ClientData { - client_data: data, - persistence, - ret, - }; - - match actor_msg_sender.send(msg).await { - Ok(_) => {} - Err(e) => { - log::error!("[FolderWebSocketReceiver]: send message to actor failed: {}", e); - } - } - match rx.await { - Ok(_) => {} - Err(e) => log::error!("[FolderWebSocketReceiver]: message ret failed {:?}", e), - }; - }); - } -} - -pub struct HttpFolderCloudPersistence(pub Arc); -impl Debug for HttpFolderCloudPersistence { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str("HttpFolderCloudPersistence") - } -} - -impl FolderCloudPersistence for HttpFolderCloudPersistence { - fn read_folder(&self, _user_id: &str, folder_id: &str) -> BoxResultFuture { - let folder_store = self.0.clone(); - let folder_id = folder_id.to_owned(); - Box::pin(async move { - let revisions = folder_store - .get_revisions(&folder_id, None) - .await - .map_err(|e| e.to_collaborate_error())?; - match make_folder_from_revisions_pb(&folder_id, revisions)? { - Some(folder_info) => Ok(folder_info), - None => Err(CollaborateError::record_not_found().context(format!("{} not exist", folder_id))), - } - }) - } - - fn create_folder( - &self, - _user_id: &str, - folder_id: &str, - mut repeated_revision: RepeatedRevisionPB, - ) -> BoxResultFuture, CollaborateError> { - let folder_store = self.0.clone(); - let folder_id = folder_id.to_owned(); - Box::pin(async move { - let folder_info = make_folder_from_revisions_pb(&folder_id, repeated_revision.clone())?; - let revisions: Vec = repeated_revision.take_items().into(); - let _ = folder_store - .set_revision(revisions) - .await - .map_err(|e| e.to_collaborate_error())?; - Ok(folder_info) - }) - } - - fn save_folder_revisions( - &self, - mut repeated_revision: RepeatedRevisionPB, - ) -> BoxResultFuture<(), CollaborateError> { - let folder_store = self.0.clone(); - Box::pin(async move { - let revisions: Vec = repeated_revision.take_items().into(); - let _ = folder_store - .set_revision(revisions) - .await - .map_err(|e| e.to_collaborate_error())?; - Ok(()) - }) - } - - fn read_folder_revisions( - &self, - folder_id: &str, - rev_ids: Option>, - ) -> BoxResultFuture, CollaborateError> { - let folder_store = self.0.clone(); - let folder_id = folder_id.to_owned(); - Box::pin(async move { - let mut repeated_revision = folder_store - .get_revisions(&folder_id, rev_ids) - .await - .map_err(|e| e.to_collaborate_error())?; - let revisions: Vec = repeated_revision.take_items().into(); - Ok(revisions) - }) - } - - fn reset_folder( - &self, - folder_id: &str, - mut repeated_revision: RepeatedRevisionPB, - ) -> BoxResultFuture<(), CollaborateError> { - let folder_store = self.0.clone(); - let folder_id = folder_id.to_owned(); - Box::pin(async move { - let _ = folder_store - .transaction(|mut transaction| { - Box::pin(async move { - let _ = transaction.batch_delete_key_start_with(&folder_id).await?; - let items = revisions_to_key_value_items(repeated_revision.take_items().into())?; - let _ = transaction.batch_set(items).await?; - Ok(()) - }) - }) - .await - .map_err(|e| e.to_collaborate_error())?; - Ok(()) - }) - } -} diff --git a/backend/src/services/kv/kv.rs b/backend/src/services/kv/kv.rs deleted file mode 100644 index 30189edf33..0000000000 --- a/backend/src/services/kv/kv.rs +++ /dev/null @@ -1,210 +0,0 @@ -use crate::{ - services::kv::{KVTransaction, KeyValue}, - util::sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder}, -}; -use anyhow::Context; -use async_trait::async_trait; -use backend_service::errors::ServerError; -use bytes::Bytes; - -use lib_infra::future::BoxResultFuture; -use sql_builder::SqlBuilder as RawSqlBuilder; -use sqlx::{ - postgres::{PgArguments, PgRow}, - Arguments, Error, PgPool, Postgres, Row, -}; - -const KV_TABLE: &str = "kv_table"; - -pub struct PostgresKV { - pub(crate) pg_pool: PgPool, -} - -impl PostgresKV { - pub async fn get(&self, key: &str) -> Result, ServerError> { - let key = key.to_owned(); - self.transaction(|mut transaction| Box::pin(async move { transaction.get(&key).await })) - .await - } - pub async fn set(&self, key: &str, value: Bytes) -> Result<(), ServerError> { - let key = key.to_owned(); - self.transaction(|mut transaction| Box::pin(async move { transaction.set(&key, value).await })) - .await - } - - pub async fn remove(&self, key: &str) -> Result<(), ServerError> { - let key = key.to_owned(); - self.transaction(|mut transaction| Box::pin(async move { transaction.remove(&key).await })) - .await - } - - pub async fn batch_set(&self, kvs: Vec) -> Result<(), ServerError> { - self.transaction(|mut transaction| Box::pin(async move { transaction.batch_set(kvs).await })) - .await - } - - pub async fn batch_get(&self, keys: Vec) -> Result, ServerError> { - self.transaction(|mut transaction| Box::pin(async move { transaction.batch_get(keys).await })) - .await - } - - pub async fn transaction(&self, f: F) -> Result - where - F: for<'a> FnOnce(Box) -> BoxResultFuture, - { - let mut transaction = self - .pg_pool - .begin() - .await - .context("[KV]:Failed to acquire a Postgres connection")?; - let postgres_transaction = PostgresTransaction(&mut transaction); - let result = f(Box::new(postgres_transaction)).await; - transaction - .commit() - .await - .context("[KV]:Failed to commit SQL transaction.")?; - - result - } -} - -pub(crate) struct PostgresTransaction<'a, 'b>(&'a mut DBTransaction<'b>); - -#[async_trait] -impl<'a, 'b> KVTransaction for PostgresTransaction<'a, 'b> { - async fn get(&mut self, key: &str) -> Result, ServerError> { - let id = key.to_string(); - let (sql, args) = SqlBuilder::select(KV_TABLE) - .add_field("*") - .and_where_eq("id", &id) - .build()?; - - let result = sqlx::query_as_with::(&sql, args) - .fetch_one(self.0 as &mut DBTransaction<'b>) - .await; - - match result { - Ok(val) => Ok(Some(Bytes::from(val.blob))), - Err(error) => match error { - Error::RowNotFound => Ok(None), - _ => Err(map_sqlx_error(error)), - }, - } - } - - async fn set(&mut self, key: &str, bytes: Bytes) -> Result<(), ServerError> { - self.batch_set(vec![KeyValue { - key: key.to_string(), - value: bytes, - }]) - .await - } - - async fn remove(&mut self, key: &str) -> Result<(), ServerError> { - let id = key.to_string(); - let (sql, args) = SqlBuilder::delete(KV_TABLE).and_where_eq("id", &id).build()?; - let _ = sqlx::query_with(&sql, args) - .execute(self.0 as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - Ok(()) - } - - async fn batch_set(&mut self, kvs: Vec) -> Result<(), ServerError> { - let mut builder = RawSqlBuilder::insert_into(KV_TABLE); - let m_builder = builder.field("id").field("blob"); - - let mut args = PgArguments::default(); - kvs.iter().enumerate().for_each(|(index, _)| { - let index = index * 2 + 1; - m_builder.values(&[format!("${}", index), format!("${}", index + 1)]); - }); - - for kv in kvs { - args.add(kv.key); - args.add(kv.value.to_vec()); - } - - let sql = m_builder.sql()?; - let _ = sqlx::query_with(&sql, args) - .execute(self.0 as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - - Ok::<(), ServerError>(()) - } - - async fn batch_get(&mut self, keys: Vec) -> Result, ServerError> { - let sql = RawSqlBuilder::select_from(KV_TABLE) - .field("id") - .field("blob") - .and_where_in_quoted("id", &keys) - .sql()?; - - let rows = sqlx::query(&sql) - .fetch_all(self.0 as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - let kvs = rows_to_key_values(rows); - Ok::, ServerError>(kvs) - } - - async fn batch_delete(&mut self, keys: Vec) -> Result<(), ServerError> { - let sql = RawSqlBuilder::delete_from(KV_TABLE).and_where_in("id", &keys).sql()?; - let _ = sqlx::query(&sql) - .execute(self.0 as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - - Ok::<(), ServerError>(()) - } - - async fn batch_get_start_with(&mut self, key: &str) -> Result, ServerError> { - let prefix = key.to_owned(); - let sql = RawSqlBuilder::select_from(KV_TABLE) - .field("id") - .field("blob") - .and_where_like_left("id", &prefix) - .sql()?; - - let rows = sqlx::query(&sql) - .fetch_all(self.0 as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - - let kvs = rows_to_key_values(rows); - - Ok::, ServerError>(kvs) - } - - async fn batch_delete_key_start_with(&mut self, keyword: &str) -> Result<(), ServerError> { - let keyword = keyword.to_owned(); - let sql = RawSqlBuilder::delete_from(KV_TABLE) - .and_where_like_left("id", &keyword) - .sql()?; - - let _ = sqlx::query(&sql) - .execute(self.0 as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - Ok::<(), ServerError>(()) - } -} -fn rows_to_key_values(rows: Vec) -> Vec { - rows.into_iter() - .map(|row| { - let bytes: Vec = row.get("blob"); - KeyValue { - key: row.get("id"), - value: Bytes::from(bytes), - } - }) - .collect::>() -} - -#[derive(Debug, Clone, sqlx::FromRow)] -struct KVTable { - #[allow(dead_code)] - pub(crate) id: String, - pub(crate) blob: Vec, -} diff --git a/backend/src/services/kv/mod.rs b/backend/src/services/kv/mod.rs deleted file mode 100644 index 4a14383b61..0000000000 --- a/backend/src/services/kv/mod.rs +++ /dev/null @@ -1,41 +0,0 @@ -#![allow(clippy::module_inception)] -mod kv; -pub mod revision_kv; - -use async_trait::async_trait; -use bytes::Bytes; - -pub(crate) use kv::*; - -use backend_service::errors::ServerError; - -// TODO: Generic the KVStore that enable switching KVStore to another -// implementation -pub type KVStore = PostgresKV; - -#[rustfmt::skip] -// https://rust-lang.github.io/async-book/07_workarounds/05_async_in_traits.html -// Note that using these trait methods will result in a heap allocation -// per-function-call. This is not a significant cost for the vast majority of -// applications, but should be considered when deciding whether to use this -// functionality in the public API of a low-level function that is expected to -// be called millions of times a second. -#[async_trait] -pub trait KVTransaction: Send + Sync { - async fn get(&mut self, key: &str) -> Result, ServerError>; - async fn set(&mut self, key: &str, value: Bytes) -> Result<(), ServerError>; - async fn remove(&mut self, key: &str) -> Result<(), ServerError>; - - async fn batch_set(&mut self, kvs: Vec) -> Result<(), ServerError>; - async fn batch_get(&mut self, keys: Vec) -> Result, ServerError>; - async fn batch_delete(&mut self, keys: Vec) -> Result<(), ServerError>; - - async fn batch_get_start_with(&mut self, key: &str) -> Result, ServerError>; - async fn batch_delete_key_start_with(&mut self, keyword: &str) -> Result<(), ServerError>; -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct KeyValue { - pub key: String, - pub value: Bytes, -} diff --git a/backend/src/services/kv/revision_kv.rs b/backend/src/services/kv/revision_kv.rs deleted file mode 100644 index c263d84e97..0000000000 --- a/backend/src/services/kv/revision_kv.rs +++ /dev/null @@ -1,128 +0,0 @@ -use crate::{ - services::kv::{KVStore, KeyValue}, - util::serde_ext::parse_from_bytes, -}; -use backend_service::errors::ServerError; -use bytes::Bytes; -use flowy_collaboration::protobuf::{RepeatedRevision as RepeatedRevisionPB, Revision as RevisionPB}; - -use protobuf::Message; -use std::sync::Arc; - -pub struct RevisionKVPersistence { - inner: Arc, -} - -impl std::ops::Deref for RevisionKVPersistence { - type Target = Arc; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -impl std::ops::DerefMut for RevisionKVPersistence { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.inner - } -} - -impl RevisionKVPersistence { - pub(crate) fn new(kv_store: Arc) -> Self { - RevisionKVPersistence { inner: kv_store } - } - - pub(crate) async fn set_revision(&self, revisions: Vec) -> Result<(), ServerError> { - let items = revisions_to_key_value_items(revisions)?; - self.inner - .transaction(|mut t| Box::pin(async move { t.batch_set(items).await })) - .await - } - - pub(crate) async fn get_revisions>>>( - &self, - object_id: &str, - rev_ids: T, - ) -> Result { - let rev_ids = rev_ids.into(); - let items = match rev_ids { - None => { - let object_id = object_id.to_owned(); - self.inner - .transaction(|mut t| Box::pin(async move { t.batch_get_start_with(&object_id).await })) - .await? - } - Some(rev_ids) => { - let keys = rev_ids - .into_iter() - .map(|rev_id| make_revision_key(object_id, rev_id)) - .collect::>(); - - self.inner - .transaction(|mut t| Box::pin(async move { t.batch_get(keys).await })) - .await? - } - }; - - Ok(key_value_items_to_revisions(items)) - } - - pub(crate) async fn delete_revisions>>>( - &self, - object_id: &str, - rev_ids: T, - ) -> Result<(), ServerError> { - match rev_ids.into() { - None => { - let object_id = object_id.to_owned(); - self.inner - .transaction(|mut t| Box::pin(async move { t.batch_delete_key_start_with(&object_id).await })) - .await - } - Some(rev_ids) => { - let keys = rev_ids - .into_iter() - .map(|rev_id| make_revision_key(object_id, rev_id)) - .collect::>(); - - self.inner - .transaction(|mut t| Box::pin(async move { t.batch_delete(keys).await })) - .await - } - } - } -} - -#[inline] -pub fn revisions_to_key_value_items(revisions: Vec) -> Result, ServerError> { - let mut items = vec![]; - for revision in revisions { - let key = make_revision_key(&revision.object_id, revision.rev_id); - - if revision.delta_data.is_empty() { - return Err(ServerError::internal().context("The delta_data of RevisionPB should not be empty")); - } - - let value = Bytes::from(revision.write_to_bytes().unwrap()); - items.push(KeyValue { key, value }); - } - Ok(items) -} - -#[inline] -fn key_value_items_to_revisions(items: Vec) -> RepeatedRevisionPB { - let mut revisions = items - .into_iter() - .filter_map(|kv| parse_from_bytes::(&kv.value).ok()) - .collect::>(); - - revisions.sort_by(|a, b| a.rev_id.cmp(&b.rev_id)); - let mut repeated_revision = RepeatedRevisionPB::new(); - repeated_revision.set_items(revisions.into()); - repeated_revision -} - -#[inline] -fn make_revision_key(object_id: &str, rev_id: i64) -> String { - format!("{}:{}", object_id, rev_id) -} diff --git a/backend/src/services/log.rs b/backend/src/services/log.rs deleted file mode 100644 index 3036234604..0000000000 --- a/backend/src/services/log.rs +++ /dev/null @@ -1,50 +0,0 @@ -use log::LevelFilter; - -use tracing::subscriber::set_global_default; - -use tracing_bunyan_formatter::{BunyanFormattingLayer, JsonStorageLayer}; -use tracing_log::LogTracer; -use tracing_subscriber::{layer::SubscriberExt, EnvFilter}; - -pub struct Builder { - name: String, - env_filter: String, -} - -impl Builder { - pub fn new(name: &str) -> Self { - Builder { - name: name.to_owned(), - env_filter: "Info".to_owned(), - } - } - - pub fn env_filter(mut self, env_filter: &str) -> Self { - self.env_filter = env_filter.to_owned(); - self - } - - pub fn build(self) -> std::result::Result<(), String> { - let env_filter = EnvFilter::new(self.env_filter); - let subscriber = tracing_subscriber::fmt() - .with_target(true) - .with_max_level(tracing::Level::TRACE) - .with_writer(std::io::stderr) - .with_thread_ids(true) - .compact() - .finish() - .with(env_filter); - - let formatting_layer = BunyanFormattingLayer::new(self.name, std::io::stdout); - let _ = set_global_default(subscriber.with(JsonStorageLayer).with(formatting_layer)) - .map_err(|e| format!("{:?}", e))?; - - let _ = LogTracer::builder() - .with_max_level(LevelFilter::Debug) - .init() - .map_err(|e| format!("{:?}", e)) - .unwrap(); - - Ok(()) - } -} diff --git a/backend/src/services/mod.rs b/backend/src/services/mod.rs deleted file mode 100644 index 28905e9684..0000000000 --- a/backend/src/services/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod document; -pub mod folder; -pub mod kv; -pub(crate) mod log; -pub mod user; -pub mod web_socket; diff --git a/backend/src/services/user/controller.rs b/backend/src/services/user/controller.rs deleted file mode 100644 index 7930f06ed1..0000000000 --- a/backend/src/services/user/controller.rs +++ /dev/null @@ -1,236 +0,0 @@ -use crate::{ - entities::{ - logged_user::{LoggedUser, AUTHORIZED_USERS}, - token::Token, - user::UserTable, - }, - util::{ - sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder}, - user_ext::{hash_password, verify_password}, - }, -}; -use anyhow::Context; -use backend_service::{ - errors::{invalid_params, ErrorCode, ServerError}, - response::FlowyResponse, -}; -use chrono::Utc; -use flowy_user_data_model::{ - parser::{UserEmail, UserName, UserPassword}, - protobuf::{ - SignInParams as SignInParamsPB, SignInResponse as SignInResponsePB, SignUpParams as SignUpParamsPB, - SignUpResponse as SignUpResponsePB, UpdateUserParams as UpdateUserParamsPB, UserProfile as UserProfilePB, - }, -}; -use sqlx::{PgPool, Postgres}; - -pub async fn sign_in(pool: &PgPool, params: SignInParamsPB) -> Result { - let email = UserEmail::parse(params.email).map_err(|e| ServerError::params_invalid().context(e))?; - let password = UserPassword::parse(params.password).map_err(|e| ServerError::params_invalid().context(e))?; - - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to sign in")?; - - let user = check_user_password(&mut transaction, email.as_ref(), password.as_ref()).await?; - transaction - .commit() - .await - .context("Failed to commit SQL transaction to sign in.")?; - - let token = Token::create_token(&user.id.to_string())?; - let logged_user = LoggedUser::new(&user.id.to_string()); - - AUTHORIZED_USERS.store_auth(logged_user, true); - let mut response_data = SignInResponsePB::default(); - response_data.set_user_id(user.id.to_string()); - response_data.set_name(user.name); - response_data.set_email(user.email); - response_data.set_token(token.into()); - - Ok(response_data) -} - -pub async fn sign_out(logged_user: LoggedUser) -> Result { - AUTHORIZED_USERS.store_auth(logged_user, false); - Ok(FlowyResponse::success()) -} - -pub async fn register_user(pool: &PgPool, params: SignUpParamsPB) -> Result { - let name = UserName::parse(params.name).map_err(|e| ServerError::params_invalid().context(e))?; - let email = UserEmail::parse(params.email).map_err(|e| ServerError::params_invalid().context(e))?; - let password = UserPassword::parse(params.password).map_err(|e| ServerError::params_invalid().context(e))?; - - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to register user")?; - - let _ = is_email_exist(&mut transaction, email.as_ref()).await?; - let response_data = insert_new_user(&mut transaction, name.as_ref(), email.as_ref(), password.as_ref()) - .await - .context("Failed to insert user")?; - - let logged_user = LoggedUser::new(&response_data.user_id); - AUTHORIZED_USERS.store_auth(logged_user, true); - - transaction - .commit() - .await - .context("Failed to commit SQL transaction to register user.")?; - - FlowyResponse::success().pb(response_data) -} - -pub(crate) async fn get_user_profile( - pool: &PgPool, - token: Token, - logged_user: LoggedUser, -) -> Result { - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to get user detail")?; - - let id = logged_user.as_uuid()?; - let user_table = sqlx::query_as::("SELECT * FROM user_table WHERE id = $1") - .bind(id) - .fetch_one(&mut transaction) - .await - .map_err(|err| ServerError::internal().context(err))?; - - transaction - .commit() - .await - .context("Failed to commit SQL transaction to get user detail.")?; - - // update the user active time - AUTHORIZED_USERS.store_auth(logged_user, true); - - let mut user_profile = UserProfilePB::default(); - user_profile.set_id(user_table.id.to_string()); - user_profile.set_email(user_table.email); - user_profile.set_name(user_table.name); - user_profile.set_token(token.0); - FlowyResponse::success().pb(user_profile) -} - -pub(crate) async fn set_user_profile( - pool: &PgPool, - logged_user: LoggedUser, - params: UpdateUserParamsPB, -) -> Result { - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to update user profile")?; - - let name = match params.has_name() { - false => None, - true => Some(UserName::parse(params.get_name().to_owned()).map_err(invalid_params)?.0), - }; - - let email = match params.has_email() { - false => None, - true => Some( - UserEmail::parse(params.get_email().to_owned()) - .map_err(invalid_params)? - .0, - ), - }; - - let password = match params.has_password() { - false => None, - true => { - let password = UserPassword::parse(params.get_password().to_owned()).map_err(invalid_params)?; - let password = hash_password(password.as_ref())?; - Some(password) - } - }; - - let (sql, args) = SqlBuilder::update("user_table") - .add_some_arg("name", name) - .add_some_arg("email", email) - .add_some_arg("password", password) - .and_where_eq("id", &logged_user.as_uuid()?) - .build()?; - - sqlx::query_with(&sql, args) - .execute(&mut transaction) - .await - .map_err(map_sqlx_error)?; - - transaction - .commit() - .await - .context("Failed to commit SQL transaction to update user profile.")?; - - Ok(FlowyResponse::success()) -} - -async fn is_email_exist(transaction: &mut DBTransaction<'_>, email: &str) -> Result<(), ServerError> { - let result = sqlx::query(r#"SELECT email FROM user_table WHERE email = $1"#) - .bind(email) - .fetch_optional(transaction) - .await - .map_err(|err| ServerError::internal().context(err))?; - - match result { - Some(_) => Err(ServerError { - code: ErrorCode::EmailAlreadyExists, - msg: format!("{} already exists", email), - }), - None => Ok(()), - } -} - -async fn check_user_password( - transaction: &mut DBTransaction<'_>, - email: &str, - password: &str, -) -> Result { - let user = sqlx::query_as::("SELECT * FROM user_table WHERE email = $1") - .bind(email) - .fetch_one(transaction) - .await - .map_err(|err| ServerError::internal().context(err))?; - - match verify_password(password, &user.password) { - Ok(true) => Ok(user), - _ => Err(ServerError::password_not_match()), - } -} - -async fn insert_new_user( - transaction: &mut DBTransaction<'_>, - name: &str, - email: &str, - password: &str, -) -> Result { - let uuid = uuid::Uuid::new_v4(); - let token = Token::create_token(&uuid.to_string())?; - let password = hash_password(password)?; - let _ = sqlx::query!( - r#" - INSERT INTO user_table (id, email, name, create_time, password) - VALUES ($1, $2, $3, $4, $5) - "#, - uuid, - email, - name, - Utc::now(), - password, - ) - .execute(transaction) - .await - .map_err(|e| ServerError::internal().context(e))?; - - let mut response = SignUpResponsePB::default(); - response.set_user_id(uuid.to_string()); - response.set_name(name.to_string()); - response.set_email(email.to_string()); - response.set_token(token.into()); - - Ok(response) -} diff --git a/backend/src/services/user/mod.rs b/backend/src/services/user/mod.rs deleted file mode 100644 index d3c1ce98a2..0000000000 --- a/backend/src/services/user/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub use controller::*; - -mod controller; -pub mod router; diff --git a/backend/src/services/user/router.rs b/backend/src/services/user/router.rs deleted file mode 100644 index 52efbe4dfa..0000000000 --- a/backend/src/services/user/router.rs +++ /dev/null @@ -1,64 +0,0 @@ -use crate::{ - entities::{logged_user::LoggedUser, token::Token}, - services::user::{get_user_profile, register_user, set_user_profile, sign_in, sign_out}, - util::serde_ext::parse_from_payload, -}; -use actix_identity::Identity; -use actix_web::{ - web::{Data, Payload}, - HttpRequest, HttpResponse, -}; -use backend_service::{errors::ServerError, response::FlowyResponse}; -use flowy_user_data_model::protobuf::{ - SignInParams as SignInParamsPB, SignUpParams as SignUpParamsPB, UpdateUserParams as UpdateUserParamsPB, -}; -use sqlx::PgPool; - -pub async fn sign_in_handler(payload: Payload, id: Identity, pool: Data) -> Result { - let params: SignInParamsPB = parse_from_payload(payload).await?; - let data = sign_in(pool.get_ref(), params).await?; - id.remember(data.token.clone()); - let response = FlowyResponse::success().pb(data)?; - Ok(response.into()) -} - -pub async fn sign_out_handler(logged_user: LoggedUser, id: Identity) -> Result { - id.forget(); - - let response = sign_out(logged_user).await?; - Ok(response.into()) -} - -pub async fn get_user_profile_handler( - token: Token, - logged_user: LoggedUser, - pool: Data, -) -> Result { - let response = get_user_profile(pool.get_ref(), token, logged_user).await?; - Ok(response.into()) -} - -pub async fn set_user_profile_handler( - logged_user: LoggedUser, - pool: Data, - payload: Payload, -) -> Result { - let params: UpdateUserParamsPB = parse_from_payload(payload).await?; - let response = set_user_profile(pool.get_ref(), logged_user, params).await?; - Ok(response.into()) -} - -pub async fn register_handler(payload: Payload, pool: Data) -> Result { - let params: SignUpParamsPB = parse_from_payload(payload).await?; - let resp = register_user(pool.get_ref(), params).await?; - - Ok(resp.into()) -} - -pub async fn change_password( - _request: HttpRequest, - _payload: Payload, - _pool: Data, -) -> Result { - unimplemented!() -} diff --git a/backend/src/services/web_socket/entities/connect.rs b/backend/src/services/web_socket/entities/connect.rs deleted file mode 100644 index c083950e45..0000000000 --- a/backend/src/services/web_socket/entities/connect.rs +++ /dev/null @@ -1,50 +0,0 @@ -use crate::services::web_socket::WebSocketMessage; -use actix::{Message, Recipient}; -use backend_service::errors::ServerError; -use serde::{Deserialize, Serialize}; -use std::fmt::Formatter; - -pub type Socket = Recipient; - -#[derive(Serialize, Deserialize, Debug, Clone, Hash, PartialEq, Eq)] -pub struct SessionId(pub String); - -impl> std::convert::From for SessionId { - fn from(s: T) -> Self { - SessionId(s.as_ref().to_owned()) - } -} - -impl std::fmt::Display for SessionId { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let desc = &self.0.to_string(); - f.write_str(desc) - } -} - -pub struct Session { - pub id: SessionId, - pub socket: Socket, -} - -impl std::convert::From for Session { - fn from(c: Connect) -> Self { - Self { - id: c.sid, - socket: c.socket, - } - } -} - -#[derive(Debug, Message, Clone)] -#[rtype(result = "Result<(), ServerError>")] -pub struct Connect { - pub socket: Socket, - pub sid: SessionId, -} - -#[derive(Debug, Message, Clone)] -#[rtype(result = "Result<(), ServerError>")] -pub struct Disconnect { - pub sid: SessionId, -} diff --git a/backend/src/services/web_socket/entities/message.rs b/backend/src/services/web_socket/entities/message.rs deleted file mode 100644 index 82f9cc1155..0000000000 --- a/backend/src/services/web_socket/entities/message.rs +++ /dev/null @@ -1,27 +0,0 @@ -use actix::Message; -use bytes::Bytes; -use flowy_collaboration::entities::ws_data::ServerRevisionWSData; -use lib_ws::{WSChannel, WebSocketRawMessage}; -use std::convert::TryInto; - -#[derive(Debug, Message, Clone)] -#[rtype(result = "()")] -pub struct WebSocketMessage(pub Bytes); - -impl std::ops::Deref for WebSocketMessage { - type Target = Bytes; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -pub fn revision_data_to_ws_message(data: ServerRevisionWSData, channel: WSChannel) -> WebSocketMessage { - let bytes: Bytes = data.try_into().unwrap(); - let msg = WebSocketRawMessage { - channel, - data: bytes.to_vec(), - }; - let bytes: Bytes = msg.try_into().unwrap(); - WebSocketMessage(bytes) -} diff --git a/backend/src/services/web_socket/entities/mod.rs b/backend/src/services/web_socket/entities/mod.rs deleted file mode 100644 index 5b05781f6b..0000000000 --- a/backend/src/services/web_socket/entities/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub use connect::*; -pub use message::*; - -mod connect; -pub mod message; diff --git a/backend/src/services/web_socket/mod.rs b/backend/src/services/web_socket/mod.rs deleted file mode 100644 index da27353f58..0000000000 --- a/backend/src/services/web_socket/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -pub use entities::message::*; -pub use ws_client::*; -pub use ws_server::*; - -pub(crate) mod entities; -pub mod router; -mod ws_client; -mod ws_server; diff --git a/backend/src/services/web_socket/router.rs b/backend/src/services/web_socket/router.rs deleted file mode 100644 index ee9c61cf45..0000000000 --- a/backend/src/services/web_socket/router.rs +++ /dev/null @@ -1,61 +0,0 @@ -use crate::{ - entities::logged_user::LoggedUser, - services::web_socket::{WSClient, WSServer, WSUser, WebSocketReceivers}, -}; -use actix::Addr; -use actix_web::{ - get, - web::{Data, Path, Payload}, - Error, HttpRequest, HttpResponse, -}; -use actix_web_actors::ws; - -#[rustfmt::skip] -// WsClient -// ┌─────────────┐ -// │ ┌────────┐ │ -// wss://xxx ─────▶│ │ WsUser │ │───┐ -// │ └────────┘ │ │ -// └─────────────┘ │ -// │ -// │ ┌──────────────────┐ 1 n ┌──────────────────┐ -// ├───▶│WebSocketReceivers│◆────│WebSocketReceiver │ -// │ └──────────────────┘ └──────────────────┘ -// WsClient │ △ -// ┌─────────────┐ │ │ -// │ ┌────────┐ │ │ │ -// wss://xxx ─────▶│ │ WsUser │ │───┘ │ -// │ └────────┘ │ ┌───────────────┐ -// └─────────────┘ │DocumentManager│ -// └───────────────┘ -#[get("/{token}")] -pub async fn establish_ws_connection( - request: HttpRequest, - payload: Payload, - token: Path, - server: Data>, - ws_receivers: Data, -) -> Result { - tracing::info!("establish_ws_connection"); - match LoggedUser::from_token(token.clone()) { - Ok(user) => { - let ws_user = WSUser::new(user); - let client = WSClient::new(ws_user, server.get_ref().clone(), ws_receivers); - let result = ws::start(client, &request, payload); - match result { - Ok(response) => Ok(response), - Err(e) => { - log::error!("ws connection error: {:?}", e); - Err(e) - }, - } - }, - Err(e) => { - if e.is_unauthorized() { - Ok(HttpResponse::Unauthorized().json(e)) - } else { - Ok(HttpResponse::BadRequest().json(e)) - } - }, - } -} diff --git a/backend/src/services/web_socket/ws_client.rs b/backend/src/services/web_socket/ws_client.rs deleted file mode 100644 index 46eb105b13..0000000000 --- a/backend/src/services/web_socket/ws_client.rs +++ /dev/null @@ -1,181 +0,0 @@ -use crate::{ - config::{HEARTBEAT_INTERVAL, PING_TIMEOUT}, - entities::logged_user::LoggedUser, - services::web_socket::{ - entities::{Connect, Disconnect, Socket}, - WSServer, WebSocketMessage, - }, -}; -use actix::*; -use actix_web::web::Data; -use actix_web_actors::{ws, ws::Message::Text}; -use bytes::Bytes; -use lib_ws::{WSChannel, WebSocketRawMessage}; -use std::{collections::HashMap, convert::TryFrom, sync::Arc, time::Instant}; - -pub trait WebSocketReceiver: Send + Sync { - fn receive(&self, data: WSClientData); -} - -#[derive(Default)] -pub struct WebSocketReceivers { - inner: HashMap>, -} - -impl WebSocketReceivers { - pub fn new() -> Self { - WebSocketReceivers::default() - } - - pub fn set(&mut self, channel: WSChannel, receiver: Arc) { - tracing::trace!("Add {:?} receiver", channel); - self.inner.insert(channel, receiver); - } - - pub fn get(&self, source: &WSChannel) -> Option> { - self.inner.get(source).cloned() - } -} - -#[derive(Debug)] -pub struct WSUser { - inner: LoggedUser, -} - -impl WSUser { - pub fn new(inner: LoggedUser) -> Self { - Self { inner } - } - - pub fn id(&self) -> &str { - &self.inner.user_id - } -} - -pub struct WSClientData { - pub(crate) user: Arc, - pub(crate) socket: Socket, - pub(crate) data: Bytes, -} - -pub struct WSClient { - user: Arc, - server: Addr, - ws_receivers: Data, - hb: Instant, -} - -impl WSClient { - pub fn new(user: WSUser, server: Addr, ws_receivers: Data) -> Self { - Self { - user: Arc::new(user), - server, - ws_receivers, - hb: Instant::now(), - } - } - - fn hb(&self, ctx: &mut ws::WebsocketContext) { - ctx.run_interval(HEARTBEAT_INTERVAL, |client, ctx| { - if Instant::now().duration_since(client.hb) > PING_TIMEOUT { - client.server.do_send(Disconnect { - sid: client.user.id().into(), - }); - ctx.stop(); - } else { - ctx.ping(b""); - } - }); - } - - fn handle_binary_message(&self, bytes: Bytes, socket: Socket) { - // TODO: ok to unwrap? - let message: WebSocketRawMessage = WebSocketRawMessage::try_from(bytes).unwrap(); - match self.ws_receivers.get(&message.channel) { - None => { - log::error!("Can't find the receiver for {:?}", message.channel); - } - Some(handler) => { - let client_data = WSClientData { - user: self.user.clone(), - socket, - data: Bytes::from(message.data), - }; - handler.receive(client_data); - } - } - } -} - -impl StreamHandler> for WSClient { - fn handle(&mut self, msg: Result, ctx: &mut Self::Context) { - match msg { - Ok(ws::Message::Ping(msg)) => { - self.hb = Instant::now(); - ctx.pong(&msg); - } - Ok(ws::Message::Pong(_msg)) => { - // tracing::debug!("Receive {} pong {:?}", &self.session_id, &msg); - self.hb = Instant::now(); - } - Ok(ws::Message::Binary(bytes)) => { - let socket = ctx.address().recipient(); - self.handle_binary_message(bytes, socket); - } - Ok(Text(_)) => { - log::warn!("Receive unexpected text message"); - } - Ok(ws::Message::Close(reason)) => { - ctx.close(reason); - ctx.stop(); - } - Ok(ws::Message::Continuation(_)) => {} - Ok(ws::Message::Nop) => {} - Err(e) => { - log::error!("[{}]: WebSocketStream protocol error {:?}", self.user.id(), e); - ctx.stop(); - } - } - } -} - -impl Handler for WSClient { - type Result = (); - - fn handle(&mut self, msg: WebSocketMessage, ctx: &mut Self::Context) { - ctx.binary(msg.0); - } -} - -impl Actor for WSClient { - type Context = ws::WebsocketContext; - - fn started(&mut self, ctx: &mut Self::Context) { - self.hb(ctx); - let socket = ctx.address().recipient(); - let connect = Connect { - socket, - sid: self.user.id().into(), - }; - self.server - .send(connect) - .into_actor(self) - .then(|res, _client, _ctx| { - match res { - Ok(Ok(_)) => tracing::trace!("Send connect message to server success"), - Ok(Err(e)) => log::error!("Send connect message to server failed: {:?}", e), - Err(e) => log::error!("Send connect message to server failed: {:?}", e), - } - fut::ready(()) - }) - .wait(ctx); - } - - fn stopping(&mut self, _: &mut Self::Context) -> Running { - self.server.do_send(Disconnect { - sid: self.user.id().into(), - }); - - Running::Stop - } -} diff --git a/backend/src/services/web_socket/ws_server.rs b/backend/src/services/web_socket/ws_server.rs deleted file mode 100644 index af69c0f4d8..0000000000 --- a/backend/src/services/web_socket/ws_server.rs +++ /dev/null @@ -1,65 +0,0 @@ -use crate::services::web_socket::{ - entities::{Connect, Disconnect, Session, SessionId}, - WebSocketMessage, -}; -use actix::{Actor, Context, Handler}; -use backend_service::errors::ServerError; -use dashmap::DashMap; - -pub struct WSServer { - sessions: DashMap, -} - -impl std::default::Default for WSServer { - fn default() -> Self { - Self { - sessions: DashMap::new(), - } - } -} -impl WSServer { - pub fn new() -> Self { - WSServer::default() - } - - pub fn send(&self, _msg: WebSocketMessage) { - unimplemented!() - } -} - -impl Actor for WSServer { - type Context = Context; - fn started(&mut self, _ctx: &mut Self::Context) {} -} - -impl Handler for WSServer { - type Result = Result<(), ServerError>; - fn handle(&mut self, msg: Connect, _ctx: &mut Context) -> Self::Result { - let session: Session = msg.into(); - self.sessions.insert(session.id.clone(), session); - - Ok(()) - } -} - -impl Handler for WSServer { - type Result = Result<(), ServerError>; - fn handle(&mut self, msg: Disconnect, _: &mut Context) -> Self::Result { - self.sessions.remove(&msg.sid); - Ok(()) - } -} - -impl Handler for WSServer { - type Result = (); - - fn handle(&mut self, _msg: WebSocketMessage, _ctx: &mut Context) -> Self::Result { - unimplemented!() - } -} - -impl actix::Supervised for WSServer { - fn restarting(&mut self, _ctx: &mut Context) { - log::warn!("restarting"); - } -} diff --git a/backend/src/util/mod.rs b/backend/src/util/mod.rs deleted file mode 100644 index c4236c6ebf..0000000000 --- a/backend/src/util/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod serde_ext; -pub mod sqlx_ext; -pub mod user_ext; diff --git a/backend/src/util/serde_ext.rs b/backend/src/util/serde_ext.rs deleted file mode 100644 index 04b791ca11..0000000000 --- a/backend/src/util/serde_ext.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::config::MAX_PAYLOAD_SIZE; -use actix_web::web; -use backend_service::errors::{ErrorCode, ServerError}; -use futures::StreamExt; -use protobuf::{Message, ProtobufResult}; - -pub async fn parse_from_payload(payload: web::Payload) -> Result { - let bytes = poll_payload(&mut payload.into_inner()).await?; - parse_from_bytes(&bytes) -} - -#[allow(dead_code)] -pub async fn parse_from_dev_payload(payload: &mut actix_web::dev::Payload) -> Result { - let bytes = poll_payload(payload).await?; - parse_from_bytes(&bytes) -} - -#[inline] -pub fn md5>(data: T) -> String { - let md5 = format!("{:x}", md5::compute(data)); - md5 -} - -pub fn parse_from_bytes(bytes: &[u8]) -> Result { - let result: ProtobufResult = Message::parse_from_bytes(bytes); - match result { - Ok(data) => Ok(data), - Err(e) => Err(e.into()), - } -} - -pub async fn poll_payload(payload: &mut actix_web::dev::Payload) -> Result { - let mut body = web::BytesMut::new(); - while let Some(chunk) = payload.next().await { - let chunk = chunk.map_err(|err| ServerError::internal().context(err))?; - - if (body.len() + chunk.len()) > MAX_PAYLOAD_SIZE { - return Err(ServerError::new( - "Payload overflow".to_string(), - ErrorCode::PayloadOverflow, - )); - } - body.extend_from_slice(&chunk); - } - Ok(body) -} diff --git a/backend/src/util/sqlx_ext/mod.rs b/backend/src/util/sqlx_ext/mod.rs deleted file mode 100644 index efb620b253..0000000000 --- a/backend/src/util/sqlx_ext/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -mod query; -mod utils; - -pub use utils::*; - -pub use query::*; diff --git a/backend/src/util/sqlx_ext/query.rs b/backend/src/util/sqlx_ext/query.rs deleted file mode 100644 index 99414b71b9..0000000000 --- a/backend/src/util/sqlx_ext/query.rs +++ /dev/null @@ -1,159 +0,0 @@ -use backend_service::errors::ServerError; -use sql_builder::SqlBuilder as InnerBuilder; -use sqlx::{postgres::PgArguments, Arguments, Encode, Postgres, Type}; - -enum BuilderType { - Create, - Select, - Update, - Delete, -} - -pub struct SqlBuilder { - table: String, - fields: Vec, - filters: Vec, - fields_args: PgArguments, - ty: BuilderType, -} - -impl SqlBuilder { - fn new(table: &str) -> Self { - Self { - table: table.to_owned(), - fields: vec![], - filters: vec![], - fields_args: PgArguments::default(), - ty: BuilderType::Select, - } - } - - pub fn create(table: &str) -> Self { - let mut builder = Self::new(table); - builder.ty = BuilderType::Create; - builder - } - - pub fn select(table: &str) -> Self { - let mut builder = Self::new(table); - builder.ty = BuilderType::Select; - builder - } - - pub fn update(table: &str) -> Self { - let mut builder = Self::new(table); - builder.ty = BuilderType::Update; - builder - } - - pub fn delete(table: &str) -> Self { - let mut builder = Self::new(table); - builder.ty = BuilderType::Delete; - builder - } - - pub fn add_field_with_arg<'a, T>(mut self, field: &str, arg: T) -> Self - where - T: 'a + Send + Encode<'a, Postgres> + Type, - { - self.fields.push(field.to_owned()); - self.fields_args.add(arg); - self - } - - #[allow(dead_code)] - pub fn add_arg_if<'a, T>(self, add: bool, field: &str, arg: T) -> Self - where - T: 'a + Send + Encode<'a, Postgres> + Type, - { - if add { - self.add_field_with_arg(field, arg) - } else { - self - } - } - - pub fn add_some_arg<'a, T>(self, field: &str, arg: Option) -> Self - where - T: 'a + Send + Encode<'a, Postgres> + Type, - { - if let Some(arg) = arg { - self.add_field_with_arg(field, arg) - } else { - self - } - } - - pub fn add_field(mut self, field: &str) -> Self { - self.fields.push(field.to_owned()); - self - } - - pub fn and_where_eq<'a, T>(mut self, field: &str, arg: T) -> Self - where - T: 'a + Send + Encode<'a, Postgres> + Type, - { - self.filters.push(field.to_owned()); - self.fields_args.add(arg); - self - } - - pub fn build(self) -> Result<(String, PgArguments), ServerError> { - match self.ty { - BuilderType::Create => { - let mut inner = InnerBuilder::insert_into(&self.table); - self.fields.iter().for_each(|field| { - inner.field(field); - }); - - let values = self - .fields - .iter() - .enumerate() - .map(|(index, _)| format!("${}", index + 1)) - .collect::>(); - - inner.values(&values); - - let sql = inner.sql()?; - Ok((sql, self.fields_args)) - } - BuilderType::Select => { - let mut inner = InnerBuilder::select_from(&self.table); - self.fields.into_iter().for_each(|field| { - inner.field(field); - }); - - self.filters.into_iter().enumerate().for_each(|(index, filter)| { - inner.and_where_eq(filter, format!("${}", index + 1)); - }); - - let sql = inner.sql()?; - Ok((sql, self.fields_args)) - } - BuilderType::Update => { - let mut inner = InnerBuilder::update_table(&self.table); - let field_len = self.fields.len(); - self.fields.into_iter().enumerate().for_each(|(index, field)| { - inner.set(&field, format!("${}", index + 1)); - }); - - self.filters.into_iter().enumerate().for_each(|(index, filter)| { - let index = index + field_len; - inner.and_where_eq(filter, format!("${}", index + 1)); - }); - - let sql = inner.sql()?; - Ok((sql, self.fields_args)) - } - BuilderType::Delete => { - let mut inner = InnerBuilder::delete_from(&self.table); - self.filters.into_iter().enumerate().for_each(|(index, filter)| { - inner.and_where_eq(filter, format!("${}", index + 1)); - }); - let sql = inner.sql()?; - Ok((sql, self.fields_args)) - } - } - } -} diff --git a/backend/src/util/sqlx_ext/utils.rs b/backend/src/util/sqlx_ext/utils.rs deleted file mode 100644 index 0b0d69b6e4..0000000000 --- a/backend/src/util/sqlx_ext/utils.rs +++ /dev/null @@ -1,11 +0,0 @@ -use backend_service::errors::{ErrorCode, ServerError}; -use sqlx::{Error, Postgres, Transaction}; - -pub type DBTransaction<'a> = Transaction<'a, Postgres>; - -pub fn map_sqlx_error(error: sqlx::Error) -> ServerError { - match error { - Error::RowNotFound => ServerError::new("".to_string(), ErrorCode::RecordNotFound), - _ => ServerError::internal().context(error), - } -} diff --git a/backend/src/util/user_ext.rs b/backend/src/util/user_ext.rs deleted file mode 100644 index 5d6e8c8867..0000000000 --- a/backend/src/util/user_ext.rs +++ /dev/null @@ -1,31 +0,0 @@ -use backend_service::errors::{ErrorCode, ServerError}; -use bcrypt::{hash, verify, DEFAULT_COST}; - -#[allow(dead_code)] -pub fn uuid() -> String { - uuid::Uuid::new_v4().to_string() -} - -pub fn hash_password(plain: &str) -> Result { - let hashing_cost = std::env::var("HASH_COST") - .ok() - .and_then(|c| c.parse().ok()) - .unwrap_or(DEFAULT_COST); - - hash(plain, hashing_cost).map_err(|e| ServerError::internal().context(e)) -} - -// The Source is the password user enter. The hash is the source after hashing. -// let source = "123"; -// let hash = hash_password(source).unwrap(); -// -// verify_password(source, hash) -pub fn verify_password(source: &str, hash: &str) -> Result { - match verify(source, hash) { - Ok(true) => Ok(true), - _ => Err(ServerError::new( - "Username and password don't match".to_string(), - ErrorCode::PasswordNotMatch, - )), - } -} diff --git a/backend/tests/api_test/auth_test.rs b/backend/tests/api_test/auth_test.rs deleted file mode 100644 index bcab6d0f4b..0000000000 --- a/backend/tests/api_test/auth_test.rs +++ /dev/null @@ -1,126 +0,0 @@ -use crate::util::helper::{spawn_user_server, TestUserServer}; -use backend_service::errors::ErrorCode; -use flowy_user_data_model::entities::{SignInParams, SignUpParams, SignUpResponse, UpdateUserParams}; - -#[actix_rt::test] -async fn user_register() { - let app = spawn_user_server().await; - let response = register_user(&app, "annie@appflowy.io", "HelloWorld123!").await; - tracing::info!("{:?}", response); -} - -#[actix_rt::test] -#[should_panic] -async fn user_sign_in_with_invalid_password() { - let app = spawn_user_server().await; - let email = "annie@appflowy.io"; - let password = "123"; - let _ = register_user(&app, email, password).await; -} - -#[actix_rt::test] -#[should_panic] -async fn user_sign_in_with_invalid_email() { - let app = spawn_user_server().await; - let email = "annie@gmail@"; - let password = "HelloWorld123!"; - let _ = register_user(&app, email, password).await; -} - -#[actix_rt::test] -async fn user_sign_in() { - let app = spawn_user_server().await; - let email = "annie@appflowy.io"; - let password = "HelloWorld123!"; - let _ = register_user(&app, email, password).await; - let params = SignInParams { - email: email.to_string(), - password: password.to_string(), - name: "rust".to_string(), - }; - let _ = app.sign_in(params).await.unwrap(); -} - -#[actix_rt::test] -#[should_panic] -async fn user_sign_out() { - let server = TestUserServer::new().await; - server.sign_out().await; - - // user_detail will be empty because use was sign out. - server.get_user_profile().await; -} - -#[actix_rt::test] -async fn user_get_detail() { - let server = TestUserServer::new().await; - tracing::info!("{:?}", server.get_user_profile().await); -} - -#[actix_rt::test] -async fn user_update_password() { - let mut server = spawn_user_server().await; - let email = "annie@appflowy.io"; - let password = "HelloWorld123!"; - let sign_up_resp = register_user(&server, email, password).await; - - let params = UpdateUserParams::new(&sign_up_resp.user_id).password("Hello123!"); - server.user_token = Some(sign_up_resp.token); - - server.update_user_profile(params).await.unwrap(); - - let sign_in_params = SignInParams { - email: email.to_string(), - password: password.to_string(), - name: "rust".to_string(), - }; - - match server.sign_in(sign_in_params).await { - Ok(_) => {} - Err(e) => { - assert_eq!(e.code, ErrorCode::PasswordNotMatch); - } - } -} - -#[actix_rt::test] -async fn user_update_name() { - let server = TestUserServer::new().await; - - let name = "tom".to_string(); - let params = UpdateUserParams::new(server.user_id()).name(&name); - server.update_user_profile(params).await.unwrap(); - - let user = server.get_user_profile().await; - assert_eq!(user.name, name); -} - -#[actix_rt::test] -async fn user_update_email() { - let server = TestUserServer::new().await; - let email = "123@gmail.com".to_string(); - let params = UpdateUserParams::new(server.user_id()).email(&email); - server.update_user_profile(params).await.unwrap(); - - let user = server.get_user_profile().await; - assert_eq!(user.email, email); -} - -#[allow(dead_code)] -async fn sign_up_user(server: &TestUserServer) -> SignUpResponse { - let email = "annie@appflowy.io"; - let password = "HelloWorld123!"; - let response = register_user(server, email, password).await; - response -} - -async fn register_user(server: &TestUserServer, email: &str, password: &str) -> SignUpResponse { - let params = SignUpParams { - email: email.to_string(), - name: "annie".to_string(), - password: password.to_string(), - }; - - let response = server.register(params).await; - response -} diff --git a/backend/tests/api_test/kv_test.rs b/backend/tests/api_test/kv_test.rs deleted file mode 100644 index 3765270ef3..0000000000 --- a/backend/tests/api_test/kv_test.rs +++ /dev/null @@ -1,79 +0,0 @@ -use crate::util::helper::spawn_server; -use backend::services::kv::KeyValue; -use std::str; - -#[actix_rt::test] -async fn kv_set_test() { - let server = spawn_server().await; - let kv = server.app_ctx.persistence.document_kv_store(); - let s1 = "123".to_string(); - let key = "1"; - - let _ = kv.set(key, s1.clone().into()).await.unwrap(); - let bytes = kv.get(key).await.unwrap().unwrap(); - let s2 = str::from_utf8(&bytes).unwrap(); - assert_eq!(s1, s2); -} - -#[actix_rt::test] -async fn kv_delete_test() { - let server = spawn_server().await; - let kv = server.app_ctx.persistence.document_kv_store(); - let s1 = "123".to_string(); - let key = "1"; - - let _ = kv.set(key, s1.clone().into()).await.unwrap(); - let _ = kv.remove(key).await.unwrap(); - assert_eq!(kv.get(key).await.unwrap(), None); -} - -#[actix_rt::test] -async fn kv_batch_set_test() { - let server = spawn_server().await; - let kv = server.app_ctx.persistence.document_kv_store(); - let kvs = vec![ - KeyValue { - key: "1".to_string(), - value: "a".to_string().into(), - }, - KeyValue { - key: "2".to_string(), - value: "b".to_string().into(), - }, - ]; - - kv.batch_set(kvs.clone()).await.unwrap(); - let kvs_from_db = kv - .batch_get(kvs.clone().into_iter().map(|value| value.key).collect::>()) - .await - .unwrap(); - - assert_eq!(kvs, kvs_from_db); -} - -#[actix_rt::test] -async fn kv_batch_get_start_with_test() { - let server = spawn_server().await; - let kv = server.app_ctx.persistence.document_kv_store(); - let kvs = vec![ - KeyValue { - key: "abc:1".to_string(), - value: "a".to_string().into(), - }, - KeyValue { - key: "abc:2".to_string(), - value: "b".to_string().into(), - }, - ]; - - kv.batch_set(kvs.clone()).await.unwrap(); - kv.transaction(|mut transaction| { - Box::pin(async move { - let kvs_from_db = transaction.batch_get_start_with("abc").await.unwrap(); - assert_eq!(kvs, kvs_from_db); - Ok(()) - }) - }) - .await - .unwrap(); -} diff --git a/backend/tests/api_test/mod.rs b/backend/tests/api_test/mod.rs deleted file mode 100644 index 6053586a32..0000000000 --- a/backend/tests/api_test/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod auth_test; -mod kv_test; -mod workspace_test; diff --git a/backend/tests/api_test/workspace_test.rs b/backend/tests/api_test/workspace_test.rs deleted file mode 100644 index 33a7157e84..0000000000 --- a/backend/tests/api_test/workspace_test.rs +++ /dev/null @@ -1,282 +0,0 @@ -#![allow(clippy::all)] - -use crate::util::helper::{BackendViewTest, *}; -use flowy_collaboration::{ - client_document::{ClientDocument, PlainDoc}, - entities::{ - document_info::{CreateDocParams, DocumentId}, - revision::{md5, RepeatedRevision, Revision}, - }, -}; -use flowy_folder_data_model::entities::{ - app::{AppId, UpdateAppParams}, - trash::{RepeatedTrashId, TrashId, TrashType}, - view::{RepeatedViewId, UpdateViewParams, ViewId}, - workspace::{CreateWorkspaceParams, UpdateWorkspaceParams, WorkspaceId}, -}; - -#[actix_rt::test] -async fn workspace_create() { - let test = BackendWorkspaceTest::new().await; - tracing::info!("{:?}", test.workspace); -} - -#[actix_rt::test] -async fn workspace_read() { - let test = BackendWorkspaceTest::new().await; - let read_params = WorkspaceId::new(Some(test.workspace.id.clone())); - let repeated_workspace = test.server.read_workspaces(read_params).await; - tracing::info!("{:?}", repeated_workspace); -} - -#[actix_rt::test] -async fn workspace_read_with_belongs() { - let test = BackendWorkspaceTest::new().await; - - let _ = test.create_app().await; - let _ = test.create_app().await; - let _ = test.create_app().await; - - let read_params = WorkspaceId::new(Some(test.workspace.id.clone())); - let workspaces = test.server.read_workspaces(read_params).await; - let workspace = workspaces.items.first().unwrap(); - assert_eq!(workspace.apps.len(), 3); -} - -#[actix_rt::test] -async fn workspace_update() { - let test = BackendWorkspaceTest::new().await; - let new_name = "rename workspace name"; - let new_desc = "rename workspace description"; - - let update_params = UpdateWorkspaceParams { - id: test.workspace.id.clone(), - name: Some(new_name.to_string()), - desc: Some(new_desc.to_string()), - }; - test.server.update_workspace(update_params).await; - let read_params = WorkspaceId::new(Some(test.workspace.id.clone())); - let repeated_workspace = test.server.read_workspaces(read_params).await; - - let workspace = repeated_workspace.first().unwrap(); - assert_eq!(workspace.name, new_name); - assert_eq!(workspace.desc, new_desc); -} - -#[actix_rt::test] -async fn workspace_delete() { - let test = BackendWorkspaceTest::new().await; - let delete_params = WorkspaceId { - workspace_id: Some(test.workspace.id.clone()), - }; - - let _ = test.server.delete_workspace(delete_params).await; - let read_params = WorkspaceId::new(Some(test.workspace.id.clone())); - let repeated_workspace = test.server.read_workspaces(read_params).await; - assert_eq!(repeated_workspace.len(), 0); -} - -#[actix_rt::test] -async fn app_create() { - let test = BackendAppTest::new().await; - tracing::info!("{:?}", test.app); -} - -#[actix_rt::test] -async fn app_read() { - let test = BackendAppTest::new().await; - let read_params = AppId::new(&test.app.id); - assert_eq!(test.server.read_app(read_params).await.is_some(), true); -} - -#[actix_rt::test] -async fn app_read_with_belongs() { - let test = BackendAppTest::new().await; - - let _ = create_test_view(&test.server, &test.app.id).await; - let _ = create_test_view(&test.server, &test.app.id).await; - - let read_params = AppId::new(&test.app.id); - let app = test.server.read_app(read_params).await.unwrap(); - assert_eq!(app.belongings.len(), 2); -} - -#[actix_rt::test] -async fn app_read_with_belongs_in_trash() { - let test = BackendAppTest::new().await; - - let _ = create_test_view(&test.server, &test.app.id).await; - let view = create_test_view(&test.server, &test.app.id).await; - - test.server.create_view_trash(&view.id).await; - - let read_params = AppId::new(&test.app.id); - let app = test.server.read_app(read_params).await.unwrap(); - assert_eq!(app.belongings.len(), 1); -} - -#[actix_rt::test] -async fn app_update() { - let test = BackendAppTest::new().await; - - let new_name = "flowy"; - - let update_params = UpdateAppParams::new(&test.app.id).name(new_name); - test.server.update_app(update_params).await; - - let read_params = AppId::new(&test.app.id); - let app = test.server.read_app(read_params).await.unwrap(); - assert_eq!(&app.name, new_name); -} - -#[actix_rt::test] -async fn app_delete() { - let test = BackendAppTest::new().await; - - let delete_params = AppId { - app_id: test.app.id.clone(), - }; - test.server.delete_app(delete_params).await; - let read_params = AppId::new(&test.app.id); - assert_eq!(test.server.read_app(read_params).await.is_none(), true); -} - -#[actix_rt::test] -async fn view_create() { - let test = BackendViewTest::new().await; - tracing::info!("{:?}", test.view); -} - -#[actix_rt::test] -async fn view_update() { - let test = BackendViewTest::new().await; - let new_name = "name view name"; - - // update - let update_params = UpdateViewParams::new(&test.view.id).name(new_name); - test.server.update_view(update_params).await; - - // read - let read_params: ViewId = test.view.id.clone().into(); - let view = test.server.read_view(read_params).await.unwrap(); - assert_eq!(&view.name, new_name); -} - -#[actix_rt::test] -async fn view_delete() { - let test = BackendViewTest::new().await; - test.server.create_view_trash(&test.view.id).await; - - let trash_ids = test - .server - .read_trash() - .await - .items - .into_iter() - .map(|item| item.id) - .collect::>(); - // read - let read_params: ViewId = test.view.id.clone().into(); - - // the view can't read from the server. it should be in the trash - assert_eq!(test.server.read_view(read_params).await.is_none(), true); - assert_eq!(trash_ids.contains(&test.view.id), true); -} - -#[actix_rt::test] -async fn trash_delete() { - let test = BackendViewTest::new().await; - test.server.create_view_trash(&test.view.id).await; - - let identifier = TrashId { - id: test.view.id.clone(), - ty: TrashType::View, - }; - test.server.delete_view_trash(vec![identifier].into()).await; - - assert_eq!(test.server.read_trash().await.is_empty(), true); -} - -#[actix_rt::test] -async fn trash_delete_all() { - let test = BackendViewTest::new().await; - test.server.create_view_trash(&test.view.id).await; - - test.server.delete_view_trash(RepeatedTrashId::all()).await; - assert_eq!(test.server.read_trash().await.is_empty(), true); -} - -#[actix_rt::test] -async fn workspace_list_read() { - let mut server = spawn_user_server().await; - let token = server.register_user().await.token; - server.user_token = Some(token); - for i in 0..3 { - let params = CreateWorkspaceParams { - name: format!("{} workspace", i), - desc: format!("This is my {} workspace", i), - }; - let _ = server.create_workspace(params).await; - } - - let read_params = WorkspaceId::new(None); - let workspaces = server.read_workspaces(read_params).await; - assert_eq!(workspaces.len(), 3); -} - -#[actix_rt::test] -async fn doc_read() { - let test = BackendViewTest::new().await; - let params = DocumentId { - doc_id: test.view.id.clone(), - }; - let doc = test.server.read_doc(params).await; - assert_eq!(doc.is_some(), true); -} - -#[actix_rt::test] -async fn doc_create() { - let mut revisions: Vec = vec![]; - let server = TestUserServer::new().await; - let doc_id = uuid::Uuid::new_v4().to_string(); - let user_id = "a".to_owned(); - let mut document = ClientDocument::new::(); - let mut offset = 0; - for i in 0..5 { - let content = i.to_string(); - let delta = document.insert(offset, content.clone()).unwrap(); - offset += content.len(); - let bytes = delta.to_bytes(); - let md5 = md5(&bytes); - let revision = if i == 0 { - Revision::new(&doc_id, i, i, bytes, &user_id, md5) - } else { - Revision::new(&doc_id, i - 1, i, bytes, &user_id, md5) - }; - revisions.push(revision); - } - - let params = CreateDocParams { - id: doc_id.clone(), - revisions: RepeatedRevision::new(revisions), - }; - server.create_doc(params).await; - - let doc = server.read_doc(DocumentId { doc_id }).await; - assert_eq!(doc.unwrap().text, document.to_json()); -} - -#[actix_rt::test] -async fn doc_delete() { - let test = BackendViewTest::new().await; - let delete_params = RepeatedViewId { - items: vec![test.view.id.clone()], - }; - test.server.delete_view(delete_params).await; - - let params = DocumentId { - doc_id: test.view.id.clone(), - }; - let doc = test.server.read_doc(params).await; - assert_eq!(doc.is_none(), true); -} diff --git a/backend/tests/document_test/edit_script.rs b/backend/tests/document_test/edit_script.rs deleted file mode 100644 index 7bf77a0eea..0000000000 --- a/backend/tests/document_test/edit_script.rs +++ /dev/null @@ -1,179 +0,0 @@ -#![allow(clippy::all)] -#![cfg_attr(rustfmt, rustfmt::skip)] -use std::convert::TryInto; -use actix_web::web::Data; -use flowy_document::core::ClientDocumentEditor; -use flowy_test::{helper::ViewTest, FlowySDKTest}; -use flowy_user::services::UserSession; -use futures_util::{stream, stream::StreamExt}; -use std::sync::Arc; -use bytes::Bytes; -use tokio::time::{sleep, Duration}; -use crate::util::helper::{spawn_server, TestServer}; -use flowy_collaboration::{entities::document_info::DocumentId, protobuf::ResetDocumentParams as ResetDocumentParamsPB}; -use lib_ot::rich_text::{RichTextAttribute, RichTextDelta}; -use parking_lot::RwLock; -use backend::services::document::persistence::{read_document, reset_document}; -use flowy_collaboration::entities::revision::{RepeatedRevision, Revision}; -use flowy_collaboration::protobuf::{RepeatedRevision as RepeatedRevisionPB, DocumentId as DocumentIdPB}; -use flowy_collaboration::server_document::ServerDocumentManager; -use flowy_net::ws::connection::FlowyWebSocketConnect; -use lib_ot::core::Interval; - -pub struct DocumentTest { - server: TestServer, - flowy_test: FlowySDKTest, -} -#[derive(Clone)] -pub enum DocScript { - ClientInsertText(usize, &'static str), - ClientFormatText(Interval, RichTextAttribute), - ClientOpenDoc, - AssertClient(&'static str), - AssertServer(&'static str, i64), - ServerResetDocument(String, i64), // delta_json, rev_id -} - -impl DocumentTest { - pub async fn new() -> Self { - let server = spawn_server().await; - let flowy_test = FlowySDKTest::new(server.client_server_config.clone()); - Self { server, flowy_test } - } - - pub async fn run_scripts(self, scripts: Vec) { - let _ = self.flowy_test.sign_up().await; - let DocumentTest { server, flowy_test } = self; - let script_context = Arc::new(RwLock::new(ScriptContext::new(flowy_test, server).await)); - run_scripts(script_context, scripts).await; - sleep(Duration::from_secs(5)).await; - } -} - -#[derive(Clone)] -struct ScriptContext { - client_editor: Option>, - client_sdk: FlowySDKTest, - client_user_session: Arc, - #[allow(dead_code)] - ws_conn: Arc, - server: TestServer, - doc_id: String, -} - -impl ScriptContext { - async fn new(client_sdk: FlowySDKTest, server: TestServer) -> Self { - let user_session = client_sdk.user_session.clone(); - let ws_conn = client_sdk.ws_conn.clone(); - let doc_id = create_doc(&client_sdk).await; - - Self { - client_editor: None, - client_sdk, - client_user_session: user_session, - ws_conn, - server, - doc_id, - } - } - - async fn open_doc(&mut self) { - let doc_id = self.doc_id.clone(); - let edit_context = self.client_sdk.document_manager.open_document(doc_id).await.unwrap(); - self.client_editor = Some(edit_context); - } - - fn client_editor(&self) -> Arc { self.client_editor.as_ref().unwrap().clone() } -} - -async fn run_scripts(context: Arc>, scripts: Vec) { - let mut fut_scripts = vec![]; - for script in scripts { - let context = context.clone(); - let fut = async move { - let doc_id = context.read().doc_id.clone(); - match script { - DocScript::ClientOpenDoc => { - context.write().open_doc().await; - }, - DocScript::ClientInsertText(index, s) => { - context.read().client_editor().insert(index, s).await.unwrap(); - }, - DocScript::ClientFormatText(interval, attribute) => { - context - .read() - .client_editor() - .format(interval, attribute) - .await - .unwrap(); - }, - DocScript::AssertClient(s) => { - sleep(Duration::from_millis(2000)).await; - let json = context.read().client_editor().doc_json().await.unwrap(); - assert_eq(s, &json); - }, - DocScript::AssertServer(s, rev_id) => { - sleep(Duration::from_millis(2000)).await; - let persistence = Data::new(context.read().server.app_ctx.persistence.document_kv_store()); - let doc_identifier: DocumentIdPB = DocumentId { - doc_id - }.try_into().unwrap(); - - let document_info = read_document(persistence.get_ref(), doc_identifier).await.unwrap(); - assert_eq(s, &document_info.text); - assert_eq!(document_info.rev_id, rev_id); - }, - DocScript::ServerResetDocument(document_json, rev_id) => { - let delta_data = Bytes::from(document_json); - let user_id = context.read().client_user_session.user_id().unwrap(); - let md5 = format!("{:x}", md5::compute(&delta_data)); - let base_rev_id = if rev_id == 0 { rev_id } else { rev_id - 1 }; - let revision = Revision::new( - &doc_id, - base_rev_id, - rev_id, - delta_data, - &user_id, - md5, - ); - - let document_manager = context.read().server.app_ctx.document_manager.clone(); - reset_doc(&doc_id, RepeatedRevision::new(vec![revision]), document_manager.get_ref()).await; - sleep(Duration::from_millis(2000)).await; - }, - } - }; - fut_scripts.push(fut); - } - - let mut stream = stream::iter(fut_scripts); - while let Some(script) = stream.next().await { - let _ = script.await; - } - - std::mem::forget(context); -} - -fn assert_eq(expect: &str, receive: &str) { - let expected_delta: RichTextDelta = serde_json::from_str(expect).unwrap(); - let target_delta: RichTextDelta = serde_json::from_str(receive).unwrap(); - - if expected_delta != target_delta { - log::error!("✅ expect: {}", expect,); - log::error!("❌ receive: {}", receive); - } - assert_eq!(target_delta, expected_delta); -} - -async fn create_doc(flowy_test: &FlowySDKTest) -> String { - let view_test = ViewTest::new(flowy_test).await; - view_test.view.id -} - -async fn reset_doc(doc_id: &str, repeated_revision: RepeatedRevision, document_manager: &Arc) { - let pb: RepeatedRevisionPB = repeated_revision.try_into().unwrap(); - let mut params = ResetDocumentParamsPB::new(); - params.set_doc_id(doc_id.to_owned()); - params.set_revisions(pb); - let _ = reset_document(document_manager, params).await.unwrap(); -} diff --git a/backend/tests/document_test/edit_test.rs b/backend/tests/document_test/edit_test.rs deleted file mode 100644 index 0aff223881..0000000000 --- a/backend/tests/document_test/edit_test.rs +++ /dev/null @@ -1,207 +0,0 @@ -use crate::document_test::edit_script::{DocScript, DocumentTest}; -use flowy_collaboration::client_document::{ClientDocument, NewlineDoc}; -use lib_ot::{core::Interval, rich_text::RichTextAttribute}; - -#[rustfmt::skip] -// ┌─────────┐ ┌─────────┐ -// │ Server │ │ Client │ -// └─────────┘ └─────────┘ -// ┌────────────────┐ │ │ ┌────────────────┐ -// │ops: [] rev: 0 │◀┼──── Ping ─────┼─┤ops: [] rev: 0 │ -// └────────────────┘ │ │ └────────────────┘ -// ┌────────────────────┐ │ │ ┌────────────────────┐ -// │ops: ["abc"] rev: 1 │◀┼───ClientPush ───┼─│ops: ["abc"] rev: 1 │ -// └────────────────────┘ │ │ └────────────────────┘ -// ┌──────────────────────────┐ │ │ ┌──────────────────────┐ -// │ops: ["abc", "123"] rev: 2│◀┼── ClientPush ───┼─│ops: ["123"] rev: 2 │ -// └──────────────────────────┘ │ │ └──────────────────────┘ -// │ │ -#[actix_rt::test] -async fn delta_sync_while_editing() { - let test = DocumentTest::new().await; - test.run_scripts(vec![ - DocScript::ClientOpenDoc, - DocScript::ClientInsertText(0, "abc"), - DocScript::ClientInsertText(3, "123"), - DocScript::AssertClient(r#"[{"insert":"abc123\n"}]"#), - DocScript::AssertServer(r#"[{"insert":"abc123\n"}]"#, 1), - ]) - .await; -} - -#[actix_rt::test] -async fn delta_sync_multi_revs() { - let test = DocumentTest::new().await; - test.run_scripts(vec![ - DocScript::ClientOpenDoc, - DocScript::ClientInsertText(0, "abc"), - DocScript::ClientInsertText(3, "123"), - DocScript::ClientInsertText(6, "efg"), - DocScript::ClientInsertText(9, "456"), - ]) - .await; -} - -#[actix_rt::test] -async fn delta_sync_while_editing_with_attribute() { - let test = DocumentTest::new().await; - test.run_scripts(vec![ - DocScript::ClientOpenDoc, - DocScript::ClientInsertText(0, "abc"), - DocScript::ClientFormatText(Interval::new(0, 3), RichTextAttribute::Bold(true)), - DocScript::AssertClient(r#"[{"insert":"abc","attributes":{"bold":true}},{"insert":"\n"}]"#), - DocScript::AssertServer(r#"[{"insert":"abc","attributes":{"bold":true}},{"insert":"\n"}]"#, 1), - DocScript::ClientInsertText(3, "efg"), - DocScript::ClientFormatText(Interval::new(3, 5), RichTextAttribute::Italic(true)), - DocScript::AssertClient(r#"[{"insert":"abc","attributes":{"bold":true}},{"insert":"ef","attributes":{"bold":true,"italic":true}},{"insert":"g","attributes":{"bold":true}},{"insert":"\n"}]"#), - DocScript::AssertServer(r#"[{"insert":"abc","attributes":{"bold":true}},{"insert":"ef","attributes":{"bold":true,"italic":true}},{"insert":"g","attributes":{"bold":true}},{"insert":"\n"}]"#, 3), - ]) - .await; -} - -#[rustfmt::skip] -// ┌─────────┐ ┌─────────┐ -// │ Server │ │ Client │ -// └─────────┘ └─────────┘ -// ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ │ -// ops: ["123", "456"] rev: 3│ │ │ -// └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ │ -// │ │ -// ◀───── Ping ───┤ Open doc -// │ │ -// │ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ -// ├───ServerPush────┼─▶ ops: ["123", "456"] rev: 3│ -// │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ -#[actix_rt::test] -async fn delta_sync_with_server_push() { - let test = DocumentTest::new().await; - let mut document = ClientDocument::new::(); - document.insert(0, "123").unwrap(); - document.insert(3, "456").unwrap(); - let json = document.to_json(); - - test.run_scripts(vec![ - DocScript::ServerResetDocument(json, 3), - DocScript::ClientOpenDoc, - DocScript::AssertClient(r#"[{"insert":"123456\n"}]"#), - DocScript::AssertServer(r#"[{"insert":"123456\n"}]"#, 3), - ]) - .await; -} - -#[rustfmt::skip] -// ┌─────────┐ ┌─────────┐ -// │ Server │ │ Client │ -// └─────────┘ └─────────┘ -// ┌ ─ ─ ─ ─ ┐ │ │ -// ops: [] │ │ -// └ ─ ─ ─ ─ ┘ │ │ -// │ │ -// ◀───── Ping ───┤ Open doc -// ◀───── Ping ───┤ -// ◀───── Ping ───┤ -// ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ │ -// ops: ["123"], rev: 3 │ │ -// └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ -// ├────ServerPush───▶ ops: ["123"] rev: 3 -// │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ -// │ │ -#[actix_rt::test] -async fn delta_sync_with_server_push_after_reset_document() { - let test = DocumentTest::new().await; - let mut document = ClientDocument::new::(); - document.insert(0, "123").unwrap(); - let json = document.to_json(); - - test.run_scripts(vec![ - DocScript::ClientOpenDoc, - DocScript::ServerResetDocument(json, 3), - DocScript::AssertClient(r#"[{"insert":"123\n"}]"#), - DocScript::AssertServer(r#"[{"insert":"123\n"}]"#, 3), - ]) - .await; -} - -#[rustfmt::skip] -// ┌─────────┐ ┌─────────┐ -// │ Server │ │ Client │ -// └─────────┘ └─────────┘ -// │ │ -// │ │ -// ◀────── Ping ─────┤ Open doc -// ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ │ -// ops: ["123"] rev: 3 │ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ -// └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ │ ops: ["abc"] rev: 1 │ -// │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ -// │ │ ┌────────────────────┐ -// ◀───ClientPush ───┤ │ops: ["abc"] rev: 1 │ -// ┌───────────────────┐ │ │ └────────────────────┘ -// │ops: ["123"] rev: 3│ ├────ServerPush───▶ transform -// └───────────────────┘ │ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ -// │ │ ops: ["abc", "123"] rev: 4│ -// │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ -// │ │ ┌────────────────────────────────┐ -// ◀────ClientPush───┤ │ops: ["retain 3","abc"] rev: 4 │ -// ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ │ └────────────────────────────────┘ -// ops: ["abc", "123"] rev: 4│ ├────ServerAck────▶ -// └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ │ -#[actix_rt::test] -async fn delta_sync_while_local_rev_less_than_server_rev() { - let test = DocumentTest::new().await; - let mut document = ClientDocument::new::(); - document.insert(0, "123").unwrap(); - let json = document.to_json(); - - test.run_scripts(vec![ - DocScript::ClientOpenDoc, - DocScript::ServerResetDocument(json, 3), - DocScript::ClientInsertText(0, "abc"), - DocScript::AssertClient(r#"[{"insert":"abc123\n"}]"#), - DocScript::AssertServer(r#"[{"insert":"abc123\n"}]"#, 4), - ]) - .await; -} - -#[rustfmt::skip] -// ┌─────────┐ ┌─────────┐ -// │ Server │ │ Client │ -// └─────────┘ └─────────┘ -// ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ │ -// ops: ["123"] rev: 1 │ │ -// └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ │ -// ◀──── Ping ────┤ Open doc -// │ │ -// │ │ ┌──────────────────┐ -// ├───ServerPush────▶ │ops: [123] rev: 1 │ -// │ │ └──────────────────┘ -// │ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ -// │ │ ops: ["123","abc", "efg"] rev: 3 │ -// │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ -// │ │ ┌──────────────────────────────┐ -// ◀────ClientPush───┤ │ops: [retain 3, "abc"] rev: 2 │ -// ┌──────────────────────────┐ │ │ └──────────────────────────────┘ -// │ops: ["123","abc"] rev: 2 │ ├────ServerAck────▶ -// └──────────────────────────┘ │ │ -// │ │ ┌──────────────────────────────┐ -// ◀────ClientPush───┤ │ops: [retain 6, "efg"] rev: 3 │ -// ┌──────────────────────────────────┐ │ │ └──────────────────────────────┘ -// │ops: ["123","abc", "efg"] rev: 3 │ ├────ServerAck────▶ -// └──────────────────────────────────┘ │ │ -#[actix_rt::test] -async fn delta_sync_while_local_rev_greater_than_server_rev() { - let test = DocumentTest::new().await; - let mut document = ClientDocument::new::(); - document.insert(0, "123").unwrap(); - let json = document.to_json(); - - test.run_scripts(vec![ - DocScript::ServerResetDocument(json, 1), - DocScript::ClientOpenDoc, - DocScript::AssertClient(r#"[{"insert":"123\n"}]"#), - DocScript::ClientInsertText(3, "abc"), - DocScript::ClientInsertText(6, "efg"), - DocScript::AssertClient(r#"[{"insert":"123abcefg\n"}]"#), - DocScript::AssertServer(r#"[{"insert":"123abcefg\n"}]"#, 3), - ]) - .await; -} diff --git a/backend/tests/document_test/mod.rs b/backend/tests/document_test/mod.rs deleted file mode 100644 index 7ffb40d9b8..0000000000 --- a/backend/tests/document_test/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod edit_script; -mod edit_test; diff --git a/backend/tests/main.rs b/backend/tests/main.rs deleted file mode 100644 index eb6c8a9e40..0000000000 --- a/backend/tests/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod api_test; -mod document_test; -pub mod util; diff --git a/backend/tests/util/helper.rs b/backend/tests/util/helper.rs deleted file mode 100644 index cce94b54eb..0000000000 --- a/backend/tests/util/helper.rs +++ /dev/null @@ -1,388 +0,0 @@ -use backend::{ - application::{init_app_context, Application}, - config::{get_configuration, DatabaseSettings}, - context::AppContext, -}; -use backend_service::{ - configuration::{get_client_server_configuration, ClientServerConfiguration}, - errors::ServerError, -}; -use flowy_collaboration::{ - client_document::default::initial_delta_string, - entities::document_info::{CreateDocParams, DocumentId, DocumentInfo}, -}; -use flowy_folder_data_model::entities::{app::*, trash::*, view::*, workspace::*}; -use flowy_net::http_server::{ - core::*, - document::{create_document_request, read_document_request}, - user::*, -}; -use flowy_user_data_model::entities::*; -use lib_infra::uuid_string; -use sqlx::{Connection, Executor, PgConnection, PgPool}; -use uuid::Uuid; - -pub struct TestUserServer { - pub inner: TestServer, - pub user_token: Option, - pub user_id: Option, -} - -impl TestUserServer { - pub async fn new() -> Self { - let mut server: TestUserServer = spawn_server().await.into(); - let response = server.register_user().await; - server.user_token = Some(response.token); - server.user_id = Some(response.user_id); - server - } - - pub async fn sign_in(&self, params: SignInParams) -> Result { - let url = format!("{}/api/auth", self.http_addr()); - let resp = user_sign_in_request(params, &url).await?; - Ok(resp) - } - - pub async fn sign_out(&self) { - let url = format!("{}/api/auth", self.http_addr()); - let _ = user_sign_out_request(self.user_token(), &url).await.unwrap(); - } - - pub fn user_token(&self) -> &str { - self.user_token.as_ref().expect("must call register_user first ") - } - - pub fn user_id(&self) -> &str { - self.user_id.as_ref().expect("must call register_user first ") - } - - pub async fn get_user_profile(&self) -> UserProfile { - let url = format!("{}/api/user", self.http_addr()); - let user_profile = get_user_profile_request(self.user_token(), &url).await.unwrap(); - user_profile - } - - pub async fn update_user_profile(&self, params: UpdateUserParams) -> Result<(), ServerError> { - let url = format!("{}/api/user", self.http_addr()); - let _ = update_user_profile_request(self.user_token(), params, &url).await?; - Ok(()) - } - - pub async fn create_workspace(&self, params: CreateWorkspaceParams) -> Workspace { - let url = format!("{}/api/workspace", self.http_addr()); - let workspace = create_workspace_request(self.user_token(), params, &url).await.unwrap(); - workspace - } - - pub async fn read_workspaces(&self, params: WorkspaceId) -> RepeatedWorkspace { - let url = format!("{}/api/workspace", self.http_addr()); - let workspaces = read_workspaces_request(self.user_token(), params, &url).await.unwrap(); - workspaces - } - - pub async fn update_workspace(&self, params: UpdateWorkspaceParams) { - let url = format!("{}/api/workspace", self.http_addr()); - update_workspace_request(self.user_token(), params, &url).await.unwrap(); - } - - pub async fn delete_workspace(&self, params: WorkspaceId) { - let url = format!("{}/api/workspace", self.http_addr()); - delete_workspace_request(self.user_token(), params, &url).await.unwrap(); - } - - pub async fn create_app(&self, params: CreateAppParams) -> App { - let url = format!("{}/api/app", self.http_addr()); - let app = create_app_request(self.user_token(), params, &url).await.unwrap(); - app - } - - pub async fn read_app(&self, params: AppId) -> Option { - let url = format!("{}/api/app", self.http_addr()); - let app = read_app_request(self.user_token(), params, &url).await.unwrap(); - app - } - - pub async fn update_app(&self, params: UpdateAppParams) { - let url = format!("{}/api/app", self.http_addr()); - update_app_request(self.user_token(), params, &url).await.unwrap(); - } - - pub async fn delete_app(&self, params: AppId) { - let url = format!("{}/api/app", self.http_addr()); - delete_app_request(self.user_token(), params, &url).await.unwrap(); - } - - pub async fn create_view(&self, params: CreateViewParams) -> View { - let url = format!("{}/api/view", self.http_addr()); - let view = create_view_request(self.user_token(), params, &url).await.unwrap(); - view - } - - pub async fn read_view(&self, params: ViewId) -> Option { - let url = format!("{}/api/view", self.http_addr()); - let view = read_view_request(self.user_token(), params, &url).await.unwrap(); - view - } - - pub async fn update_view(&self, params: UpdateViewParams) { - let url = format!("{}/api/view", self.http_addr()); - update_view_request(self.user_token(), params, &url).await.unwrap(); - } - - pub async fn delete_view(&self, params: RepeatedViewId) { - let url = format!("{}/api/view", self.http_addr()); - delete_view_request(self.user_token(), params, &url).await.unwrap(); - } - - pub async fn create_view_trash(&self, view_id: &str) { - let identifier = TrashId { - id: view_id.to_string(), - ty: TrashType::View, - }; - let url = format!("{}/api/trash", self.http_addr()); - create_trash_request(self.user_token(), vec![identifier].into(), &url) - .await - .unwrap(); - } - - pub async fn delete_view_trash(&self, trash_identifiers: RepeatedTrashId) { - let url = format!("{}/api/trash", self.http_addr()); - - delete_trash_request(self.user_token(), trash_identifiers, &url) - .await - .unwrap(); - } - - pub async fn read_trash(&self) -> RepeatedTrash { - let url = format!("{}/api/trash", self.http_addr()); - read_trash_request(self.user_token(), &url).await.unwrap() - } - - pub async fn read_doc(&self, params: DocumentId) -> Option { - let url = format!("{}/api/doc", self.http_addr()); - let doc = read_document_request(self.user_token(), params, &url).await.unwrap(); - doc - } - - pub async fn create_doc(&self, params: CreateDocParams) { - let url = format!("{}/api/doc", self.http_addr()); - let _ = create_document_request(self.user_token(), params, &url).await.unwrap(); - } - - pub async fn register_user(&self) -> SignUpResponse { - let params = SignUpParams { - email: "annie@appflowy.io".to_string(), - name: "annie".to_string(), - password: "HelloAppFlowy123!".to_string(), - }; - - self.register(params).await - } - - pub async fn register(&self, params: SignUpParams) -> SignUpResponse { - let url = format!("{}/api/register", self.http_addr()); - let response = user_sign_up_request(params, &url).await.unwrap(); - response - } - - pub fn http_addr(&self) -> String { - self.inner.client_server_config.base_url() - } - - pub fn ws_addr(&self) -> String { - format!( - "{}/{}", - self.inner.client_server_config.ws_addr(), - self.user_token.as_ref().unwrap() - ) - } -} - -impl std::convert::From for TestUserServer { - fn from(server: TestServer) -> Self { - TestUserServer { - inner: server, - user_token: None, - user_id: None, - } - } -} - -pub async fn spawn_user_server() -> TestUserServer { - let server: TestUserServer = spawn_server().await.into(); - server -} - -#[derive(Clone)] -pub struct TestServer { - pub app_ctx: AppContext, - pub client_server_config: ClientServerConfiguration, -} - -pub async fn spawn_server() -> TestServer { - let database_name = Uuid::new_v4().to_string(); - let configuration = { - let mut c = get_configuration().expect("Failed to read configuration."); - c.database.database_name = database_name.clone(); - // Use a random OS port - c.application.port = 0; - c - }; - - let _ = configure_database(&configuration.database).await; - let app_ctx = init_app_context(&configuration).await; - let application = Application::build(configuration.clone(), app_ctx.clone()) - .await - .expect("Failed to build application."); - let application_port = application.port(); - - let _ = tokio::spawn(async { - let _ = application.run_until_stopped(); - // drop_test_database(database_name).await; - }); - - let mut client_server_config = get_client_server_configuration().expect("Failed to read configuration."); - client_server_config.reset_host_with_port("localhost", application_port); - - TestServer { - app_ctx, - client_server_config, - } -} - -async fn configure_database(config: &DatabaseSettings) -> PgPool { - // Create database - let mut connection = PgConnection::connect_with(&config.without_db()) - .await - .expect("Failed to connect to Postgres"); - connection - .execute(&*format!(r#"CREATE DATABASE "{}";"#, config.database_name)) - .await - .expect("Failed to create database."); - - // Migrate database - let connection_pool = PgPool::connect_with(config.with_db()) - .await - .expect("Failed to connect to Postgres."); - - sqlx::migrate!("./migrations") - .run(&connection_pool) - .await - .expect("Failed to migrate the database"); - - connection_pool -} - -#[allow(dead_code)] -async fn drop_test_database(database_name: String) { - // https://stackoverflow.com/questions/36502401/postgres-drop-database-error-pq-cannot-drop-the-currently-open-database?rq=1 - let configuration = { - let mut c = get_configuration().expect("Failed to read configuration."); - c.database.database_name = "flowy".to_owned(); - c.application.port = 0; - c - }; - - let mut connection = PgConnection::connect_with(&configuration.database.without_db()) - .await - .expect("Failed to connect to Postgres"); - - connection - .execute(&*format!(r#"Drop DATABASE "{}";"#, database_name)) - .await - .expect("Failed to drop database."); -} - -pub async fn create_test_workspace(server: &TestUserServer) -> Workspace { - let params = CreateWorkspaceParams { - name: "My first workspace".to_string(), - desc: "This is my first workspace".to_string(), - }; - - let workspace = server.create_workspace(params).await; - workspace -} - -pub async fn create_test_app(server: &TestUserServer, workspace_id: &str) -> App { - let params = CreateAppParams { - workspace_id: workspace_id.to_owned(), - name: "My first app".to_string(), - desc: "This is my first app".to_string(), - color_style: ColorStyle::default(), - }; - - let app = server.create_app(params).await; - app -} - -pub async fn create_test_view(application: &TestUserServer, app_id: &str) -> View { - let name = "My first view".to_string(); - let desc = "This is my first view".to_string(); - let thumbnail = "http://1.png".to_string(); - - let params = CreateViewParams::new( - app_id.to_owned(), - name, - desc, - ViewType::Doc, - thumbnail, - initial_delta_string(), - uuid_string(), - ); - let app = application.create_view(params).await; - app -} - -pub struct BackendWorkspaceTest { - pub server: TestUserServer, - pub workspace: Workspace, -} - -impl BackendWorkspaceTest { - pub async fn new() -> Self { - let server = TestUserServer::new().await; - let workspace = create_test_workspace(&server).await; - Self { server, workspace } - } - - pub async fn create_app(&self) -> App { - create_test_app(&self.server, &self.workspace.id).await - } -} - -pub struct BackendAppTest { - pub server: TestUserServer, - pub workspace: Workspace, - pub app: App, -} - -impl BackendAppTest { - pub async fn new() -> Self { - let server = TestUserServer::new().await; - let workspace = create_test_workspace(&server).await; - let app = create_test_app(&server, &workspace.id).await; - Self { server, workspace, app } - } -} - -pub struct BackendViewTest { - pub server: TestUserServer, - pub workspace: Workspace, - pub app: App, - pub view: View, -} - -impl BackendViewTest { - pub async fn new() -> Self { - let server = TestUserServer::new().await; - let workspace = create_test_workspace(&server).await; - let app = create_test_app(&server, &workspace.id).await; - let view = create_test_view(&server, &app.id).await; - Self { - server, - workspace, - app, - view, - } - } -} diff --git a/backend/tests/util/mod.rs b/backend/tests/util/mod.rs deleted file mode 100644 index 485bb81a91..0000000000 --- a/backend/tests/util/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod helper; From 680d1309865faec3477ddabe2c37b15390d9b3b3 Mon Sep 17 00:00:00 2001 From: appflowy Date: Mon, 7 Feb 2022 10:37:01 +0800 Subject: [PATCH 5/9] refactor: remove unuse crate references --- frontend/Makefile.toml | 6 ------ frontend/rust-lib/dart-ffi/Cargo.toml | 3 --- frontend/rust-lib/dart-ffi/src/lib.rs | 5 ++--- frontend/rust-lib/flowy-net/src/http_server/core.rs | 8 ++++---- frontend/rust-lib/flowy-net/src/lib.rs | 2 ++ frontend/rust-lib/flowy-sdk/Cargo.toml | 1 - .../flowy-sdk/src/deps_resolve/document_deps.rs | 2 +- .../flowy-sdk/src/deps_resolve/folder_deps.rs | 6 +++--- .../rust-lib/flowy-sdk/src/deps_resolve/user_deps.rs | 2 +- frontend/rust-lib/flowy-sdk/src/lib.rs | 7 ++++--- frontend/rust-lib/flowy-test/Cargo.toml | 1 - frontend/rust-lib/flowy-test/src/lib.rs | 2 +- shared-lib/Cargo.lock | 1 - shared-lib/lib-ws/Cargo.toml | 1 - shared-lib/lib-ws/src/errors.rs | 7 +++++++ shared-lib/lib-ws/src/protobuf/model/document | 8 -------- shared-lib/lib-ws/src/ws.rs | 11 +++++------ 17 files changed, 30 insertions(+), 43 deletions(-) delete mode 100644 shared-lib/lib-ws/src/protobuf/model/document diff --git a/frontend/Makefile.toml b/frontend/Makefile.toml index 550575819f..48433548c2 100644 --- a/frontend/Makefile.toml +++ b/frontend/Makefile.toml @@ -115,12 +115,6 @@ script = [ echo PRODUCT_EXT: ${PRODUCT_EXT} echo APP_ENVIRONMENT: ${APP_ENVIRONMENT} echo ${platforms} - - echo "-------- Flutter --------" - flutter doctor - - echo "-------- Rust --------" - rustup show ''' ] script_runner = "@shell" diff --git a/frontend/rust-lib/dart-ffi/Cargo.toml b/frontend/rust-lib/dart-ffi/Cargo.toml index 1db8a03325..6787a98d26 100644 --- a/frontend/rust-lib/dart-ffi/Cargo.toml +++ b/frontend/rust-lib/dart-ffi/Cargo.toml @@ -26,10 +26,7 @@ once_cell = "1" lib-dispatch = {path = "../lib-dispatch" } flowy-sdk = {path = "../flowy-sdk"} dart-notify = {path = "../dart-notify" } - flowy-derive = {path = "../../../shared-lib/flowy-derive" } -backend-service = { path = "../../../shared-lib/backend-service" } - [features] flutter = ["dart-notify/dart"] diff --git a/frontend/rust-lib/dart-ffi/src/lib.rs b/frontend/rust-lib/dart-ffi/src/lib.rs index 027ddf5af2..24a87b1519 100644 --- a/frontend/rust-lib/dart-ffi/src/lib.rs +++ b/frontend/rust-lib/dart-ffi/src/lib.rs @@ -8,7 +8,9 @@ use crate::{ c::{extend_front_four_bytes_into_bytes, forget_rust}, model::{FFIRequest, FFIResponse}, }; +use flowy_sdk::get_client_server_configuration; use flowy_sdk::*; +use lib_dispatch::prelude::ToBytes; use lib_dispatch::prelude::*; use once_cell::sync::OnceCell; use std::{ffi::CStr, os::raw::c_char}; @@ -80,9 +82,6 @@ pub extern "C" fn set_stream_port(port: i64) -> i32 { #[no_mangle] pub extern "C" fn link_me_please() {} -use backend_service::configuration::get_client_server_configuration; -use lib_dispatch::prelude::ToBytes; - #[inline(always)] async fn post_to_flutter(response: EventResponse, port: i64) { let isolate = allo_isolate::Isolate::new(port); diff --git a/frontend/rust-lib/flowy-net/src/http_server/core.rs b/frontend/rust-lib/flowy-net/src/http_server/core.rs index a488d630ed..aa5e56a641 100644 --- a/frontend/rust-lib/flowy-net/src/http_server/core.rs +++ b/frontend/rust-lib/flowy-net/src/http_server/core.rs @@ -18,17 +18,17 @@ use lib_infra::future::FutureResult; use std::sync::Arc; use tokio::sync::broadcast; -pub struct CoreHttpCloudService { +pub struct FolderHttpCloudService { config: ClientServerConfiguration, } -impl CoreHttpCloudService { - pub fn new(config: ClientServerConfiguration) -> CoreHttpCloudService { +impl FolderHttpCloudService { + pub fn new(config: ClientServerConfiguration) -> FolderHttpCloudService { Self { config } } } -impl FolderCouldServiceV1 for CoreHttpCloudService { +impl FolderCouldServiceV1 for FolderHttpCloudService { fn init(&self) {} fn create_workspace(&self, token: &str, params: CreateWorkspaceParams) -> FutureResult { diff --git a/frontend/rust-lib/flowy-net/src/lib.rs b/frontend/rust-lib/flowy-net/src/lib.rs index f4c0be577d..9b646d6f5a 100644 --- a/frontend/rust-lib/flowy-net/src/lib.rs +++ b/frontend/rust-lib/flowy-net/src/lib.rs @@ -6,3 +6,5 @@ pub mod local_server; pub mod module; pub mod protobuf; pub mod ws; + +pub use backend_service::configuration::{get_client_server_configuration, ClientServerConfiguration}; diff --git a/frontend/rust-lib/flowy-sdk/Cargo.toml b/frontend/rust-lib/flowy-sdk/Cargo.toml index 4f8b69bdb0..0afae1e70e 100644 --- a/frontend/rust-lib/flowy-sdk/Cargo.toml +++ b/frontend/rust-lib/flowy-sdk/Cargo.toml @@ -26,7 +26,6 @@ parking_lot = "0.11" flowy-collaboration = { path = "../../../shared-lib/flowy-collaboration" } lib-ws = { path = "../../../shared-lib/lib-ws" } -backend-service = { path = "../../../shared-lib/backend-service" } lib-infra = { path = "../../../shared-lib/lib-infra" } [dev-dependencies] diff --git a/frontend/rust-lib/flowy-sdk/src/deps_resolve/document_deps.rs b/frontend/rust-lib/flowy-sdk/src/deps_resolve/document_deps.rs index 1bbc3701a1..453bc50435 100644 --- a/frontend/rust-lib/flowy-sdk/src/deps_resolve/document_deps.rs +++ b/frontend/rust-lib/flowy-sdk/src/deps_resolve/document_deps.rs @@ -1,4 +1,3 @@ -use backend_service::configuration::ClientServerConfiguration; use bytes::Bytes; use flowy_collaboration::entities::ws_data::ClientRevisionWSData; use flowy_database::ConnectionPool; @@ -6,6 +5,7 @@ use flowy_document::{ errors::{internal_error, FlowyError}, DocumentCloudService, DocumentUser, FlowyDocumentManager, }; +use flowy_net::ClientServerConfiguration; use flowy_net::{ http_server::document::DocumentHttpCloudService, local_server::LocalServer, ws::connection::FlowyWebSocketConnect, }; diff --git a/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs b/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs index 1d32ae8629..8e23f64863 100644 --- a/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs +++ b/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs @@ -1,4 +1,3 @@ -use backend_service::configuration::ClientServerConfiguration; use bytes::Bytes; use flowy_collaboration::entities::ws_data::ClientRevisionWSData; use flowy_database::ConnectionPool; @@ -8,8 +7,9 @@ use flowy_folder::{ errors::{internal_error, FlowyError}, event_map::{FolderCouldServiceV1, WorkspaceDatabase, WorkspaceUser}, }; +use flowy_net::ClientServerConfiguration; use flowy_net::{ - http_server::core::CoreHttpCloudService, local_server::LocalServer, ws::connection::FlowyWebSocketConnect, + http_server::core::FolderHttpCloudService, local_server::LocalServer, ws::connection::FlowyWebSocketConnect, }; use flowy_sync::{RevisionWebSocket, WSStateReceiver}; use flowy_user::services::UserSession; @@ -31,7 +31,7 @@ impl FolderDepsResolver { let database: Arc = Arc::new(WorkspaceDatabaseImpl(user_session)); let web_socket = Arc::new(FolderWebSocketImpl(ws_conn.clone())); let cloud_service: Arc = match local_server { - None => Arc::new(CoreHttpCloudService::new(server_config.clone())), + None => Arc::new(FolderHttpCloudService::new(server_config.clone())), Some(local_server) => local_server, }; diff --git a/frontend/rust-lib/flowy-sdk/src/deps_resolve/user_deps.rs b/frontend/rust-lib/flowy-sdk/src/deps_resolve/user_deps.rs index 59890bd0eb..7115c5a2bb 100644 --- a/frontend/rust-lib/flowy-sdk/src/deps_resolve/user_deps.rs +++ b/frontend/rust-lib/flowy-sdk/src/deps_resolve/user_deps.rs @@ -1,4 +1,4 @@ -use backend_service::configuration::ClientServerConfiguration; +use flowy_net::ClientServerConfiguration; use flowy_net::{http_server::user::UserHttpCloudService, local_server::LocalServer}; use flowy_user::event_map::UserCloudService; diff --git a/frontend/rust-lib/flowy-sdk/src/lib.rs b/frontend/rust-lib/flowy-sdk/src/lib.rs index 03c723db27..7e3c0da37b 100644 --- a/frontend/rust-lib/flowy-sdk/src/lib.rs +++ b/frontend/rust-lib/flowy-sdk/src/lib.rs @@ -1,8 +1,11 @@ mod deps_resolve; pub mod module; +pub use flowy_net::get_client_server_configuration; + use crate::deps_resolve::*; -use backend_service::configuration::ClientServerConfiguration; +use flowy_document::FlowyDocumentManager; use flowy_folder::{controller::FolderManager, errors::FlowyError}; +use flowy_net::ClientServerConfiguration; use flowy_net::{ entities::NetworkType, local_server::LocalServer, @@ -10,8 +13,6 @@ use flowy_net::{ }; use flowy_user::services::{notifier::UserStatus, UserSession, UserSessionConfig}; use lib_dispatch::prelude::*; - -use flowy_document::FlowyDocumentManager; use lib_dispatch::util::tokio_default_runtime; use module::mk_modules; pub use module::*; diff --git a/frontend/rust-lib/flowy-test/Cargo.toml b/frontend/rust-lib/flowy-test/Cargo.toml index 2495da3c00..faf85b7c32 100644 --- a/frontend/rust-lib/flowy-test/Cargo.toml +++ b/frontend/rust-lib/flowy-test/Cargo.toml @@ -13,7 +13,6 @@ flowy-folder = { path = "../flowy-folder", default-features = false} lib-dispatch = { path = "../lib-dispatch" } flowy-collaboration = { path = "../../../shared-lib/flowy-collaboration" } -backend-service = { path = "../../../shared-lib/backend-service" } lib-ot = { path = "../../../shared-lib/lib-ot" } lib-infra = { path = "../../../shared-lib/lib-infra" } diff --git a/frontend/rust-lib/flowy-test/src/lib.rs b/frontend/rust-lib/flowy-test/src/lib.rs index c996cd294c..6d02f4f973 100644 --- a/frontend/rust-lib/flowy-test/src/lib.rs +++ b/frontend/rust-lib/flowy-test/src/lib.rs @@ -2,7 +2,7 @@ pub mod event_builder; pub mod helper; use crate::helper::*; -use backend_service::configuration::{get_client_server_configuration, ClientServerConfiguration}; +use flowy_net::{get_client_server_configuration, ClientServerConfiguration}; use flowy_sdk::{FlowySDK, FlowySDKConfig}; use flowy_user::entities::UserProfile; use lib_infra::uuid_string; diff --git a/shared-lib/Cargo.lock b/shared-lib/Cargo.lock index f67087619d..262980c653 100644 --- a/shared-lib/Cargo.lock +++ b/shared-lib/Cargo.lock @@ -1118,7 +1118,6 @@ dependencies = [ name = "lib-ws" version = "0.1.0" dependencies = [ - "backend-service", "bytes", "dashmap", "env_logger 0.8.4", diff --git a/shared-lib/lib-ws/Cargo.toml b/shared-lib/lib-ws/Cargo.toml index 1deef5e0a7..9094e3a334 100644 --- a/shared-lib/lib-ws/Cargo.toml +++ b/shared-lib/lib-ws/Cargo.toml @@ -7,7 +7,6 @@ edition = "2018" [dependencies] flowy-derive = { path = "../flowy-derive" } -backend-service = { path = "../backend-service" } lib-infra = { path = "../lib-infra" } tokio-tungstenite = "0.15" diff --git a/shared-lib/lib-ws/src/errors.rs b/shared-lib/lib-ws/src/errors.rs index e263706b44..318b8fa7de 100644 --- a/shared-lib/lib-ws/src/errors.rs +++ b/shared-lib/lib-ws/src/errors.rs @@ -2,6 +2,7 @@ use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; use futures_channel::mpsc::TrySendError; use std::fmt::Debug; use strum_macros::Display; +use tokio::sync::oneshot::error::RecvError; use tokio_tungstenite::tungstenite::{http::StatusCode, Message}; use url::ParseError; @@ -83,6 +84,12 @@ impl std::convert::From> for WSErro } } +impl std::convert::From for WSError { + fn from(error: RecvError) -> Self { + WSError::internal().context(error) + } +} + impl std::convert::From for WSError { fn from(error: tokio_tungstenite::tungstenite::Error) -> Self { match error { diff --git a/shared-lib/lib-ws/src/protobuf/model/document b/shared-lib/lib-ws/src/protobuf/model/document deleted file mode 100644 index 280e596bde..0000000000 --- a/shared-lib/lib-ws/src/protobuf/model/document +++ /dev/null @@ -1,8 +0,0 @@ -#![cfg_attr(rustfmt, rustfmt::skip)] -// Auto-generated, do not edit - -mod errors; -pub use errors::*; - -mod msg; -pub use msg::*; diff --git a/shared-lib/lib-ws/src/ws.rs b/shared-lib/lib-ws/src/ws.rs index 05e3f98b68..adbb49f9ac 100644 --- a/shared-lib/lib-ws/src/ws.rs +++ b/shared-lib/lib-ws/src/ws.rs @@ -4,7 +4,6 @@ use crate::{ errors::WSError, WSChannel, WebSocketRawMessage, }; -use backend_service::errors::ServerError; use bytes::Bytes; use dashmap::DashMap; use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; @@ -73,7 +72,7 @@ impl WSController { Ok(()) } - pub async fn start(&self, addr: String) -> Result<(), ServerError> { + pub async fn start(&self, addr: String) -> Result<(), WSError> { *self.addr.write().await = Some(addr.clone()); let strategy = FixedInterval::from_millis(5000).take(3); self.connect(addr, strategy).await @@ -89,7 +88,7 @@ impl WSController { } } - async fn connect(&self, addr: String, strategy: T) -> Result<(), ServerError> + async fn connect(&self, addr: String, strategy: T) -> Result<(), WSError> where T: IntoIterator, I: Iterator + Send + 'static, @@ -100,7 +99,7 @@ impl WSController { return Ok(()); } - let (ret, rx) = oneshot::channel::>(); + let (ret, rx) = oneshot::channel::>(); *self.addr.write().await = Some(addr.clone()); let action = WSConnectAction { addr, @@ -133,14 +132,14 @@ impl WSController { .write() .await .update_state(WSConnectState::Disconnected); - let _ = ret.send(Err(ServerError::internal().context(e))); + let _ = ret.send(Err(WSError::internal().context(e))); } } }); rx.await? } - pub async fn retry(&self, count: usize) -> Result<(), ServerError> { + pub async fn retry(&self, count: usize) -> Result<(), WSError> { if !self.conn_state_notify.read().await.conn_state.is_disconnected() { return Ok(()); } From 084e9c5f6f47ba1d28dfffabf9a780c2c681d7da Mon Sep 17 00:00:00 2001 From: appflowy Date: Mon, 7 Feb 2022 14:40:45 +0800 Subject: [PATCH 6/9] refactor: refactor some crates with http_server --- frontend/rust-lib/.gitignore | 1 - frontend/rust-lib/Cargo.lock | 3119 +++++++++++++++++ frontend/rust-lib/flowy-document/Cargo.toml | 2 +- frontend/rust-lib/flowy-error/Cargo.toml | 4 +- .../src/ext/{backend.rs => http_server.rs} | 2 +- frontend/rust-lib/flowy-error/src/ext/mod.rs | 8 +- frontend/rust-lib/flowy-folder/Cargo.toml | 2 +- frontend/rust-lib/flowy-net/Cargo.toml | 12 +- .../flowy-net}/configuration/base.yaml | 0 .../flowy-net}/configuration/local.yaml | 0 .../flowy-net}/configuration/production.yaml | 0 .../rust-lib/flowy-net}/src/configuration.rs | 2 +- .../flowy-net/src/http_server/document.rs | 4 +- .../src/http_server/{core.rs => folder.rs} | 16 +- .../rust-lib/flowy-net/src/http_server/mod.rs | 2 +- .../flowy-net/src/http_server/user.rs | 3 +- frontend/rust-lib/flowy-net/src/lib.rs | 4 +- .../flowy-net/src/local_server/mod.rs | 2 +- .../rust-lib/flowy-net/src}/request.rs | 11 +- .../flowy-sdk/src/deps_resolve/folder_deps.rs | 2 +- frontend/rust-lib/flowy-sync/Cargo.toml | 2 +- frontend/rust-lib/flowy-user/Cargo.toml | 2 +- frontend/scripts/makefile/desktop.toml | 1 + shared-lib/Cargo.lock | 1094 +----- shared-lib/Cargo.toml | 1 - shared-lib/backend-service/Cargo.toml | 33 - shared-lib/backend-service/src/errors.rs | 138 - shared-lib/backend-service/src/lib.rs | 4 - shared-lib/backend-service/src/middleware.rs | 41 - shared-lib/backend-service/src/request/mod.rs | 4 - .../backend-service/src/response/mod.rs | 7 - .../backend-service/src/response/response.rs | 100 - .../src/response/response_http.rs | 26 - 33 files changed, 3172 insertions(+), 1477 deletions(-) create mode 100755 frontend/rust-lib/Cargo.lock rename frontend/rust-lib/flowy-error/src/ext/{backend.rs => http_server.rs} (91%) rename {shared-lib/backend-service => frontend/rust-lib/flowy-net}/configuration/base.yaml (100%) rename {shared-lib/backend-service => frontend/rust-lib/flowy-net}/configuration/local.yaml (100%) rename {shared-lib/backend-service => frontend/rust-lib/flowy-net}/configuration/production.yaml (100%) rename {shared-lib/backend-service => frontend/rust-lib/flowy-net}/src/configuration.rs (98%) rename frontend/rust-lib/flowy-net/src/http_server/{core.rs => folder.rs} (96%) rename {shared-lib/backend-service/src/request => frontend/rust-lib/flowy-net/src}/request.rs (94%) delete mode 100644 shared-lib/backend-service/Cargo.toml delete mode 100644 shared-lib/backend-service/src/errors.rs delete mode 100644 shared-lib/backend-service/src/lib.rs delete mode 100644 shared-lib/backend-service/src/middleware.rs delete mode 100644 shared-lib/backend-service/src/request/mod.rs delete mode 100644 shared-lib/backend-service/src/response/mod.rs delete mode 100644 shared-lib/backend-service/src/response/response.rs delete mode 100644 shared-lib/backend-service/src/response/response_http.rs diff --git a/frontend/rust-lib/.gitignore b/frontend/rust-lib/.gitignore index a42aa3663e..6a492f9bfd 100644 --- a/frontend/rust-lib/.gitignore +++ b/frontend/rust-lib/.gitignore @@ -4,7 +4,6 @@ # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html -Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock new file mode 100755 index 0000000000..e7bf278535 --- /dev/null +++ b/frontend/rust-lib/Cargo.lock @@ -0,0 +1,3119 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "allo-isolate" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31644a919a9e4b0188e4569e55bbf5a78b5588ea645acffc15c29240407261bc" +dependencies = [ + "atomic", + "pin-project", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "async-stream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "171374e7e3b2504e0e5236e3b59260560f9fe94bfe9ac39ba5e4e929c5590625" +dependencies = [ + "async-stream-impl", + "futures-core", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "648ed8c8d2ce5409ccd57453d9d1b214b342a0d69376a6feda1fd6cae3299308" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-trait" +version = "0.1.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b88d82667eca772c4aa12f0f1348b3ae643424c8876448f3f7bd5787032e234c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "backtrace" +version = "0.3.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "321629d8ba6513061f26707241fa9bc89524ff1cd7a915a97ef0c62c666ce1b6" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bit-set" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" + +[[package]] +name = "bytecount" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" +dependencies = [ + "serde", +] + +[[package]] +name = "cast" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c24dab4283a142afa2fdca129b80ad2c6284e073930f964c3a1293c225ee39a" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "cc" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi", +] + +[[package]] +name = "claim" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ad37958d55b29a7088909368968d2fe876a24c203f8441195130f3b15194b9" +dependencies = [ + "autocfg", +] + +[[package]] +name = "claim" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81099d6bb72e1df6d50bb2347224b666a670912bb7f06dbe867a4a070ab3ce8" +dependencies = [ + "autocfg", +] + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "bitflags", + "textwrap", + "unicode-width", +] + +[[package]] +name = "color-eyre" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f1885697ee8a177096d42f158922251a41973117f6d8a234cee94b9509157b7" +dependencies = [ + "backtrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", +] + +[[package]] +name = "config" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b076e143e1d9538dde65da30f8481c2a6c44040edb8e02b9bf1351edb92ce3" +dependencies = [ + "lazy_static", + "nom", + "serde", + "yaml-rust", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "core-foundation" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "cpufeatures" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +dependencies = [ + "libc", +] + +[[package]] +name = "criterion" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1604dafd25fba2fe2d5895a9da139f8dc9b319a5fe5354ca137cbbce4e178d10" +dependencies = [ + "atty", + "cast", + "clap", + "criterion-plot", + "csv", + "itertools", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_cbor", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d00996de9f2f7559f7f4dc286073197f83e92256a59ed395f9aac01fe717da57" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae5588f6b3c3cb05239e90bd110f257254aecd01e4635400391aeae07497845" +dependencies = [ + "cfg-if", + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97242a70df9b89a65d0b6df3c4bf5b9ce03c5b7309019777fbde37e7537f8762" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b979d76c9fcb84dffc80a73f7290da0f83e4c95773494674cb44b76d13a7a110" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcae03edb34f947e64acdb1c33ec169824e20657e9ecb61cef6c8c74dcb8120" +dependencies = [ + "cfg-if", + "lazy_static", +] + +[[package]] +name = "csv" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" +dependencies = [ + "bstr", + "csv-core", + "itoa 0.4.8", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +dependencies = [ + "memchr", +] + +[[package]] +name = "darling" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0d720b8683f8dd83c65155f0530560cba68cd2bf395f6513a483caee57ff7f4" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a340f241d2ceed1deb47ae36c4144b2707ec7dd0b649f894cb39bb595986324" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c41b3b7352feb3211a0d743dc5700a4e3b60f51bd2b368892d1e0f9a95f44b" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "dart-ffi" +version = "0.1.0" +dependencies = [ + "allo-isolate", + "byteorder", + "bytes", + "dart-notify", + "ffi-support", + "flowy-derive", + "flowy-sdk", + "lib-dispatch", + "log", + "once_cell", + "protobuf", + "serde", + "serde_json", + "tokio", +] + +[[package]] +name = "dart-notify" +version = "0.1.0" +dependencies = [ + "allo-isolate", + "bytes", + "flowy-derive", + "lazy_static", + "lib-dispatch", + "log", + "protobuf", +] + +[[package]] +name = "dashmap" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" +dependencies = [ + "cfg-if", + "num_cpus", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", +] + +[[package]] +name = "diesel" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b28135ecf6b7d446b43e27e225622a038cc4e2930a1022f51cdb97ada19b8e4d" +dependencies = [ + "byteorder", + "diesel_derives", + "libsqlite3-sys", +] + +[[package]] +name = "diesel_derives" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "diesel_migrations" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf3cde8413353dc7f5d72fa8ce0b99a560a359d2c5ef1e5817ca731cd9008f4c" +dependencies = [ + "migrations_internals", + "migrations_macros", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "dissimilar" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31ad93652f40969dead8d4bf897a41e9462095152eb21c56e5830537e41179dd" + +[[package]] +name = "dyn-clone" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee2626afccd7561a06cf1367e2950c4718ea04565e20fb5029b6c7d8ad09abcf" + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "encoding_rs" +version = "0.8.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "error-chain" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e791d3be96241c77c43846b665ef1384606da2cd2a48730abe606a12906e02" +dependencies = [ + "backtrace", +] + +[[package]] +name = "error-code" +version = "0.1.0" +dependencies = [ + "derive_more", + "flowy-derive", + "protobuf", +] + +[[package]] +name = "eyre" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "221239d1d5ea86bf5d6f91c9d6bc3646ffe471b08ff9b0f91c44f115ac969d2b" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "fake" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6479fa2c7e83ddf8be7d435421e093b072ca891b99a49bc84eba098f4044f818" +dependencies = [ + "rand 0.7.3", +] + +[[package]] +name = "fancy-regex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe09872bd11351a75f22b24c3769fc863e8212d926d6db46b94ad710d14cc5cc" +dependencies = [ + "bit-set", + "regex", +] + +[[package]] +name = "fastrand" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "779d043b6a0b90cc4c0ed7ee380a6504394cee7efd7db050e3774eee387324b2" +dependencies = [ + "instant", +] + +[[package]] +name = "ffi-support" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27838c6815cfe9de2d3aeb145ffd19e565f577414b33f3bdbf42fe040e9e0ff6" +dependencies = [ + "lazy_static", + "log", +] + +[[package]] +name = "flowy-ast" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "flowy-collaboration" +version = "0.1.0" +dependencies = [ + "async-stream", + "bytes", + "chrono", + "dashmap", + "dissimilar", + "flowy-derive", + "flowy-folder-data-model", + "futures", + "lib-infra", + "lib-ot", + "log", + "md5", + "parking_lot", + "protobuf", + "serde", + "serde_json", + "strum", + "strum_macros", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "flowy-database" +version = "0.1.0" +dependencies = [ + "diesel", + "diesel_derives", + "diesel_migrations", + "lazy_static", + "lib-sqlite", + "log", +] + +[[package]] +name = "flowy-derive" +version = "0.1.0" +dependencies = [ + "flowy-ast", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "flowy-document" +version = "0.1.0" +dependencies = [ + "async-stream", + "async-trait", + "bytecount", + "byteorder", + "bytes", + "chrono", + "color-eyre", + "criterion", + "dart-notify", + "dashmap", + "derive_more", + "diesel", + "diesel_derives", + "flowy-collaboration", + "flowy-database", + "flowy-derive", + "flowy-document", + "flowy-error", + "flowy-sync", + "flowy-test", + "futures", + "futures-util", + "lib-dispatch", + "lib-infra", + "lib-ot", + "lib-ws", + "log", + "parking_lot", + "pin-project", + "protobuf", + "rand 0.7.3", + "serde", + "serde_json", + "strum", + "strum_macros", + "tokio", + "tracing", + "unicode-segmentation", + "url", +] + +[[package]] +name = "flowy-error" +version = "0.1.0" +dependencies = [ + "bytes", + "error-code", + "flowy-collaboration", + "flowy-database", + "flowy-derive", + "http-flowy", + "lib-dispatch", + "lib-ot", + "lib-sqlite", + "protobuf", + "r2d2", + "serde_json", +] + +[[package]] +name = "flowy-folder" +version = "0.1.0" +dependencies = [ + "bincode", + "bytes", + "chrono", + "crossbeam", + "crossbeam-utils", + "dart-notify", + "derive_more", + "diesel", + "diesel_derives", + "flowy-collaboration", + "flowy-database", + "flowy-derive", + "flowy-document", + "flowy-error", + "flowy-folder", + "flowy-folder-data-model", + "flowy-sync", + "flowy-test", + "futures", + "futures-core", + "lazy_static", + "lib-dispatch", + "lib-infra", + "lib-ot", + "lib-sqlite", + "log", + "parking_lot", + "pin-project", + "protobuf", + "serde", + "serde_json", + "serial_test", + "strum", + "strum_macros", + "tokio", + "tracing", +] + +[[package]] +name = "flowy-folder-data-model" +version = "0.1.0" +dependencies = [ + "bytes", + "chrono", + "derive_more", + "error-code", + "flowy-derive", + "log", + "protobuf", + "serde", + "serde_json", + "strum", + "strum_macros", + "unicode-segmentation", + "uuid", +] + +[[package]] +name = "flowy-net" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-stream", + "bytes", + "config", + "dashmap", + "flowy-collaboration", + "flowy-derive", + "flowy-document", + "flowy-error", + "flowy-folder", + "flowy-folder-data-model", + "flowy-user", + "flowy-user-data-model", + "futures-util", + "http-flowy", + "hyper", + "lazy_static", + "lib-dispatch", + "lib-infra", + "lib-ws", + "log", + "parking_lot", + "protobuf", + "reqwest", + "serde", + "serde-aux", + "serde_json", + "strum", + "strum_macros", + "tokio", + "tracing", +] + +[[package]] +name = "flowy-sdk" +version = "0.1.0" +dependencies = [ + "bincode", + "bytes", + "claim 0.5.0", + "color-eyre", + "flowy-collaboration", + "flowy-database", + "flowy-document", + "flowy-folder", + "flowy-net", + "flowy-sync", + "flowy-user", + "futures-core", + "futures-util", + "lib-dispatch", + "lib-infra", + "lib-log", + "lib-ws", + "log", + "parking_lot", + "protobuf", + "serde", + "tokio", + "tracing", +] + +[[package]] +name = "flowy-sync" +version = "0.1.0" +dependencies = [ + "async-stream", + "bytes", + "dashmap", + "diesel", + "diesel_derives", + "flowy-collaboration", + "flowy-database", + "flowy-error", + "futures-util", + "lib-infra", + "lib-ot", + "lib-ws", + "parking_lot", + "protobuf", + "serde", + "serde_json", + "strum", + "strum_macros", + "tokio", + "tracing", +] + +[[package]] +name = "flowy-test" +version = "0.1.0" +dependencies = [ + "bincode", + "bytes", + "claim 0.4.0", + "claim 0.5.0", + "fake", + "flowy-collaboration", + "flowy-folder", + "flowy-net", + "flowy-sdk", + "flowy-user", + "futures", + "futures-util", + "lib-dispatch", + "lib-infra", + "lib-ot", + "log", + "protobuf", + "quickcheck", + "quickcheck_macros", + "serde", + "serde_json", + "serial_test", + "thread-id", + "tokio", +] + +[[package]] +name = "flowy-user" +version = "0.1.0" +dependencies = [ + "bytes", + "dart-notify", + "dashmap", + "derive_more", + "diesel", + "diesel_derives", + "flowy-database", + "flowy-derive", + "flowy-error", + "flowy-test", + "flowy-user-data-model", + "futures", + "futures-core", + "lazy_static", + "lib-dispatch", + "lib-infra", + "lib-sqlite", + "log", + "once_cell", + "parking_lot", + "pin-project", + "protobuf", + "r2d2", + "serde", + "serde_json", + "serial_test", + "strum", + "strum_macros", + "thread-id", + "thread_local", + "tokio", + "tracing", +] + +[[package]] +name = "flowy-user-data-model" +version = "0.1.0" +dependencies = [ + "bytes", + "derive_more", + "error-code", + "fancy-regex", + "flowy-derive", + "lazy_static", + "log", + "protobuf", + "serde", + "unicode-segmentation", + "validator", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28560757fe2bb34e79f907794bb6b22ae8b0e5c669b638a1132f2592b19035b4" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3dda0b6588335f360afc675d0564c17a77a2bda81ca178a4b6081bd86c7f0b" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0c8ff0461b82559810cdccfde3215c3f373807f5e5232b71479bff7bb2583d7" + +[[package]] +name = "futures-executor" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29d6d2ff5bb10fb95c85b8ce46538a2e5f5e7fdc755623a7d4529ab8a4ed9d2a" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f9d34af5a1aac6fb380f735fe510746c38067c5bf16c7fd250280503c971b2" + +[[package]] +name = "futures-macro" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbd947adfffb0efc70599b3ddcf7b5597bb5fa9e245eb99f62b3a5f7bb8bd3c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3055baccb68d74ff6480350f8d6eb8fcfa3aa11bdc1a1ae3afdd0514617d508" + +[[package]] +name = "futures-task" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ee7c6485c30167ce4dfb83ac568a849fe53274c831081476ee13e0dce1aad72" + +[[package]] +name = "futures-util" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b5cf40b47a271f77a8b1bec03ca09044d99d2372c0de244e66430761127164" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "gethostname" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e692e296bfac1d2533ef168d0b60ff5897b8b70a4009276834014dd8924cc028" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", +] + +[[package]] +name = "gimli" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" + +[[package]] +name = "h2" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9de88456263e249e241fcd211d3954e2c9b0ef7ccfc235a444eb367cae3689" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "http" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.1", +] + +[[package]] +name = "http-body" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "http-flowy" +version = "0.1.0" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Server#3012acce300024dda8819d8507c9105af84cd909" +dependencies = [ + "anyhow", + "bytes", + "derive_more", + "hyper", + "protobuf", + "reqwest", + "serde", + "serde_json", + "serde_repr", + "thiserror", + "uuid", +] + +[[package]] +name = "httparse" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7ec3e62bdc98a2f0393a5048e4c30ef659440ea6e0e572965103e72bd836f55" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa 0.4.8", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "indexmap" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "ipnet" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" + +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "js-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lexical-core" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" +dependencies = [ + "arrayvec", + "bitflags", + "cfg-if", + "ryu", + "static_assertions", +] + +[[package]] +name = "lib-dispatch" +version = "0.1.0" +dependencies = [ + "bincode", + "bytes", + "dashmap", + "derivative", + "dyn-clone", + "env_logger 0.8.4", + "futures", + "futures-channel", + "futures-core", + "futures-util", + "lazy_static", + "log", + "paste", + "pin-project", + "protobuf", + "serde", + "serde_json", + "serde_with", + "thread-id", + "tokio", + "tracing", + "uuid", +] + +[[package]] +name = "lib-infra" +version = "0.1.0" +dependencies = [ + "bytes", + "chrono", + "futures-core", + "log", + "pin-project", + "rand 0.8.4", + "tokio", + "uuid", +] + +[[package]] +name = "lib-log" +version = "0.1.0" +dependencies = [ + "chrono", + "lazy_static", + "log", + "serde", + "serde_json", + "tracing", + "tracing-appender", + "tracing-bunyan-formatter", + "tracing-core", + "tracing-futures", + "tracing-log", + "tracing-subscriber", +] + +[[package]] +name = "lib-ot" +version = "0.1.0" +dependencies = [ + "anyhow", + "bytecount", + "bytes", + "dashmap", + "derive_more", + "lazy_static", + "log", + "md5", + "serde", + "serde_json", + "strum", + "strum_macros", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "lib-sqlite" +version = "0.1.0" +dependencies = [ + "diesel", + "diesel_derives", + "diesel_migrations", + "error-chain", + "lazy_static", + "libsqlite3-sys", + "log", + "r2d2", + "scheduled-thread-pool", +] + +[[package]] +name = "lib-ws" +version = "0.1.0" +dependencies = [ + "bytes", + "dashmap", + "flowy-derive", + "futures", + "futures-channel", + "futures-core", + "futures-util", + "lib-infra", + "log", + "parking_lot", + "paste", + "pin-project", + "protobuf", + "strum", + "strum_macros", + "tokio", + "tokio-tungstenite", + "tracing", + "url", +] + +[[package]] +name = "libc" +version = "0.2.112" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" + +[[package]] +name = "libsqlite3-sys" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290b64917f8b0cb885d9de0f9959fe1f775d7fa12f1da2db9001c1c8ab60f89d" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" + +[[package]] +name = "lock_api" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matchers" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "migrations_internals" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b4fc84e4af020b837029e017966f86a1c2d5e83e64b589963d5047525995860" +dependencies = [ + "diesel", +] + +[[package]] +name = "migrations_macros" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9753f12909fd8d923f75ae5c3258cae1ed3c8ec052e1b38c93c21a6d157f789c" +dependencies = [ + "migrations_internals", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + +[[package]] +name = "mio" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "native-tls" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nom" +version = "5.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" +dependencies = [ + "lexical-core", + "memchr", + "version_check", +] + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl" +version = "0.10.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-sys", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "owo-colors" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2386b4ebe91c2f7f51082d4cefa145d030e33a1842a96b12e4885cc3c01f7a55" + +[[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", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall 0.2.10", + "smallvec", + "winapi", +] + +[[package]] +name = "paste" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5" + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" + +[[package]] +name = "plotters" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d88417318da0eaf0fdcdb51a0ee6c3bed624333bff8f946733049380be67ac1c" + +[[package]] +name = "plotters-svg" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521fa9638fa597e1dc53e9412a4f9cefb01187ee1f7413076f9e6749e2885ba9" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "protobuf" +version = "2.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47c327e191621a2158159df97cdbc2e7074bb4e940275e35abf38eb3d2595754" + +[[package]] +name = "quickcheck" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44883e74aa97ad63db83c4bf8ca490f02b2fc02f92575e720c8551e843c945f" +dependencies = [ + "env_logger 0.7.1", + "log", + "rand 0.7.3", + "rand_core 0.5.1", +] + +[[package]] +name = "quickcheck_macros" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608c156fd8e97febc07dc9c2e2c80bf74cfc6ef26893eae3daf8bc2bc94a4b7f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r2d2" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "545c5bc2b880973c9c10e4067418407a0ccaa3091781d1671d46eb35107cb26f" +dependencies = [ + "log", + "parking_lot", + "scheduled-thread-pool", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc 0.2.0", +] + +[[package]] +name = "rand" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.3", + "rand_hc 0.3.1", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.3", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom 0.2.3", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_hc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core 0.6.3", +] + +[[package]] +name = "rayon" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "reqwest" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f242f1488a539a79bac6dbe7c8609ae43b7914b7736210f239a37cccb32525" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "lazy_static", + "log", + "mime", + "native-tls", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +dependencies = [ + "lazy_static", + "winapi", +] + +[[package]] +name = "scheduled-thread-pool" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6f74fd1204073fa02d5d5d68bec8021be4c38690b61264b2fdb48083d0e7d7" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "security-framework" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525bc1abfda2e1998d152c45cf13e696f76d0a4972310b22fac1658b05df7c87" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" + +[[package]] +name = "serde" +version = "1.0.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97565067517b60e2d1ea8b268e59ce036de907ac523ad83a0475da04e818989a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-aux" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "905f2fc9f3d1574e8b5923a58118240021f01d4e239673937ffb9f42707a4f22" +dependencies = [ + "chrono", + "serde", + "serde_json", +] + +[[package]] +name = "serde_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed201699328568d8d08208fdd080e3ff594e6c422e438b6705905da01005d537" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee2bb9cd061c5865d345bb02ca49fcef1391741b672b54a0bf7b679badec3142" +dependencies = [ + "itoa 1.0.1", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98d0516900518c29efa217c298fa1f4e6c6ffc85ae29fd7f4ee48f176e1a9ed5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" +dependencies = [ + "form_urlencoded", + "itoa 0.4.8", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad6056b4cb69b6e43e3a0f055def223380baecc99da683884f205bf347f7c4b3" +dependencies = [ + "rustversion", + "serde", + "serde_with_macros", +] + +[[package]] +name = "serde_with_macros" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12e47be9471c72889ebafb5e14d5ff930d89ae7a67bbdb5f8abb564f845a927e" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serial_test" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0bccbcf40c8938196944a3da0e133e031a33f4d6b72db3bda3cc556e361905d" +dependencies = [ + "lazy_static", + "parking_lot", + "serial_test_derive", +] + +[[package]] +name = "serial_test_derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2acd6defeddb41eb60bb468f8825d0cfd0c2a76bc03bfd235b6a1dc4f6a1ad5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sha-1" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +dependencies = [ + "block-buffer", + "cfg-if", + "cpufeatures", + "digest", + "opaque-debug", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" + +[[package]] +name = "smallvec" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" + +[[package]] +name = "socket2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strum" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaf86bbcfd1fa9670b7a129f64fc0c9fcbbfe4f1bc4210e9e98fe71ffc12cde2" + +[[package]] +name = "strum_macros" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a684ac3dcd8913827e18cd09a68384ee66c1de24157e3c556c9ab16d85695fb7" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall 0.2.10", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread-id" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1" +dependencies = [ + "libc", + "redox_syscall 0.1.57", + "winapi", +] + +[[package]] +name = "thread_local" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" +dependencies = [ + "once_cell", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "tinyvec" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbbf1c778ec206785635ce8ad57fe52b3009ae9e0c9f574a728f3049d3e55838" +dependencies = [ + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "once_cell", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "511de3f85caf1c98983545490c3d09685fa8eb634e57eec22bb4db271f46cbd8" +dependencies = [ + "futures-util", + "log", + "pin-project", + "tokio", + "tungstenite", +] + +[[package]] +name = "tokio-util" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower-service" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" + +[[package]] +name = "tracing" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-appender" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9965507e507f12c8901432a33e31131222abac31edd90cabbcf85cf544b7127a" +dependencies = [ + "chrono", + "crossbeam-channel", + "tracing-subscriber", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-bunyan-formatter" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c408910c9b7eabc0215fe2b4a89f8ec95581a91cea1f7619f7c78caf14cbc2a1" +dependencies = [ + "chrono", + "gethostname", + "log", + "serde", + "serde_json", + "tracing", + "tracing-core", + "tracing-log", + "tracing-subscriber", +] + +[[package]] +name = "tracing-core" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "tracing-log" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6923477a48e41c1951f1999ef8bb5a3023eb723ceadafe78ffb65dc366761e3" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-serde" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" +dependencies = [ + "ansi_term", + "chrono", + "lazy_static", + "matchers", + "regex", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", + "tracing-serde", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "tungstenite" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0b2d8558abd2e276b0a8df5c05a2ec762609344191e5fd23e292c910e9165b5" +dependencies = [ + "base64", + "byteorder", + "bytes", + "http", + "httparse", + "log", + "rand 0.8.4", + "sha-1", + "thiserror", + "url", + "utf-8", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "unicode-bidi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +dependencies = [ + "getrandom 0.2.3", + "serde", +] + +[[package]] +name = "validator" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d6937c33ec6039d8071bcf72933146b5bbe378d645d8fa59bdadabfc2a249" +dependencies = [ + "idna", + "lazy_static", + "regex", + "serde", + "serde_derive", + "serde_json", + "url", + "validator_types", +] + +[[package]] +name = "validator_types" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad9680608df133af2c1ddd5eaf1ddce91d60d61b6bc51494ef326458365a470a" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasm-bindgen" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" + +[[package]] +name = "web-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "winreg" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" +dependencies = [ + "winapi", +] + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] diff --git a/frontend/rust-lib/flowy-document/Cargo.toml b/frontend/rust-lib/flowy-document/Cargo.toml index 11f282c2f3..0dbecccd3b 100644 --- a/frontend/rust-lib/flowy-document/Cargo.toml +++ b/frontend/rust-lib/flowy-document/Cargo.toml @@ -17,7 +17,7 @@ derive_more = {version = "0.99", features = ["display"]} lib-dispatch = { path = "../lib-dispatch" } flowy-database = { path = "../flowy-database" } flowy-sync = { path = "../flowy-sync" } -flowy-error = { path = "../flowy-error", features = ["collaboration", "ot", "backend", "serde", "db"] } +flowy-error = { path = "../flowy-error", features = ["collaboration", "ot", "http_server", "serde", "db"] } dart-notify = { path = "../dart-notify" } diesel = {version = "1.4.8", features = ["sqlite"]} diff --git a/frontend/rust-lib/flowy-error/Cargo.toml b/frontend/rust-lib/flowy-error/Cargo.toml index 16da001014..6deceba9d7 100644 --- a/frontend/rust-lib/flowy-error/Cargo.toml +++ b/frontend/rust-lib/flowy-error/Cargo.toml @@ -16,7 +16,7 @@ bytes = "1.0" flowy-collaboration = { path = "../../../shared-lib/flowy-collaboration", optional = true} lib-ot = { path = "../../../shared-lib/lib-ot", optional = true} serde_json = {version = "1.0", optional = true} -backend-service = { path = "../../../shared-lib/backend-service", optional = true} +http-flowy = { git = "https://github.com/AppFlowy-IO/AppFlowy-Server", optional = true} flowy-database = { path = "../flowy-database", optional = true} r2d2 = { version = "0.8", optional = true} lib-sqlite = { path = "../lib-sqlite", optional = true } @@ -25,5 +25,5 @@ lib-sqlite = { path = "../lib-sqlite", optional = true } collaboration = ["flowy-collaboration"] ot = ["lib-ot"] serde = ["serde_json"] -backend = ["backend-service"] +http_server = ["http-flowy"] db = ["flowy-database", "lib-sqlite", "r2d2"] diff --git a/frontend/rust-lib/flowy-error/src/ext/backend.rs b/frontend/rust-lib/flowy-error/src/ext/http_server.rs similarity index 91% rename from frontend/rust-lib/flowy-error/src/ext/backend.rs rename to frontend/rust-lib/flowy-error/src/ext/http_server.rs index 3c901ef2a6..4515eddf1f 100644 --- a/frontend/rust-lib/flowy-error/src/ext/backend.rs +++ b/frontend/rust-lib/flowy-error/src/ext/http_server.rs @@ -1,6 +1,6 @@ use crate::FlowyError; -use backend_service::errors::{ErrorCode as ServerErrorCode, ServerError}; use error_code::ErrorCode; +use http_flowy::errors::{ErrorCode as ServerErrorCode, ServerError}; impl std::convert::From for FlowyError { fn from(error: ServerError) -> Self { diff --git a/frontend/rust-lib/flowy-error/src/ext/mod.rs b/frontend/rust-lib/flowy-error/src/ext/mod.rs index 8bd455de93..6468d6777a 100644 --- a/frontend/rust-lib/flowy-error/src/ext/mod.rs +++ b/frontend/rust-lib/flowy-error/src/ext/mod.rs @@ -16,10 +16,10 @@ mod serde; pub use serde::*; // -#[cfg(feature = "backend")] -mod backend; -#[cfg(feature = "backend")] -pub use backend::*; +#[cfg(feature = "http_server")] +mod http_server; +#[cfg(feature = "http_server")] +pub use http_server::*; #[cfg(feature = "db")] mod database; diff --git a/frontend/rust-lib/flowy-folder/Cargo.toml b/frontend/rust-lib/flowy-folder/Cargo.toml index b8ae1e251b..f98ecd51e3 100644 --- a/frontend/rust-lib/flowy-folder/Cargo.toml +++ b/frontend/rust-lib/flowy-folder/Cargo.toml @@ -14,7 +14,7 @@ lib-infra = { path = "../../../shared-lib/lib-infra" } flowy-document = { path = "../flowy-document" } flowy-database = { path = "../flowy-database" } -flowy-error = { path = "../flowy-error", features = ["db", "backend"]} +flowy-error = { path = "../flowy-error", features = ["db", "http_server"]} dart-notify = { path = "../dart-notify" } lib-dispatch = { path = "../lib-dispatch" } lib-sqlite = { path = "../lib-sqlite" } diff --git a/frontend/rust-lib/flowy-net/Cargo.toml b/frontend/rust-lib/flowy-net/Cargo.toml index d67e12606b..67e215c86d 100644 --- a/frontend/rust-lib/flowy-net/Cargo.toml +++ b/frontend/rust-lib/flowy-net/Cargo.toml @@ -7,10 +7,9 @@ edition = "2018" [dependencies] lib-dispatch = { path = "../lib-dispatch" } -flowy-error = { path = "../flowy-error", features = ["collaboration"] } +flowy-error = { path = "../flowy-error", features = ["collaboration", "http_server"] } flowy-derive = { path = "../../../shared-lib/flowy-derive" } flowy-collaboration = { path = "../../../shared-lib/flowy-collaboration"} -backend-service = { path = "../../../shared-lib/backend-service" } flowy-folder-data-model = { path = "../../../shared-lib/flowy-folder-data-model" } flowy-user-data-model = { path = "../../../shared-lib/flowy-user-data-model"} flowy-folder = { path = "../flowy-folder" } @@ -30,5 +29,14 @@ tracing = { version = "0.1", features = ["log"] } dashmap = {version = "4.0"} async-stream = "0.3.2" futures-util = "0.3.15" +http-flowy = { git = "https://github.com/AppFlowy-IO/AppFlowy-Server", features = ["with_reqwest"] } +serde-aux = "1.0.1" +reqwest = "0.11" +hyper = "0.14" +config = { version = "0.10.1", default-features = false, features = ["yaml"] } +log = "0.4.14" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" + [features] http_server = [] \ No newline at end of file diff --git a/shared-lib/backend-service/configuration/base.yaml b/frontend/rust-lib/flowy-net/configuration/base.yaml similarity index 100% rename from shared-lib/backend-service/configuration/base.yaml rename to frontend/rust-lib/flowy-net/configuration/base.yaml diff --git a/shared-lib/backend-service/configuration/local.yaml b/frontend/rust-lib/flowy-net/configuration/local.yaml similarity index 100% rename from shared-lib/backend-service/configuration/local.yaml rename to frontend/rust-lib/flowy-net/configuration/local.yaml diff --git a/shared-lib/backend-service/configuration/production.yaml b/frontend/rust-lib/flowy-net/configuration/production.yaml similarity index 100% rename from shared-lib/backend-service/configuration/production.yaml rename to frontend/rust-lib/flowy-net/configuration/production.yaml diff --git a/shared-lib/backend-service/src/configuration.rs b/frontend/rust-lib/flowy-net/src/configuration.rs similarity index 98% rename from shared-lib/backend-service/src/configuration.rs rename to frontend/rust-lib/flowy-net/src/configuration.rs index 6912188826..883151dc48 100644 --- a/shared-lib/backend-service/src/configuration.rs +++ b/frontend/rust-lib/flowy-net/src/configuration.rs @@ -1,7 +1,6 @@ use config::FileFormat; use serde_aux::field_attributes::deserialize_number_from_string; use std::convert::{TryFrom, TryInto}; -pub const HOST: &str = "localhost:8000"; pub const HEADER_TOKEN: &str = "token"; #[derive(serde::Deserialize, Clone, Debug)] @@ -89,6 +88,7 @@ pub enum Environment { } impl Environment { + #[allow(dead_code)] pub fn as_str(&self) -> &'static str { match self { Environment::Local => "local", diff --git a/frontend/rust-lib/flowy-net/src/http_server/document.rs b/frontend/rust-lib/flowy-net/src/http_server/document.rs index 8615ad1f77..e4d8722ca8 100644 --- a/frontend/rust-lib/flowy-net/src/http_server/document.rs +++ b/frontend/rust-lib/flowy-net/src/http_server/document.rs @@ -1,11 +1,11 @@ -use backend_service::{ +use crate::{ configuration::*, request::{HttpRequestBuilder, ResponseMiddleware}, - response::FlowyResponse, }; use flowy_collaboration::entities::document_info::{CreateDocParams, DocumentId, DocumentInfo, ResetDocumentParams}; use flowy_document::DocumentCloudService; use flowy_error::FlowyError; +use http_flowy::response::FlowyResponse; use lazy_static::lazy_static; use lib_infra::future::FutureResult; use std::sync::Arc; diff --git a/frontend/rust-lib/flowy-net/src/http_server/core.rs b/frontend/rust-lib/flowy-net/src/http_server/folder.rs similarity index 96% rename from frontend/rust-lib/flowy-net/src/http_server/core.rs rename to frontend/rust-lib/flowy-net/src/http_server/folder.rs index aa5e56a641..e70d690bb7 100644 --- a/frontend/rust-lib/flowy-net/src/http_server/core.rs +++ b/frontend/rust-lib/flowy-net/src/http_server/folder.rs @@ -1,8 +1,6 @@ -use backend_service::{ +use crate::{ configuration::{ClientServerConfiguration, HEADER_TOKEN}, - errors::ServerError, request::{HttpRequestBuilder, ResponseMiddleware}, - response::FlowyResponse, }; use flowy_error::FlowyError; use flowy_folder_data_model::entities::{ @@ -13,6 +11,8 @@ use flowy_folder_data_model::entities::{ }; use flowy_folder::event_map::FolderCouldServiceV1; +use http_flowy::errors::ServerError; +use http_flowy::response::FlowyResponse; use lazy_static::lazy_static; use lib_infra::future::FutureResult; use std::sync::Arc; @@ -338,17 +338,17 @@ pub async fn read_trash_request(token: &str, url: &str) -> Result = Arc::new(CoreResponseMiddleware::new()); + static ref MIDDLEWARE: Arc = Arc::new(FolderResponseMiddleware::new()); } -pub struct CoreResponseMiddleware { +pub struct FolderResponseMiddleware { invalid_token_sender: broadcast::Sender, } -impl CoreResponseMiddleware { +impl FolderResponseMiddleware { fn new() -> Self { let (sender, _) = broadcast::channel(10); - CoreResponseMiddleware { + FolderResponseMiddleware { invalid_token_sender: sender, } } @@ -359,7 +359,7 @@ impl CoreResponseMiddleware { } } -impl ResponseMiddleware for CoreResponseMiddleware { +impl ResponseMiddleware for FolderResponseMiddleware { fn receive_response(&self, token: &Option, response: &FlowyResponse) { if let Some(error) = &response.error { if error.is_unauthorized() { diff --git a/frontend/rust-lib/flowy-net/src/http_server/mod.rs b/frontend/rust-lib/flowy-net/src/http_server/mod.rs index ee2bdb2073..53fd3bdead 100644 --- a/frontend/rust-lib/flowy-net/src/http_server/mod.rs +++ b/frontend/rust-lib/flowy-net/src/http_server/mod.rs @@ -1,3 +1,3 @@ -pub mod core; pub mod document; +pub mod folder; pub mod user; diff --git a/frontend/rust-lib/flowy-net/src/http_server/user.rs b/frontend/rust-lib/flowy-net/src/http_server/user.rs index 82d173a15a..7a09ca5b95 100644 --- a/frontend/rust-lib/flowy-net/src/http_server/user.rs +++ b/frontend/rust-lib/flowy-net/src/http_server/user.rs @@ -1,9 +1,10 @@ -use backend_service::{configuration::*, errors::ServerError, request::HttpRequestBuilder}; +use crate::{configuration::*, request::HttpRequestBuilder}; use flowy_error::FlowyError; use flowy_user::event_map::UserCloudService; use flowy_user_data_model::entities::{ SignInParams, SignInResponse, SignUpParams, SignUpResponse, UpdateUserParams, UserProfile, }; +use http_flowy::errors::ServerError; use lib_infra::future::FutureResult; pub struct UserHttpCloudService { diff --git a/frontend/rust-lib/flowy-net/src/lib.rs b/frontend/rust-lib/flowy-net/src/lib.rs index 9b646d6f5a..78f8a28f02 100644 --- a/frontend/rust-lib/flowy-net/src/lib.rs +++ b/frontend/rust-lib/flowy-net/src/lib.rs @@ -1,3 +1,4 @@ +mod configuration; pub mod entities; mod event; mod handlers; @@ -5,6 +6,7 @@ pub mod http_server; pub mod local_server; pub mod module; pub mod protobuf; +mod request; pub mod ws; -pub use backend_service::configuration::{get_client_server_configuration, ClientServerConfiguration}; +pub use crate::configuration::{get_client_server_configuration, ClientServerConfiguration}; diff --git a/frontend/rust-lib/flowy-net/src/local_server/mod.rs b/frontend/rust-lib/flowy-net/src/local_server/mod.rs index e62b0680c4..3cbc28664d 100644 --- a/frontend/rust-lib/flowy-net/src/local_server/mod.rs +++ b/frontend/rust-lib/flowy-net/src/local_server/mod.rs @@ -1,4 +1,4 @@ -use backend_service::configuration::ClientServerConfiguration; +use crate::configuration::ClientServerConfiguration; use tokio::sync::{broadcast, mpsc}; mod persistence; diff --git a/shared-lib/backend-service/src/request/request.rs b/frontend/rust-lib/flowy-net/src/request.rs similarity index 94% rename from shared-lib/backend-service/src/request/request.rs rename to frontend/rust-lib/flowy-net/src/request.rs index 6f0eb5aa4a..f65c19687f 100644 --- a/shared-lib/backend-service/src/request/request.rs +++ b/frontend/rust-lib/flowy-net/src/request.rs @@ -1,5 +1,7 @@ -use crate::{configuration::HEADER_TOKEN, errors::ServerError, response::FlowyResponse}; +use crate::configuration::HEADER_TOKEN; use bytes::Bytes; +use http_flowy::errors::ServerError; +use http_flowy::response::FlowyResponse; use hyper::http; use protobuf::ProtobufError; use reqwest::{header::HeaderMap, Client, Method, Response}; @@ -141,7 +143,7 @@ impl HttpRequestBuilder { let method = self.method.clone(); let headers = self.headers.clone(); - // reqwest client is not 'Sync' by channel is. + // reqwest client is not 'Sync' but channel is. tokio::spawn(async move { let client = default_client(); let mut builder = client.request(method.clone(), url).headers(headers); @@ -153,7 +155,10 @@ impl HttpRequestBuilder { let _ = tx.send(response); }); - let response = rx.await??; + let response = rx.await.map_err(|e| { + let mag = format!("Receive http response channel error: {}", e); + ServerError::internal().context(mag) + })??; tracing::trace!("Http Response: {:?}", response); let flowy_response = flowy_response_from(response).await?; let token = self.token(); diff --git a/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs b/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs index 8e23f64863..68a5a1011e 100644 --- a/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs +++ b/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs @@ -9,7 +9,7 @@ use flowy_folder::{ }; use flowy_net::ClientServerConfiguration; use flowy_net::{ - http_server::core::FolderHttpCloudService, local_server::LocalServer, ws::connection::FlowyWebSocketConnect, + http_server::folder::FolderHttpCloudService, local_server::LocalServer, ws::connection::FlowyWebSocketConnect, }; use flowy_sync::{RevisionWebSocket, WSStateReceiver}; use flowy_user::services::UserSession; diff --git a/frontend/rust-lib/flowy-sync/Cargo.toml b/frontend/rust-lib/flowy-sync/Cargo.toml index c7c39ffd1d..2aadd7b3be 100644 --- a/frontend/rust-lib/flowy-sync/Cargo.toml +++ b/frontend/rust-lib/flowy-sync/Cargo.toml @@ -11,7 +11,7 @@ lib-ot = { path = "../../../shared-lib/lib-ot" } lib-ws = { path = "../../../shared-lib/lib-ws" } lib-infra = { path = "../../../shared-lib/lib-infra" } flowy-database = { path = "../flowy-database" } -flowy-error = { path = "../flowy-error", features = ["collaboration", "ot", "backend", "serde", "db"] } +flowy-error = { path = "../flowy-error", features = ["collaboration", "ot", "http_server", "serde", "db"] } diesel = {version = "1.4.8", features = ["sqlite"]} diesel_derives = {version = "1.4.1", features = ["sqlite"]} protobuf = {version = "2.18.0"} diff --git a/frontend/rust-lib/flowy-user/Cargo.toml b/frontend/rust-lib/flowy-user/Cargo.toml index 78092c9925..3f7e1021c3 100644 --- a/frontend/rust-lib/flowy-user/Cargo.toml +++ b/frontend/rust-lib/flowy-user/Cargo.toml @@ -14,7 +14,7 @@ derive_more = {version = "0.99", features = ["display"]} flowy-database = { path = "../flowy-database" } dart-notify = { path = "../dart-notify" } lib-dispatch = { path = "../lib-dispatch" } -flowy-error = { path = "../flowy-error", features = ["db", "backend"] } +flowy-error = { path = "../flowy-error", features = ["db", "http_server"] } lib-sqlite = { path = "../lib-sqlite" } tracing = { version = "0.1", features = ["log"] } diff --git a/frontend/scripts/makefile/desktop.toml b/frontend/scripts/makefile/desktop.toml index 98d4227857..cab4dcba60 100644 --- a/frontend/scripts/makefile/desktop.toml +++ b/frontend/scripts/makefile/desktop.toml @@ -34,6 +34,7 @@ private = true script = [ """ cd rust-lib/ + rustup show echo cargo build --package=dart-ffi --target ${RUST_COMPILE_TARGET} --features "${FEATURES}" cargo build --package=dart-ffi --target ${RUST_COMPILE_TARGET} --features "${FEATURES}" cd ../ diff --git a/shared-lib/Cargo.lock b/shared-lib/Cargo.lock index 262980c653..326e71a4ac 100644 --- a/shared-lib/Cargo.lock +++ b/shared-lib/Cargo.lock @@ -2,206 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "actix-codec" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13895df506faee81e423febbae3a33b27fca71831b96bb3d60adf16ebcfea952" -dependencies = [ - "bitflags", - "bytes", - "futures-core", - "futures-sink", - "log", - "memchr", - "pin-project-lite", - "tokio", - "tokio-util", -] - -[[package]] -name = "actix-http" -version = "3.0.0-beta.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afaeb3d3fcb06b775ac62f05d580aae4afe5a149513333a73f688fdf26c06639" -dependencies = [ - "actix-codec", - "actix-rt", - "actix-service", - "actix-utils", - "ahash", - "base64", - "bitflags", - "brotli2", - "bytes", - "bytestring", - "derive_more", - "encoding_rs", - "flate2", - "futures-core", - "futures-util", - "h2", - "http", - "httparse", - "httpdate", - "itoa", - "language-tags", - "local-channel", - "log", - "mime", - "percent-encoding", - "pin-project", - "pin-project-lite", - "rand 0.8.4", - "sha-1", - "smallvec", - "zstd", -] - -[[package]] -name = "actix-macros" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" -dependencies = [ - "quote", - "syn", -] - -[[package]] -name = "actix-router" -version = "0.5.0-beta.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36b95ce0d76d1aa2f98b681702807475ade0f99bd4552546a6843a966d42ea3d" -dependencies = [ - "bytestring", - "firestorm", - "http", - "log", - "regex", - "serde", -] - -[[package]] -name = "actix-rt" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a0c218d0a17c120f10ee0c69c9f0c45d87319e8f66b1f065e8412b612fc3e24" -dependencies = [ - "actix-macros", - "futures-core", - "tokio", -] - -[[package]] -name = "actix-server" -version = "2.0.0-beta.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "411dd3296dd317ff5eff50baa13f31923ea40ec855dd7f2d3ed8639948f0195f" -dependencies = [ - "actix-rt", - "actix-service", - "actix-utils", - "futures-core", - "futures-util", - "log", - "mio", - "num_cpus", - "socket2", - "tokio", -] - -[[package]] -name = "actix-service" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d3dc6a618b082974a08d7a4781d24d4691cba51500059bfebe6656a61ebfe1e" -dependencies = [ - "futures-core", - "paste", - "pin-project-lite", -] - -[[package]] -name = "actix-utils" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e491cbaac2e7fc788dfff99ff48ef317e23b3cf63dbaf7aaab6418f40f92aa94" -dependencies = [ - "local-waker", - "pin-project-lite", -] - -[[package]] -name = "actix-web" -version = "4.0.0-beta.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e85aa9bb018d83a0db70f557ba0cde9c6170a5d1de4fede02e377f68c1ac5aa9" -dependencies = [ - "actix-codec", - "actix-http", - "actix-macros", - "actix-router", - "actix-rt", - "actix-server", - "actix-service", - "actix-utils", - "actix-web-codegen", - "ahash", - "bytes", - "cfg-if", - "cookie", - "derive_more", - "either", - "encoding_rs", - "futures-core", - "futures-util", - "itoa", - "language-tags", - "log", - "mime", - "once_cell", - "paste", - "pin-project", - "regex", - "serde", - "serde_json", - "serde_urlencoded", - "smallvec", - "socket2", - "time 0.3.5", - "url", -] - -[[package]] -name = "actix-web-codegen" -version = "0.5.0-beta.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfe80a8828fa88a0420dc8fdd4c16b8207326c917f17701881b063eadc2a8d3b" -dependencies = [ - "actix-router", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "ahash" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" -dependencies = [ - "getrandom 0.2.3", - "once_cell", - "version_check", -] - [[package]] name = "aho-corasick" version = "0.7.18" @@ -217,12 +17,6 @@ version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38d9ff5d688f1c13395289f67db01d4826b46dd694e7580accdc3e8430f2d98e" -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - [[package]] name = "async-stream" version = "0.3.2" @@ -261,39 +55,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" -[[package]] -name = "backend-service" -version = "0.1.0" -dependencies = [ - "actix-web", - "anyhow", - "bytes", - "config", - "derive_more", - "flowy-collaboration", - "flowy-folder-data-model", - "flowy-user-data-model", - "hyper", - "lazy_static", - "log", - "protobuf", - "reqwest", - "serde", - "serde-aux", - "serde_json", - "serde_repr", - "thiserror", - "tokio", - "tracing", - "uuid", -] - -[[package]] -name = "base-x" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" - [[package]] name = "base64" version = "0.13.0" @@ -330,32 +91,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "brotli-sys" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4445dea95f4c2b41cde57cc9fee236ae4dbae88d8fcbdb4750fc1bb5d86aaecd" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "brotli2" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cb036c3eade309815c15ddbacec5b22c4d1f3983a774ab2eac2e3e9ea85568e" -dependencies = [ - "brotli-sys", - "libc", -] - -[[package]] -name = "bumpalo" -version = "3.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" - [[package]] name = "bytecount" version = "0.6.2" @@ -373,27 +108,6 @@ name = "bytes" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" -dependencies = [ - "serde", -] - -[[package]] -name = "bytestring" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90706ba19e97b90786e19dc0d5e2abd80008d99d4c0c5d1ad0b5e72cec7c494d" -dependencies = [ - "bytes", -] - -[[package]] -name = "cc" -version = "1.0.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" -dependencies = [ - "jobserver", -] [[package]] name = "cfg-if" @@ -410,7 +124,7 @@ dependencies = [ "libc", "num-integer", "num-traits", - "time 0.1.44", + "time", "winapi", ] @@ -423,57 +137,12 @@ dependencies = [ "autocfg", ] -[[package]] -name = "config" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b076e143e1d9538dde65da30f8481c2a6c44040edb8e02b9bf1351edb92ce3" -dependencies = [ - "lazy_static", - "nom", - "serde", - "yaml-rust", -] - -[[package]] -name = "const_fn" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f92cfa0fd5690b3cf8c1ef2cabbd9b7ef22fa53cf5e1f92b05103f6d5d1cf6e7" - [[package]] name = "convert_case" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" -[[package]] -name = "cookie" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f1c7727e460397e56abc4bddc1d49e07a1ad78fc98eb2e1c8f032a58a2f80d" -dependencies = [ - "percent-encoding", - "time 0.2.27", - "version_check", -] - -[[package]] -name = "core-foundation" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - [[package]] name = "cpufeatures" version = "0.2.1" @@ -483,15 +152,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crc32fast" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" -dependencies = [ - "cfg-if", -] - [[package]] name = "dashmap" version = "4.0.2" @@ -511,7 +171,7 @@ dependencies = [ "convert_case", "proc-macro2", "quote", - "rustc_version 0.3.3", + "rustc_version", "syn", ] @@ -524,33 +184,12 @@ dependencies = [ "generic-array", ] -[[package]] -name = "discard" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" - [[package]] name = "dissimilar" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31ad93652f40969dead8d4bf897a41e9462095152eb21c56e5830537e41179dd" -[[package]] -name = "either" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" - -[[package]] -name = "encoding_rs" -version = "0.8.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a74ea89a0a1b98f6332de42c95baff457ada66d1cb4030f9ff151b2041a1c746" -dependencies = [ - "cfg-if", -] - [[package]] name = "env_logger" version = "0.7.1" @@ -602,24 +241,6 @@ dependencies = [ "regex", ] -[[package]] -name = "firestorm" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31586bda1b136406162e381a3185a506cdfc1631708dd40cba2f6628d8634499" - -[[package]] -name = "flate2" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" -dependencies = [ - "cfg-if", - "crc32fast", - "libc", - "miniz_oxide", -] - [[package]] name = "flowy-ast" version = "0.1.0" @@ -717,21 +338,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "form_urlencoded" version = "1.0.1" @@ -874,31 +480,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" -[[package]] -name = "h2" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fd819562fcebdac5afc5c113c3ec36f902840b70fd4fc458799c8ce4607ae55" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" - [[package]] name = "heck" version = "0.3.3" @@ -928,72 +509,18 @@ dependencies = [ "itoa", ] -[[package]] -name = "http-body" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - [[package]] name = "httparse" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" -[[package]] -name = "httpdate" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" - [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" -[[package]] -name = "hyper" -version = "0.14.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436ec0091e4f20e655156a30a0df3770fe2900aa301e548e08446ec794b6953c" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - [[package]] name = "idna" version = "0.2.3" @@ -1005,16 +532,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "indexmap" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" -dependencies = [ - "autocfg", - "hashbrown", -] - [[package]] name = "instant" version = "0.1.12" @@ -1024,61 +541,18 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "ipnet" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" - [[package]] name = "itoa" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" -[[package]] -name = "jobserver" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" -dependencies = [ - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "language-tags" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" - [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -[[package]] -name = "lexical-core" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" -dependencies = [ - "arrayvec", - "bitflags", - "cfg-if", - "ryu", - "static_assertions", -] - [[package]] name = "lib-infra" version = "0.1.0" @@ -1146,30 +620,6 @@ version = "0.2.107" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219" -[[package]] -name = "linked-hash-map" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" - -[[package]] -name = "local-channel" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6246c68cf195087205a0512559c97e15eaf95198bf0e206d662092cdcb03fe9f" -dependencies = [ - "futures-core", - "futures-sink", - "futures-util", - "local-waker", -] - -[[package]] -name = "local-waker" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84f9a2d3e27ce99ce2c3aad0b09b1a7b916293ea9b2bf624c13fe646fadd8da4" - [[package]] name = "lock_api" version = "0.4.5" @@ -1206,22 +656,6 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" -[[package]] -name = "mime" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" - -[[package]] -name = "miniz_oxide" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" -dependencies = [ - "adler", - "autocfg", -] - [[package]] name = "mio" version = "0.7.14" @@ -1244,35 +678,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "native-tls" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "nom" -version = "5.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" -dependencies = [ - "lexical-core", - "memchr", - "version_check", -] - [[package]] name = "ntapi" version = "0.3.6" @@ -1323,39 +728,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" -[[package]] -name = "openssl" -version = "0.10.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95" -dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-sys", -] - -[[package]] -name = "openssl-probe" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" - -[[package]] -name = "openssl-sys" -version = "0.9.71" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7df13d165e607909b363a4757a6f133f8a818a74e9d3a98d09c6128e15fa4c73" -dependencies = [ - "autocfg", - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "parking_lot" version = "0.11.2" @@ -1434,12 +806,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pkg-config" -version = "0.3.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f" - [[package]] name = "ppv-lite86" version = "0.2.15" @@ -1612,66 +978,13 @@ version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - -[[package]] -name = "reqwest" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d2927ca2f685faf0fc620ac4834690d29e7abb153add10f5812eef20b5e280" -dependencies = [ - "base64", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "http", - "http-body", - "hyper", - "hyper-tls", - "ipnet", - "js-sys", - "lazy_static", - "log", - "mime", - "native-tls", - "percent-encoding", - "pin-project-lite", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-native-tls", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg", -] - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver 0.9.0", -] - [[package]] name = "rustc_version" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" dependencies = [ - "semver 0.11.0", + "semver", ] [[package]] @@ -1680,69 +993,21 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" -[[package]] -name = "schannel" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" -dependencies = [ - "lazy_static", - "winapi", -] - [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -[[package]] -name = "security-framework" -version = "2.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525bc1abfda2e1998d152c45cf13e696f76d0a4972310b22fac1658b05df7c87" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser 0.7.0", -] - [[package]] name = "semver" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" dependencies = [ - "semver-parser 0.10.2", + "semver-parser", ] -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - [[package]] name = "semver-parser" version = "0.10.2" @@ -1761,17 +1026,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde-aux" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "905f2fc9f3d1574e8b5923a58118240021f01d4e239673937ffb9f42707a4f22" -dependencies = [ - "chrono", - "serde", - "serde_json", -] - [[package]] name = "serde_derive" version = "1.0.130" @@ -1794,29 +1048,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_repr" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98d0516900518c29efa217c298fa1f4e6c6ffc85ae29fd7f4ee48f176e1a9ed5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - [[package]] name = "serial_test" version = "0.5.1" @@ -1852,12 +1083,6 @@ dependencies = [ "opaque-debug", ] -[[package]] -name = "sha1" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" - [[package]] name = "signal-hook-registry" version = "1.4.0" @@ -1879,80 +1104,6 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" -[[package]] -name = "socket2" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "standback" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" -dependencies = [ - "version_check", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "stdweb" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" -dependencies = [ - "discard", - "rustc_version 0.2.3", - "stdweb-derive", - "stdweb-internal-macros", - "stdweb-internal-runtime", - "wasm-bindgen", -] - -[[package]] -name = "stdweb-derive" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "serde_derive", - "syn", -] - -[[package]] -name = "stdweb-internal-macros" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" -dependencies = [ - "base-x", - "proc-macro2", - "quote", - "serde", - "serde_derive", - "serde_json", - "sha1", - "syn", -] - -[[package]] -name = "stdweb-internal-runtime" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" - [[package]] name = "strum" version = "0.21.0" @@ -1982,20 +1133,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "tempfile" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" -dependencies = [ - "cfg-if", - "libc", - "rand 0.8.4", - "redox_syscall", - "remove_dir_all", - "winapi", -] - [[package]] name = "termcolor" version = "1.1.2" @@ -2036,54 +1173,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "time" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242" -dependencies = [ - "const_fn", - "libc", - "standback", - "stdweb", - "time-macros", - "version_check", - "winapi", -] - -[[package]] -name = "time" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41effe7cfa8af36f439fac33861b66b049edc6f9a32331e2312660529c1c24ad" -dependencies = [ - "itoa", - "libc", -] - -[[package]] -name = "time-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" -dependencies = [ - "proc-macro-hack", - "time-macros-impl", -] - -[[package]] -name = "time-macros-impl" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" -dependencies = [ - "proc-macro-hack", - "proc-macro2", - "quote", - "standback", - "syn", -] - [[package]] name = "tinyvec" version = "1.5.1" @@ -2130,16 +1219,6 @@ dependencies = [ "syn", ] -[[package]] -name = "tokio-native-tls" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" -dependencies = [ - "native-tls", - "tokio", -] - [[package]] name = "tokio-tungstenite" version = "0.15.0" @@ -2153,20 +1232,6 @@ dependencies = [ "tungstenite", ] -[[package]] -name = "tokio-util" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "log", - "pin-project-lite", - "tokio", -] - [[package]] name = "toml" version = "0.5.8" @@ -2176,12 +1241,6 @@ dependencies = [ "serde", ] -[[package]] -name = "tower-service" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" - [[package]] name = "tracing" version = "0.1.29" @@ -2215,12 +1274,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "try-lock" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" - [[package]] name = "trybuild" version = "1.0.52" @@ -2343,28 +1396,12 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad9680608df133af2c1ddd5eaf1ddce91d60d61b6bc51494ef326458365a470a" -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version_check" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" -[[package]] -name = "want" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" -dependencies = [ - "log", - "try-lock", -] - [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" @@ -2377,82 +1414,6 @@ version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" -[[package]] -name = "wasm-bindgen" -version = "0.2.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" -dependencies = [ - "bumpalo", - "lazy_static", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" - -[[package]] -name = "web-sys" -version = "0.3.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "winapi" version = "0.3.9" @@ -2483,50 +1444,3 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "winreg" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" -dependencies = [ - "winapi", -] - -[[package]] -name = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] - -[[package]] -name = "zstd" -version = "0.7.0+zstd.1.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9428752481d8372e15b1bf779ea518a179ad6c771cca2d2c60e4fbff3cc2cd52" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "3.1.0+zstd.1.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa1926623ad7fe406e090555387daf73db555b948134b4d73eac5eb08fb666d" -dependencies = [ - "libc", - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "1.5.0+zstd.1.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e6c094340240369025fc6b731b054ee2a834328fa584310ac96aa4baebdc465" -dependencies = [ - "cc", - "libc", -] diff --git a/shared-lib/Cargo.toml b/shared-lib/Cargo.toml index cf4dceaa68..ed805600e4 100644 --- a/shared-lib/Cargo.toml +++ b/shared-lib/Cargo.toml @@ -6,7 +6,6 @@ members = [ "lib-ot", "lib-ws", "lib-infra", - "backend-service", "flowy-derive", "flowy-ast", "error-code", diff --git a/shared-lib/backend-service/Cargo.toml b/shared-lib/backend-service/Cargo.toml deleted file mode 100644 index 5386524be5..0000000000 --- a/shared-lib/backend-service/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "backend-service" -version = "0.1.0" -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -flowy-folder-data-model = { path = "../flowy-folder-data-model" } -flowy-user-data-model = { path = "../flowy-user-data-model" } -flowy-collaboration = { path = "../flowy-collaboration" } - -log = "0.4.14" -lazy_static = "1.4.0" -tokio = { version = "1", features = ["rt"] } -anyhow = "1.0" -thiserror = "1.0.24" -bytes = { version = "1.0", features = ["serde"]} -reqwest = "0.11" -hyper = "0.14" -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -serde_repr = "0.1" -uuid = { version = "0.8", features = ["v4"] } -protobuf = {version = "2.18.0"} -derive_more = {version = "0.99", features = ["display"]} -tracing = { version = "0.1", features = ["log"] } -actix-web = {version = "4.0.0-beta.8", optional = true} -config = { version = "0.10.1", default-features = false, features = ["yaml"] } -serde-aux = "1.0.1" - -[features] -http_server = ["actix-web"] \ No newline at end of file diff --git a/shared-lib/backend-service/src/errors.rs b/shared-lib/backend-service/src/errors.rs deleted file mode 100644 index 8035798817..0000000000 --- a/shared-lib/backend-service/src/errors.rs +++ /dev/null @@ -1,138 +0,0 @@ -use crate::response::FlowyResponse; -use bytes::Bytes; -use serde::{Deserialize, Serialize, __private::Formatter}; -use serde_repr::*; -use std::{fmt, fmt::Debug}; - -pub type Result = std::result::Result; -use flowy_collaboration::errors::CollaborateError; -#[derive(thiserror::Error, Debug, Serialize, Deserialize, Clone)] -pub struct ServerError { - pub code: ErrorCode, - pub msg: String, -} - -macro_rules! static_error { - ($name:ident, $status:expr) => { - #[allow(non_snake_case, missing_docs)] - pub fn $name() -> ServerError { - ServerError { - code: $status, - msg: format!("{}", $status), - } - } - }; -} - -impl ServerError { - static_error!(internal, ErrorCode::InternalError); - static_error!(http, ErrorCode::HttpError); - static_error!(payload_none, ErrorCode::PayloadUnexpectedNone); - static_error!(unauthorized, ErrorCode::UserUnauthorized); - static_error!(password_not_match, ErrorCode::PasswordNotMatch); - static_error!(params_invalid, ErrorCode::ParamsInvalid); - static_error!(connect_timeout, ErrorCode::ConnectTimeout); - static_error!(connect_close, ErrorCode::ConnectClose); - static_error!(connect_cancel, ErrorCode::ConnectCancel); - static_error!(connect_refused, ErrorCode::ConnectRefused); - static_error!(record_not_found, ErrorCode::RecordNotFound); - - pub fn new(msg: String, code: ErrorCode) -> Self { - Self { code, msg } - } - - pub fn context(mut self, error: T) -> Self { - self.msg = format!("{:?}", error); - self - } - - pub fn is_record_not_found(&self) -> bool { - self.code == ErrorCode::RecordNotFound - } - - pub fn is_unauthorized(&self) -> bool { - self.code == ErrorCode::UserUnauthorized - } - - pub fn to_collaborate_error(&self) -> CollaborateError { - if self.is_record_not_found() { - CollaborateError::record_not_found() - } else { - CollaborateError::internal().context(self.msg.clone()) - } - } -} - -pub fn internal_error(e: T) -> ServerError -where - T: std::fmt::Debug, -{ - ServerError::internal().context(e) -} - -pub fn invalid_params(e: T) -> ServerError { - ServerError::params_invalid().context(e) -} - -impl std::fmt::Display for ServerError { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let msg = format!("{:?}:{}", self.code, self.msg); - f.write_str(&msg) - } -} - -impl std::convert::From<&ServerError> for FlowyResponse { - fn from(error: &ServerError) -> Self { - FlowyResponse { - data: Bytes::from(vec![]), - error: Some(error.clone()), - } - } -} - -#[derive(Serialize_repr, Deserialize_repr, PartialEq, Debug, Clone, derive_more::Display)] -#[repr(u16)] -pub enum ErrorCode { - #[display(fmt = "Unauthorized")] - UserUnauthorized = 1, - #[display(fmt = "Payload too large")] - PayloadOverflow = 2, - #[display(fmt = "Payload deserialize failed")] - PayloadSerdeFail = 3, - #[display(fmt = "Unexpected empty payload")] - PayloadUnexpectedNone = 4, - #[display(fmt = "Params is invalid")] - ParamsInvalid = 5, - - #[display(fmt = "Protobuf serde error")] - ProtobufError = 10, - #[display(fmt = "Json serde Error")] - SerdeError = 11, - - #[display(fmt = "Email address already exists")] - EmailAlreadyExists = 50, - - #[display(fmt = "Username and password do not match")] - PasswordNotMatch = 51, - - #[display(fmt = "Connect refused")] - ConnectRefused = 100, - - #[display(fmt = "Connection timeout")] - ConnectTimeout = 101, - #[display(fmt = "Connection closed")] - ConnectClose = 102, - #[display(fmt = "Connection canceled")] - ConnectCancel = 103, - - #[display(fmt = "Sql error")] - SqlError = 200, - #[display(fmt = "Record not found")] - RecordNotFound = 201, - - #[display(fmt = "Http request error")] - HttpError = 300, - - #[display(fmt = "Internal error")] - InternalError = 1000, -} diff --git a/shared-lib/backend-service/src/lib.rs b/shared-lib/backend-service/src/lib.rs deleted file mode 100644 index 18e02d6b95..0000000000 --- a/shared-lib/backend-service/src/lib.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod configuration; -pub mod errors; -pub mod request; -pub mod response; diff --git a/shared-lib/backend-service/src/middleware.rs b/shared-lib/backend-service/src/middleware.rs deleted file mode 100644 index c1e8b695c0..0000000000 --- a/shared-lib/backend-service/src/middleware.rs +++ /dev/null @@ -1,41 +0,0 @@ -use crate::{request::ResponseMiddleware, response::FlowyResponse}; -use lazy_static::lazy_static; -use std::sync::Arc; -use tokio::sync::broadcast; -lazy_static! { - pub static ref BACKEND_API_MIDDLEWARE: Arc = Arc::new(WorkspaceMiddleware::new()); -} - -pub struct WorkspaceMiddleware { - invalid_token_sender: broadcast::Sender, -} - -impl WorkspaceMiddleware { - fn new() -> Self { - let (sender, _) = broadcast::channel(10); - WorkspaceMiddleware { - invalid_token_sender: sender, - } - } - - pub fn invalid_token_subscribe(&self) -> broadcast::Receiver { - self.invalid_token_sender.subscribe() - } -} - -impl ResponseMiddleware for WorkspaceMiddleware { - fn receive_response(&self, token: &Option, response: &FlowyResponse) { - if let Some(error) = &response.error { - if error.is_unauthorized() { - log::error!("user is unauthorized"); - match token { - None => {} - Some(token) => match self.invalid_token_sender.send(token.clone()) { - Ok(_) => {} - Err(e) => log::error!("{:?}", e), - }, - } - } - } - } -} diff --git a/shared-lib/backend-service/src/request/mod.rs b/shared-lib/backend-service/src/request/mod.rs deleted file mode 100644 index fd1bf9d798..0000000000 --- a/shared-lib/backend-service/src/request/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -#![allow(clippy::module_inception)] -mod request; - -pub use request::*; diff --git a/shared-lib/backend-service/src/response/mod.rs b/shared-lib/backend-service/src/response/mod.rs deleted file mode 100644 index 886f694baf..0000000000 --- a/shared-lib/backend-service/src/response/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![allow(clippy::module_inception)] -mod response; - -#[cfg(feature = "http_server")] -mod response_http; - -pub use response::*; diff --git a/shared-lib/backend-service/src/response/response.rs b/shared-lib/backend-service/src/response/response.rs deleted file mode 100644 index 9815b3bcb8..0000000000 --- a/shared-lib/backend-service/src/response/response.rs +++ /dev/null @@ -1,100 +0,0 @@ -use crate::errors::{ErrorCode, ServerError}; -use bytes::Bytes; -use serde::{Deserialize, Serialize}; -use std::{convert::TryInto, error::Error, fmt::Debug}; -use tokio::sync::oneshot::error::RecvError; - -#[derive(Debug, Serialize, Deserialize)] -pub struct FlowyResponse { - pub data: Bytes, - #[serde(skip_serializing_if = "Option::is_none")] - pub error: Option, -} - -impl FlowyResponse { - pub fn new(data: Bytes, error: Option) -> Self { - FlowyResponse { data, error } - } - - pub fn success() -> Self { - Self::new(Bytes::new(), None) - } - - pub fn data>(mut self, data: T) -> Result { - let bytes: Bytes = data.try_into()?; - self.data = bytes; - Ok(self) - } - - pub fn pb(mut self, data: T) -> Result { - let bytes: Bytes = Bytes::from(data.write_to_bytes()?); - self.data = bytes; - Ok(self) - } -} - -impl std::convert::From for ServerError { - fn from(e: protobuf::ProtobufError) -> Self { - ServerError::internal().context(e) - } -} - -impl std::convert::From for ServerError { - fn from(error: RecvError) -> Self { - ServerError::internal().context(error) - } -} - -impl std::convert::From for ServerError { - fn from(e: serde_json::Error) -> Self { - ServerError::internal().context(e) - } -} - -impl std::convert::From for ServerError { - fn from(error: anyhow::Error) -> Self { - ServerError::internal().context(error) - } -} - -impl std::convert::From for ServerError { - fn from(error: reqwest::Error) -> Self { - if error.is_timeout() { - return ServerError::connect_timeout().context(error); - } - - if error.is_request() { - let hyper_error: Option<&hyper::Error> = error.source().unwrap().downcast_ref(); - return match hyper_error { - None => ServerError::connect_refused().context(error), - Some(hyper_error) => { - let mut code = ErrorCode::InternalError; - let msg = format!("{}", error); - if hyper_error.is_closed() { - code = ErrorCode::ConnectClose; - } - - if hyper_error.is_connect() { - code = ErrorCode::ConnectRefused; - } - - if hyper_error.is_canceled() { - code = ErrorCode::ConnectCancel; - } - - if hyper_error.is_timeout() {} - - ServerError { code, msg } - } - }; - } - - ServerError::internal().context(error) - } -} - -impl std::convert::From for ServerError { - fn from(e: uuid::Error) -> Self { - ServerError::internal().context(e) - } -} diff --git a/shared-lib/backend-service/src/response/response_http.rs b/shared-lib/backend-service/src/response/response_http.rs deleted file mode 100644 index f81bdd75a6..0000000000 --- a/shared-lib/backend-service/src/response/response_http.rs +++ /dev/null @@ -1,26 +0,0 @@ -use crate::response::*; -use actix_web::{error::ResponseError, HttpResponse}; - -use crate::errors::ServerError; -use actix_web::body::AnyBody; - -impl ResponseError for ServerError { - fn error_response(&self) -> HttpResponse { - let response: FlowyResponse = self.into(); - response.into() - } -} -impl std::convert::Into for FlowyResponse { - fn into(self) -> HttpResponse { - HttpResponse::Ok().json(self) - } -} - -impl std::convert::Into for FlowyResponse { - fn into(self) -> AnyBody { - match serde_json::to_string(&self) { - Ok(body) => AnyBody::from(body), - Err(_) => AnyBody::Empty, - } - } -} From 0c6903f793b6107ce1e66fb64426173cf04a4cd1 Mon Sep 17 00:00:00 2001 From: appflowy Date: Mon, 7 Feb 2022 14:40:45 +0800 Subject: [PATCH 7/9] refactor: refactor some crates with http_server --- frontend/rust-lib/.gitignore | 1 - frontend/rust-lib/Cargo.lock | 3038 +++++++++++++++++ frontend/rust-lib/flowy-document/Cargo.toml | 2 +- frontend/rust-lib/flowy-error/Cargo.toml | 4 +- .../src/ext/{backend.rs => http_server.rs} | 2 +- frontend/rust-lib/flowy-error/src/ext/mod.rs | 8 +- frontend/rust-lib/flowy-folder/Cargo.toml | 2 +- frontend/rust-lib/flowy-net/Cargo.toml | 5 +- .../flowy-net}/configuration/base.yaml | 0 .../flowy-net}/configuration/local.yaml | 0 .../flowy-net}/configuration/production.yaml | 0 .../rust-lib/flowy-net}/src/configuration.rs | 0 .../flowy-net/src/http_server/document.rs | 4 +- .../src/http_server/{core.rs => folder.rs} | 16 +- .../rust-lib/flowy-net/src/http_server/mod.rs | 2 +- .../flowy-net/src/http_server/user.rs | 3 +- frontend/rust-lib/flowy-net/src/lib.rs | 5 +- .../flowy-net/src/local_server/mod.rs | 2 +- .../rust-lib/flowy-net}/src/middleware.rs | 0 .../rust-lib/flowy-net/src}/request.rs | 11 +- .../flowy-sdk/src/deps_resolve/folder_deps.rs | 2 +- frontend/rust-lib/flowy-sync/Cargo.toml | 2 +- frontend/rust-lib/flowy-user/Cargo.toml | 2 +- shared-lib/Cargo.lock | 1094 +----- shared-lib/Cargo.toml | 1 - shared-lib/backend-service/Cargo.toml | 33 - shared-lib/backend-service/src/errors.rs | 138 - shared-lib/backend-service/src/lib.rs | 4 - shared-lib/backend-service/src/request/mod.rs | 4 - .../backend-service/src/response/mod.rs | 7 - .../backend-service/src/response/response.rs | 100 - .../src/response/response_http.rs | 26 - 32 files changed, 3083 insertions(+), 1435 deletions(-) create mode 100755 frontend/rust-lib/Cargo.lock rename frontend/rust-lib/flowy-error/src/ext/{backend.rs => http_server.rs} (91%) rename {shared-lib/backend-service => frontend/rust-lib/flowy-net}/configuration/base.yaml (100%) rename {shared-lib/backend-service => frontend/rust-lib/flowy-net}/configuration/local.yaml (100%) rename {shared-lib/backend-service => frontend/rust-lib/flowy-net}/configuration/production.yaml (100%) rename {shared-lib/backend-service => frontend/rust-lib/flowy-net}/src/configuration.rs (100%) rename frontend/rust-lib/flowy-net/src/http_server/{core.rs => folder.rs} (96%) rename {shared-lib/backend-service => frontend/rust-lib/flowy-net}/src/middleware.rs (100%) rename {shared-lib/backend-service/src/request => frontend/rust-lib/flowy-net/src}/request.rs (94%) delete mode 100644 shared-lib/backend-service/Cargo.toml delete mode 100644 shared-lib/backend-service/src/errors.rs delete mode 100644 shared-lib/backend-service/src/lib.rs delete mode 100644 shared-lib/backend-service/src/request/mod.rs delete mode 100644 shared-lib/backend-service/src/response/mod.rs delete mode 100644 shared-lib/backend-service/src/response/response.rs delete mode 100644 shared-lib/backend-service/src/response/response_http.rs diff --git a/frontend/rust-lib/.gitignore b/frontend/rust-lib/.gitignore index a42aa3663e..6a492f9bfd 100644 --- a/frontend/rust-lib/.gitignore +++ b/frontend/rust-lib/.gitignore @@ -4,7 +4,6 @@ # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html -Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock new file mode 100755 index 0000000000..4f2993f6dc --- /dev/null +++ b/frontend/rust-lib/Cargo.lock @@ -0,0 +1,3038 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "allo-isolate" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31644a919a9e4b0188e4569e55bbf5a78b5588ea645acffc15c29240407261bc" +dependencies = [ + "atomic", + "pin-project", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3" + +[[package]] +name = "async-stream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "171374e7e3b2504e0e5236e3b59260560f9fe94bfe9ac39ba5e4e929c5590625" +dependencies = [ + "async-stream-impl", + "futures-core", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "648ed8c8d2ce5409ccd57453d9d1b214b342a0d69376a6feda1fd6cae3299308" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-trait" +version = "0.1.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b88d82667eca772c4aa12f0f1348b3ae643424c8876448f3f7bd5787032e234c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "backtrace" +version = "0.3.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "321629d8ba6513061f26707241fa9bc89524ff1cd7a915a97ef0c62c666ce1b6" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bit-set" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" + +[[package]] +name = "bytecount" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" +dependencies = [ + "serde", +] + +[[package]] +name = "cast" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c24dab4283a142afa2fdca129b80ad2c6284e073930f964c3a1293c225ee39a" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "cc" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi", +] + +[[package]] +name = "claim" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ad37958d55b29a7088909368968d2fe876a24c203f8441195130f3b15194b9" +dependencies = [ + "autocfg", +] + +[[package]] +name = "claim" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81099d6bb72e1df6d50bb2347224b666a670912bb7f06dbe867a4a070ab3ce8" +dependencies = [ + "autocfg", +] + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "bitflags", + "textwrap", + "unicode-width", +] + +[[package]] +name = "color-eyre" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f1885697ee8a177096d42f158922251a41973117f6d8a234cee94b9509157b7" +dependencies = [ + "backtrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "core-foundation" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "cpufeatures" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +dependencies = [ + "libc", +] + +[[package]] +name = "criterion" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1604dafd25fba2fe2d5895a9da139f8dc9b319a5fe5354ca137cbbce4e178d10" +dependencies = [ + "atty", + "cast", + "clap", + "criterion-plot", + "csv", + "itertools", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_cbor", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d00996de9f2f7559f7f4dc286073197f83e92256a59ed395f9aac01fe717da57" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae5588f6b3c3cb05239e90bd110f257254aecd01e4635400391aeae07497845" +dependencies = [ + "cfg-if", + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97242a70df9b89a65d0b6df3c4bf5b9ce03c5b7309019777fbde37e7537f8762" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b979d76c9fcb84dffc80a73f7290da0f83e4c95773494674cb44b76d13a7a110" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcae03edb34f947e64acdb1c33ec169824e20657e9ecb61cef6c8c74dcb8120" +dependencies = [ + "cfg-if", + "lazy_static", +] + +[[package]] +name = "csv" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" +dependencies = [ + "bstr", + "csv-core", + "itoa 0.4.8", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +dependencies = [ + "memchr", +] + +[[package]] +name = "darling" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0d720b8683f8dd83c65155f0530560cba68cd2bf395f6513a483caee57ff7f4" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a340f241d2ceed1deb47ae36c4144b2707ec7dd0b649f894cb39bb595986324" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c41b3b7352feb3211a0d743dc5700a4e3b60f51bd2b368892d1e0f9a95f44b" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "dart-ffi" +version = "0.1.0" +dependencies = [ + "allo-isolate", + "byteorder", + "bytes", + "dart-notify", + "ffi-support", + "flowy-derive", + "flowy-sdk", + "lib-dispatch", + "log", + "once_cell", + "protobuf", + "serde", + "serde_json", + "tokio", +] + +[[package]] +name = "dart-notify" +version = "0.1.0" +dependencies = [ + "allo-isolate", + "bytes", + "flowy-derive", + "lazy_static", + "lib-dispatch", + "log", + "protobuf", +] + +[[package]] +name = "dashmap" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" +dependencies = [ + "cfg-if", + "num_cpus", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", +] + +[[package]] +name = "diesel" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b28135ecf6b7d446b43e27e225622a038cc4e2930a1022f51cdb97ada19b8e4d" +dependencies = [ + "byteorder", + "diesel_derives", + "libsqlite3-sys", +] + +[[package]] +name = "diesel_derives" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "diesel_migrations" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf3cde8413353dc7f5d72fa8ce0b99a560a359d2c5ef1e5817ca731cd9008f4c" +dependencies = [ + "migrations_internals", + "migrations_macros", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "dissimilar" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31ad93652f40969dead8d4bf897a41e9462095152eb21c56e5830537e41179dd" + +[[package]] +name = "dyn-clone" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee2626afccd7561a06cf1367e2950c4718ea04565e20fb5029b6c7d8ad09abcf" + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "encoding_rs" +version = "0.8.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "error-chain" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e791d3be96241c77c43846b665ef1384606da2cd2a48730abe606a12906e02" +dependencies = [ + "backtrace", +] + +[[package]] +name = "error-code" +version = "0.1.0" +dependencies = [ + "derive_more", + "flowy-derive", + "protobuf", +] + +[[package]] +name = "eyre" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "221239d1d5ea86bf5d6f91c9d6bc3646ffe471b08ff9b0f91c44f115ac969d2b" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "fake" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6479fa2c7e83ddf8be7d435421e093b072ca891b99a49bc84eba098f4044f818" +dependencies = [ + "rand 0.7.3", +] + +[[package]] +name = "fancy-regex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe09872bd11351a75f22b24c3769fc863e8212d926d6db46b94ad710d14cc5cc" +dependencies = [ + "bit-set", + "regex", +] + +[[package]] +name = "fastrand" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "779d043b6a0b90cc4c0ed7ee380a6504394cee7efd7db050e3774eee387324b2" +dependencies = [ + "instant", +] + +[[package]] +name = "ffi-support" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27838c6815cfe9de2d3aeb145ffd19e565f577414b33f3bdbf42fe040e9e0ff6" +dependencies = [ + "lazy_static", + "log", +] + +[[package]] +name = "flowy-ast" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "flowy-collaboration" +version = "0.1.0" +dependencies = [ + "async-stream", + "bytes", + "chrono", + "dashmap", + "dissimilar", + "flowy-derive", + "flowy-folder-data-model", + "futures", + "lib-infra", + "lib-ot", + "log", + "md5", + "parking_lot", + "protobuf", + "serde", + "serde_json", + "strum", + "strum_macros", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "flowy-database" +version = "0.1.0" +dependencies = [ + "diesel", + "diesel_derives", + "diesel_migrations", + "lazy_static", + "lib-sqlite", + "log", +] + +[[package]] +name = "flowy-derive" +version = "0.1.0" +dependencies = [ + "flowy-ast", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "flowy-document" +version = "0.1.0" +dependencies = [ + "async-stream", + "async-trait", + "bytecount", + "byteorder", + "bytes", + "chrono", + "color-eyre", + "criterion", + "dart-notify", + "dashmap", + "derive_more", + "diesel", + "diesel_derives", + "flowy-collaboration", + "flowy-database", + "flowy-derive", + "flowy-document", + "flowy-error", + "flowy-sync", + "flowy-test", + "futures", + "futures-util", + "lib-dispatch", + "lib-infra", + "lib-ot", + "lib-ws", + "log", + "parking_lot", + "pin-project", + "protobuf", + "rand 0.7.3", + "serde", + "serde_json", + "strum", + "strum_macros", + "tokio", + "tracing", + "unicode-segmentation", + "url", +] + +[[package]] +name = "flowy-error" +version = "0.1.0" +dependencies = [ + "bytes", + "error-code", + "flowy-collaboration", + "flowy-database", + "flowy-derive", + "http-response", + "lib-dispatch", + "lib-ot", + "lib-sqlite", + "protobuf", + "r2d2", + "serde_json", +] + +[[package]] +name = "flowy-folder" +version = "0.1.0" +dependencies = [ + "bincode", + "bytes", + "chrono", + "crossbeam", + "crossbeam-utils", + "dart-notify", + "derive_more", + "diesel", + "diesel_derives", + "flowy-collaboration", + "flowy-database", + "flowy-derive", + "flowy-document", + "flowy-error", + "flowy-folder", + "flowy-folder-data-model", + "flowy-sync", + "flowy-test", + "futures", + "futures-core", + "lazy_static", + "lib-dispatch", + "lib-infra", + "lib-ot", + "lib-sqlite", + "log", + "parking_lot", + "pin-project", + "protobuf", + "serde", + "serde_json", + "serial_test", + "strum", + "strum_macros", + "tokio", + "tracing", +] + +[[package]] +name = "flowy-folder-data-model" +version = "0.1.0" +dependencies = [ + "bytes", + "chrono", + "derive_more", + "error-code", + "flowy-derive", + "log", + "protobuf", + "serde", + "serde_json", + "strum", + "strum_macros", + "unicode-segmentation", + "uuid", +] + +[[package]] +name = "flowy-net" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-stream", + "bytes", + "dashmap", + "flowy-collaboration", + "flowy-derive", + "flowy-document", + "flowy-error", + "flowy-folder", + "flowy-folder-data-model", + "flowy-user", + "flowy-user-data-model", + "futures-util", + "http-response", + "lazy_static", + "lib-dispatch", + "lib-infra", + "lib-ws", + "parking_lot", + "protobuf", + "strum", + "strum_macros", + "tokio", + "tracing", +] + +[[package]] +name = "flowy-sdk" +version = "0.1.0" +dependencies = [ + "bincode", + "bytes", + "claim 0.5.0", + "color-eyre", + "flowy-collaboration", + "flowy-database", + "flowy-document", + "flowy-folder", + "flowy-net", + "flowy-sync", + "flowy-user", + "futures-core", + "futures-util", + "lib-dispatch", + "lib-infra", + "lib-log", + "lib-ws", + "log", + "parking_lot", + "protobuf", + "serde", + "tokio", + "tracing", +] + +[[package]] +name = "flowy-sync" +version = "0.1.0" +dependencies = [ + "async-stream", + "bytes", + "dashmap", + "diesel", + "diesel_derives", + "flowy-collaboration", + "flowy-database", + "flowy-error", + "futures-util", + "lib-infra", + "lib-ot", + "lib-ws", + "parking_lot", + "protobuf", + "serde", + "serde_json", + "strum", + "strum_macros", + "tokio", + "tracing", +] + +[[package]] +name = "flowy-test" +version = "0.1.0" +dependencies = [ + "bincode", + "bytes", + "claim 0.4.0", + "claim 0.5.0", + "fake", + "flowy-collaboration", + "flowy-folder", + "flowy-net", + "flowy-sdk", + "flowy-user", + "futures", + "futures-util", + "lib-dispatch", + "lib-infra", + "lib-ot", + "log", + "protobuf", + "quickcheck", + "quickcheck_macros", + "serde", + "serde_json", + "serial_test", + "thread-id", + "tokio", +] + +[[package]] +name = "flowy-user" +version = "0.1.0" +dependencies = [ + "bytes", + "dart-notify", + "dashmap", + "derive_more", + "diesel", + "diesel_derives", + "flowy-database", + "flowy-derive", + "flowy-error", + "flowy-test", + "flowy-user-data-model", + "futures", + "futures-core", + "lazy_static", + "lib-dispatch", + "lib-infra", + "lib-sqlite", + "log", + "once_cell", + "parking_lot", + "pin-project", + "protobuf", + "r2d2", + "serde", + "serde_json", + "serial_test", + "strum", + "strum_macros", + "thread-id", + "thread_local", + "tokio", + "tracing", +] + +[[package]] +name = "flowy-user-data-model" +version = "0.1.0" +dependencies = [ + "bytes", + "derive_more", + "error-code", + "fancy-regex", + "flowy-derive", + "lazy_static", + "log", + "protobuf", + "serde", + "unicode-segmentation", + "validator", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28560757fe2bb34e79f907794bb6b22ae8b0e5c669b638a1132f2592b19035b4" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3dda0b6588335f360afc675d0564c17a77a2bda81ca178a4b6081bd86c7f0b" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0c8ff0461b82559810cdccfde3215c3f373807f5e5232b71479bff7bb2583d7" + +[[package]] +name = "futures-executor" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29d6d2ff5bb10fb95c85b8ce46538a2e5f5e7fdc755623a7d4529ab8a4ed9d2a" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f9d34af5a1aac6fb380f735fe510746c38067c5bf16c7fd250280503c971b2" + +[[package]] +name = "futures-macro" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbd947adfffb0efc70599b3ddcf7b5597bb5fa9e245eb99f62b3a5f7bb8bd3c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3055baccb68d74ff6480350f8d6eb8fcfa3aa11bdc1a1ae3afdd0514617d508" + +[[package]] +name = "futures-task" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ee7c6485c30167ce4dfb83ac568a849fe53274c831081476ee13e0dce1aad72" + +[[package]] +name = "futures-util" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b5cf40b47a271f77a8b1bec03ca09044d99d2372c0de244e66430761127164" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "gethostname" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e692e296bfac1d2533ef168d0b60ff5897b8b70a4009276834014dd8924cc028" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", +] + +[[package]] +name = "gimli" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" + +[[package]] +name = "h2" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9de88456263e249e241fcd211d3954e2c9b0ef7ccfc235a444eb367cae3689" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "http" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.1", +] + +[[package]] +name = "http-body" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "http-response" +version = "0.1.0" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Server#0f3b0623506b3bc5855473eeb823e21ead2316a8" +dependencies = [ + "anyhow", + "bytes", + "derive_more", + "hyper", + "protobuf", + "reqwest", + "serde", + "serde_json", + "serde_repr", + "thiserror", + "uuid", +] + +[[package]] +name = "httparse" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7ec3e62bdc98a2f0393a5048e4c30ef659440ea6e0e572965103e72bd836f55" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa 0.4.8", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "indexmap" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "ipnet" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" + +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "js-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lib-dispatch" +version = "0.1.0" +dependencies = [ + "bincode", + "bytes", + "dashmap", + "derivative", + "dyn-clone", + "env_logger 0.8.4", + "futures", + "futures-channel", + "futures-core", + "futures-util", + "lazy_static", + "log", + "paste", + "pin-project", + "protobuf", + "serde", + "serde_json", + "serde_with", + "thread-id", + "tokio", + "tracing", + "uuid", +] + +[[package]] +name = "lib-infra" +version = "0.1.0" +dependencies = [ + "bytes", + "chrono", + "futures-core", + "log", + "pin-project", + "rand 0.8.4", + "tokio", + "uuid", +] + +[[package]] +name = "lib-log" +version = "0.1.0" +dependencies = [ + "chrono", + "lazy_static", + "log", + "serde", + "serde_json", + "tracing", + "tracing-appender", + "tracing-bunyan-formatter", + "tracing-core", + "tracing-futures", + "tracing-log", + "tracing-subscriber", +] + +[[package]] +name = "lib-ot" +version = "0.1.0" +dependencies = [ + "anyhow", + "bytecount", + "bytes", + "dashmap", + "derive_more", + "lazy_static", + "log", + "md5", + "serde", + "serde_json", + "strum", + "strum_macros", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "lib-sqlite" +version = "0.1.0" +dependencies = [ + "diesel", + "diesel_derives", + "diesel_migrations", + "error-chain", + "lazy_static", + "libsqlite3-sys", + "log", + "r2d2", + "scheduled-thread-pool", +] + +[[package]] +name = "lib-ws" +version = "0.1.0" +dependencies = [ + "bytes", + "dashmap", + "flowy-derive", + "futures", + "futures-channel", + "futures-core", + "futures-util", + "lib-infra", + "log", + "parking_lot", + "paste", + "pin-project", + "protobuf", + "strum", + "strum_macros", + "tokio", + "tokio-tungstenite", + "tracing", + "url", +] + +[[package]] +name = "libc" +version = "0.2.112" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" + +[[package]] +name = "libsqlite3-sys" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290b64917f8b0cb885d9de0f9959fe1f775d7fa12f1da2db9001c1c8ab60f89d" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "lock_api" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matchers" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "migrations_internals" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b4fc84e4af020b837029e017966f86a1c2d5e83e64b589963d5047525995860" +dependencies = [ + "diesel", +] + +[[package]] +name = "migrations_macros" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9753f12909fd8d923f75ae5c3258cae1ed3c8ec052e1b38c93c21a6d157f789c" +dependencies = [ + "migrations_internals", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + +[[package]] +name = "mio" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "native-tls" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl" +version = "0.10.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-sys", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "owo-colors" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2386b4ebe91c2f7f51082d4cefa145d030e33a1842a96b12e4885cc3c01f7a55" + +[[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", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall 0.2.10", + "smallvec", + "winapi", +] + +[[package]] +name = "paste" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5" + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" + +[[package]] +name = "plotters" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d88417318da0eaf0fdcdb51a0ee6c3bed624333bff8f946733049380be67ac1c" + +[[package]] +name = "plotters-svg" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521fa9638fa597e1dc53e9412a4f9cefb01187ee1f7413076f9e6749e2885ba9" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "protobuf" +version = "2.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47c327e191621a2158159df97cdbc2e7074bb4e940275e35abf38eb3d2595754" + +[[package]] +name = "quickcheck" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44883e74aa97ad63db83c4bf8ca490f02b2fc02f92575e720c8551e843c945f" +dependencies = [ + "env_logger 0.7.1", + "log", + "rand 0.7.3", + "rand_core 0.5.1", +] + +[[package]] +name = "quickcheck_macros" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608c156fd8e97febc07dc9c2e2c80bf74cfc6ef26893eae3daf8bc2bc94a4b7f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r2d2" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "545c5bc2b880973c9c10e4067418407a0ccaa3091781d1671d46eb35107cb26f" +dependencies = [ + "log", + "parking_lot", + "scheduled-thread-pool", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc 0.2.0", +] + +[[package]] +name = "rand" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.3", + "rand_hc 0.3.1", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.3", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom 0.2.3", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_hc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core 0.6.3", +] + +[[package]] +name = "rayon" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "reqwest" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f242f1488a539a79bac6dbe7c8609ae43b7914b7736210f239a37cccb32525" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "lazy_static", + "log", + "mime", + "native-tls", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +dependencies = [ + "lazy_static", + "winapi", +] + +[[package]] +name = "scheduled-thread-pool" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6f74fd1204073fa02d5d5d68bec8021be4c38690b61264b2fdb48083d0e7d7" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "security-framework" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525bc1abfda2e1998d152c45cf13e696f76d0a4972310b22fac1658b05df7c87" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" + +[[package]] +name = "serde" +version = "1.0.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97565067517b60e2d1ea8b268e59ce036de907ac523ad83a0475da04e818989a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed201699328568d8d08208fdd080e3ff594e6c422e438b6705905da01005d537" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee2bb9cd061c5865d345bb02ca49fcef1391741b672b54a0bf7b679badec3142" +dependencies = [ + "itoa 1.0.1", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98d0516900518c29efa217c298fa1f4e6c6ffc85ae29fd7f4ee48f176e1a9ed5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" +dependencies = [ + "form_urlencoded", + "itoa 0.4.8", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad6056b4cb69b6e43e3a0f055def223380baecc99da683884f205bf347f7c4b3" +dependencies = [ + "rustversion", + "serde", + "serde_with_macros", +] + +[[package]] +name = "serde_with_macros" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12e47be9471c72889ebafb5e14d5ff930d89ae7a67bbdb5f8abb564f845a927e" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serial_test" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0bccbcf40c8938196944a3da0e133e031a33f4d6b72db3bda3cc556e361905d" +dependencies = [ + "lazy_static", + "parking_lot", + "serial_test_derive", +] + +[[package]] +name = "serial_test_derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2acd6defeddb41eb60bb468f8825d0cfd0c2a76bc03bfd235b6a1dc4f6a1ad5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sha-1" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +dependencies = [ + "block-buffer", + "cfg-if", + "cpufeatures", + "digest", + "opaque-debug", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" + +[[package]] +name = "smallvec" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" + +[[package]] +name = "socket2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strum" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaf86bbcfd1fa9670b7a129f64fc0c9fcbbfe4f1bc4210e9e98fe71ffc12cde2" + +[[package]] +name = "strum_macros" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a684ac3dcd8913827e18cd09a68384ee66c1de24157e3c556c9ab16d85695fb7" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall 0.2.10", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread-id" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1" +dependencies = [ + "libc", + "redox_syscall 0.1.57", + "winapi", +] + +[[package]] +name = "thread_local" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" +dependencies = [ + "once_cell", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "tinyvec" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbbf1c778ec206785635ce8ad57fe52b3009ae9e0c9f574a728f3049d3e55838" +dependencies = [ + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "once_cell", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "511de3f85caf1c98983545490c3d09685fa8eb634e57eec22bb4db271f46cbd8" +dependencies = [ + "futures-util", + "log", + "pin-project", + "tokio", + "tungstenite", +] + +[[package]] +name = "tokio-util" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower-service" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" + +[[package]] +name = "tracing" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-appender" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9965507e507f12c8901432a33e31131222abac31edd90cabbcf85cf544b7127a" +dependencies = [ + "chrono", + "crossbeam-channel", + "tracing-subscriber", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-bunyan-formatter" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c408910c9b7eabc0215fe2b4a89f8ec95581a91cea1f7619f7c78caf14cbc2a1" +dependencies = [ + "chrono", + "gethostname", + "log", + "serde", + "serde_json", + "tracing", + "tracing-core", + "tracing-log", + "tracing-subscriber", +] + +[[package]] +name = "tracing-core" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "tracing-log" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6923477a48e41c1951f1999ef8bb5a3023eb723ceadafe78ffb65dc366761e3" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-serde" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" +dependencies = [ + "ansi_term", + "chrono", + "lazy_static", + "matchers", + "regex", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", + "tracing-serde", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "tungstenite" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0b2d8558abd2e276b0a8df5c05a2ec762609344191e5fd23e292c910e9165b5" +dependencies = [ + "base64", + "byteorder", + "bytes", + "http", + "httparse", + "log", + "rand 0.8.4", + "sha-1", + "thiserror", + "url", + "utf-8", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "unicode-bidi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +dependencies = [ + "getrandom 0.2.3", + "serde", +] + +[[package]] +name = "validator" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d6937c33ec6039d8071bcf72933146b5bbe378d645d8fa59bdadabfc2a249" +dependencies = [ + "idna", + "lazy_static", + "regex", + "serde", + "serde_derive", + "serde_json", + "url", + "validator_types", +] + +[[package]] +name = "validator_types" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad9680608df133af2c1ddd5eaf1ddce91d60d61b6bc51494ef326458365a470a" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasm-bindgen" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" + +[[package]] +name = "web-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "winreg" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" +dependencies = [ + "winapi", +] diff --git a/frontend/rust-lib/flowy-document/Cargo.toml b/frontend/rust-lib/flowy-document/Cargo.toml index 11f282c2f3..0dbecccd3b 100644 --- a/frontend/rust-lib/flowy-document/Cargo.toml +++ b/frontend/rust-lib/flowy-document/Cargo.toml @@ -17,7 +17,7 @@ derive_more = {version = "0.99", features = ["display"]} lib-dispatch = { path = "../lib-dispatch" } flowy-database = { path = "../flowy-database" } flowy-sync = { path = "../flowy-sync" } -flowy-error = { path = "../flowy-error", features = ["collaboration", "ot", "backend", "serde", "db"] } +flowy-error = { path = "../flowy-error", features = ["collaboration", "ot", "http_server", "serde", "db"] } dart-notify = { path = "../dart-notify" } diesel = {version = "1.4.8", features = ["sqlite"]} diff --git a/frontend/rust-lib/flowy-error/Cargo.toml b/frontend/rust-lib/flowy-error/Cargo.toml index 16da001014..30f8d2c691 100644 --- a/frontend/rust-lib/flowy-error/Cargo.toml +++ b/frontend/rust-lib/flowy-error/Cargo.toml @@ -16,7 +16,7 @@ bytes = "1.0" flowy-collaboration = { path = "../../../shared-lib/flowy-collaboration", optional = true} lib-ot = { path = "../../../shared-lib/lib-ot", optional = true} serde_json = {version = "1.0", optional = true} -backend-service = { path = "../../../shared-lib/backend-service", optional = true} +http-response = { git = "https://github.com/AppFlowy-IO/AppFlowy-Server", optional = true} flowy-database = { path = "../flowy-database", optional = true} r2d2 = { version = "0.8", optional = true} lib-sqlite = { path = "../lib-sqlite", optional = true } @@ -25,5 +25,5 @@ lib-sqlite = { path = "../lib-sqlite", optional = true } collaboration = ["flowy-collaboration"] ot = ["lib-ot"] serde = ["serde_json"] -backend = ["backend-service"] +http_server = ["http-response"] db = ["flowy-database", "lib-sqlite", "r2d2"] diff --git a/frontend/rust-lib/flowy-error/src/ext/backend.rs b/frontend/rust-lib/flowy-error/src/ext/http_server.rs similarity index 91% rename from frontend/rust-lib/flowy-error/src/ext/backend.rs rename to frontend/rust-lib/flowy-error/src/ext/http_server.rs index 3c901ef2a6..960dd59146 100644 --- a/frontend/rust-lib/flowy-error/src/ext/backend.rs +++ b/frontend/rust-lib/flowy-error/src/ext/http_server.rs @@ -1,6 +1,6 @@ use crate::FlowyError; -use backend_service::errors::{ErrorCode as ServerErrorCode, ServerError}; use error_code::ErrorCode; +use http_response::errors::{ErrorCode as ServerErrorCode, ServerError}; impl std::convert::From for FlowyError { fn from(error: ServerError) -> Self { diff --git a/frontend/rust-lib/flowy-error/src/ext/mod.rs b/frontend/rust-lib/flowy-error/src/ext/mod.rs index 8bd455de93..6468d6777a 100644 --- a/frontend/rust-lib/flowy-error/src/ext/mod.rs +++ b/frontend/rust-lib/flowy-error/src/ext/mod.rs @@ -16,10 +16,10 @@ mod serde; pub use serde::*; // -#[cfg(feature = "backend")] -mod backend; -#[cfg(feature = "backend")] -pub use backend::*; +#[cfg(feature = "http_server")] +mod http_server; +#[cfg(feature = "http_server")] +pub use http_server::*; #[cfg(feature = "db")] mod database; diff --git a/frontend/rust-lib/flowy-folder/Cargo.toml b/frontend/rust-lib/flowy-folder/Cargo.toml index b8ae1e251b..f98ecd51e3 100644 --- a/frontend/rust-lib/flowy-folder/Cargo.toml +++ b/frontend/rust-lib/flowy-folder/Cargo.toml @@ -14,7 +14,7 @@ lib-infra = { path = "../../../shared-lib/lib-infra" } flowy-document = { path = "../flowy-document" } flowy-database = { path = "../flowy-database" } -flowy-error = { path = "../flowy-error", features = ["db", "backend"]} +flowy-error = { path = "../flowy-error", features = ["db", "http_server"]} dart-notify = { path = "../dart-notify" } lib-dispatch = { path = "../lib-dispatch" } lib-sqlite = { path = "../lib-sqlite" } diff --git a/frontend/rust-lib/flowy-net/Cargo.toml b/frontend/rust-lib/flowy-net/Cargo.toml index d67e12606b..04bb1886d9 100644 --- a/frontend/rust-lib/flowy-net/Cargo.toml +++ b/frontend/rust-lib/flowy-net/Cargo.toml @@ -7,10 +7,9 @@ edition = "2018" [dependencies] lib-dispatch = { path = "../lib-dispatch" } -flowy-error = { path = "../flowy-error", features = ["collaboration"] } +flowy-error = { path = "../flowy-error", features = ["collaboration", "http_server"] } flowy-derive = { path = "../../../shared-lib/flowy-derive" } flowy-collaboration = { path = "../../../shared-lib/flowy-collaboration"} -backend-service = { path = "../../../shared-lib/backend-service" } flowy-folder-data-model = { path = "../../../shared-lib/flowy-folder-data-model" } flowy-user-data-model = { path = "../../../shared-lib/flowy-user-data-model"} flowy-folder = { path = "../flowy-folder" } @@ -30,5 +29,7 @@ tracing = { version = "0.1", features = ["log"] } dashmap = {version = "4.0"} async-stream = "0.3.2" futures-util = "0.3.15" +http-response = { git = "https://github.com/AppFlowy-IO/AppFlowy-Server", features = ["with_reqwest"]} + [features] http_server = [] \ No newline at end of file diff --git a/shared-lib/backend-service/configuration/base.yaml b/frontend/rust-lib/flowy-net/configuration/base.yaml similarity index 100% rename from shared-lib/backend-service/configuration/base.yaml rename to frontend/rust-lib/flowy-net/configuration/base.yaml diff --git a/shared-lib/backend-service/configuration/local.yaml b/frontend/rust-lib/flowy-net/configuration/local.yaml similarity index 100% rename from shared-lib/backend-service/configuration/local.yaml rename to frontend/rust-lib/flowy-net/configuration/local.yaml diff --git a/shared-lib/backend-service/configuration/production.yaml b/frontend/rust-lib/flowy-net/configuration/production.yaml similarity index 100% rename from shared-lib/backend-service/configuration/production.yaml rename to frontend/rust-lib/flowy-net/configuration/production.yaml diff --git a/shared-lib/backend-service/src/configuration.rs b/frontend/rust-lib/flowy-net/src/configuration.rs similarity index 100% rename from shared-lib/backend-service/src/configuration.rs rename to frontend/rust-lib/flowy-net/src/configuration.rs diff --git a/frontend/rust-lib/flowy-net/src/http_server/document.rs b/frontend/rust-lib/flowy-net/src/http_server/document.rs index 8615ad1f77..5ab9c31440 100644 --- a/frontend/rust-lib/flowy-net/src/http_server/document.rs +++ b/frontend/rust-lib/flowy-net/src/http_server/document.rs @@ -1,11 +1,11 @@ -use backend_service::{ +use crate::{ configuration::*, request::{HttpRequestBuilder, ResponseMiddleware}, - response::FlowyResponse, }; use flowy_collaboration::entities::document_info::{CreateDocParams, DocumentId, DocumentInfo, ResetDocumentParams}; use flowy_document::DocumentCloudService; use flowy_error::FlowyError; +use http_response::response::FlowyResponse; use lazy_static::lazy_static; use lib_infra::future::FutureResult; use std::sync::Arc; diff --git a/frontend/rust-lib/flowy-net/src/http_server/core.rs b/frontend/rust-lib/flowy-net/src/http_server/folder.rs similarity index 96% rename from frontend/rust-lib/flowy-net/src/http_server/core.rs rename to frontend/rust-lib/flowy-net/src/http_server/folder.rs index aa5e56a641..535c180bc8 100644 --- a/frontend/rust-lib/flowy-net/src/http_server/core.rs +++ b/frontend/rust-lib/flowy-net/src/http_server/folder.rs @@ -1,8 +1,6 @@ -use backend_service::{ +use crate::{ configuration::{ClientServerConfiguration, HEADER_TOKEN}, - errors::ServerError, request::{HttpRequestBuilder, ResponseMiddleware}, - response::FlowyResponse, }; use flowy_error::FlowyError; use flowy_folder_data_model::entities::{ @@ -13,6 +11,8 @@ use flowy_folder_data_model::entities::{ }; use flowy_folder::event_map::FolderCouldServiceV1; +use http_response::errors::ServerError; +use http_response::response::FlowyResponse; use lazy_static::lazy_static; use lib_infra::future::FutureResult; use std::sync::Arc; @@ -338,17 +338,17 @@ pub async fn read_trash_request(token: &str, url: &str) -> Result = Arc::new(CoreResponseMiddleware::new()); + static ref MIDDLEWARE: Arc = Arc::new(FolderResponseMiddleware::new()); } -pub struct CoreResponseMiddleware { +pub struct FolderResponseMiddleware { invalid_token_sender: broadcast::Sender, } -impl CoreResponseMiddleware { +impl FolderResponseMiddleware { fn new() -> Self { let (sender, _) = broadcast::channel(10); - CoreResponseMiddleware { + FolderResponseMiddleware { invalid_token_sender: sender, } } @@ -359,7 +359,7 @@ impl CoreResponseMiddleware { } } -impl ResponseMiddleware for CoreResponseMiddleware { +impl ResponseMiddleware for FolderResponseMiddleware { fn receive_response(&self, token: &Option, response: &FlowyResponse) { if let Some(error) = &response.error { if error.is_unauthorized() { diff --git a/frontend/rust-lib/flowy-net/src/http_server/mod.rs b/frontend/rust-lib/flowy-net/src/http_server/mod.rs index ee2bdb2073..53fd3bdead 100644 --- a/frontend/rust-lib/flowy-net/src/http_server/mod.rs +++ b/frontend/rust-lib/flowy-net/src/http_server/mod.rs @@ -1,3 +1,3 @@ -pub mod core; pub mod document; +pub mod folder; pub mod user; diff --git a/frontend/rust-lib/flowy-net/src/http_server/user.rs b/frontend/rust-lib/flowy-net/src/http_server/user.rs index 82d173a15a..fd077c776e 100644 --- a/frontend/rust-lib/flowy-net/src/http_server/user.rs +++ b/frontend/rust-lib/flowy-net/src/http_server/user.rs @@ -1,9 +1,10 @@ -use backend_service::{configuration::*, errors::ServerError, request::HttpRequestBuilder}; +use crate::{configuration::*, request::HttpRequestBuilder}; use flowy_error::FlowyError; use flowy_user::event_map::UserCloudService; use flowy_user_data_model::entities::{ SignInParams, SignInResponse, SignUpParams, SignUpResponse, UpdateUserParams, UserProfile, }; +use http_response::errors::ServerError; use lib_infra::future::FutureResult; pub struct UserHttpCloudService { diff --git a/frontend/rust-lib/flowy-net/src/lib.rs b/frontend/rust-lib/flowy-net/src/lib.rs index 9b646d6f5a..111f9e36e8 100644 --- a/frontend/rust-lib/flowy-net/src/lib.rs +++ b/frontend/rust-lib/flowy-net/src/lib.rs @@ -1,10 +1,13 @@ +mod configuration; pub mod entities; mod event; mod handlers; pub mod http_server; pub mod local_server; +mod middleware; pub mod module; pub mod protobuf; +mod request; pub mod ws; -pub use backend_service::configuration::{get_client_server_configuration, ClientServerConfiguration}; +pub use crate::configuration::{get_client_server_configuration, ClientServerConfiguration}; diff --git a/frontend/rust-lib/flowy-net/src/local_server/mod.rs b/frontend/rust-lib/flowy-net/src/local_server/mod.rs index e62b0680c4..3cbc28664d 100644 --- a/frontend/rust-lib/flowy-net/src/local_server/mod.rs +++ b/frontend/rust-lib/flowy-net/src/local_server/mod.rs @@ -1,4 +1,4 @@ -use backend_service::configuration::ClientServerConfiguration; +use crate::configuration::ClientServerConfiguration; use tokio::sync::{broadcast, mpsc}; mod persistence; diff --git a/shared-lib/backend-service/src/middleware.rs b/frontend/rust-lib/flowy-net/src/middleware.rs similarity index 100% rename from shared-lib/backend-service/src/middleware.rs rename to frontend/rust-lib/flowy-net/src/middleware.rs diff --git a/shared-lib/backend-service/src/request/request.rs b/frontend/rust-lib/flowy-net/src/request.rs similarity index 94% rename from shared-lib/backend-service/src/request/request.rs rename to frontend/rust-lib/flowy-net/src/request.rs index 6f0eb5aa4a..710e79adad 100644 --- a/shared-lib/backend-service/src/request/request.rs +++ b/frontend/rust-lib/flowy-net/src/request.rs @@ -1,5 +1,7 @@ -use crate::{configuration::HEADER_TOKEN, errors::ServerError, response::FlowyResponse}; +use crate::configuration::HEADER_TOKEN; use bytes::Bytes; +use http_response::errors::ServerError; +use http_response::response::FlowyResponse; use hyper::http; use protobuf::ProtobufError; use reqwest::{header::HeaderMap, Client, Method, Response}; @@ -141,7 +143,7 @@ impl HttpRequestBuilder { let method = self.method.clone(); let headers = self.headers.clone(); - // reqwest client is not 'Sync' by channel is. + // reqwest client is not 'Sync' but channel is. tokio::spawn(async move { let client = default_client(); let mut builder = client.request(method.clone(), url).headers(headers); @@ -153,7 +155,10 @@ impl HttpRequestBuilder { let _ = tx.send(response); }); - let response = rx.await??; + let response = rx.await.map_err(|e| { + let mag = format!("Receive http response channel error: {}", e); + ServerError::internal().context(mag) + })??; tracing::trace!("Http Response: {:?}", response); let flowy_response = flowy_response_from(response).await?; let token = self.token(); diff --git a/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs b/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs index 8e23f64863..68a5a1011e 100644 --- a/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs +++ b/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs @@ -9,7 +9,7 @@ use flowy_folder::{ }; use flowy_net::ClientServerConfiguration; use flowy_net::{ - http_server::core::FolderHttpCloudService, local_server::LocalServer, ws::connection::FlowyWebSocketConnect, + http_server::folder::FolderHttpCloudService, local_server::LocalServer, ws::connection::FlowyWebSocketConnect, }; use flowy_sync::{RevisionWebSocket, WSStateReceiver}; use flowy_user::services::UserSession; diff --git a/frontend/rust-lib/flowy-sync/Cargo.toml b/frontend/rust-lib/flowy-sync/Cargo.toml index c7c39ffd1d..2aadd7b3be 100644 --- a/frontend/rust-lib/flowy-sync/Cargo.toml +++ b/frontend/rust-lib/flowy-sync/Cargo.toml @@ -11,7 +11,7 @@ lib-ot = { path = "../../../shared-lib/lib-ot" } lib-ws = { path = "../../../shared-lib/lib-ws" } lib-infra = { path = "../../../shared-lib/lib-infra" } flowy-database = { path = "../flowy-database" } -flowy-error = { path = "../flowy-error", features = ["collaboration", "ot", "backend", "serde", "db"] } +flowy-error = { path = "../flowy-error", features = ["collaboration", "ot", "http_server", "serde", "db"] } diesel = {version = "1.4.8", features = ["sqlite"]} diesel_derives = {version = "1.4.1", features = ["sqlite"]} protobuf = {version = "2.18.0"} diff --git a/frontend/rust-lib/flowy-user/Cargo.toml b/frontend/rust-lib/flowy-user/Cargo.toml index 78092c9925..3f7e1021c3 100644 --- a/frontend/rust-lib/flowy-user/Cargo.toml +++ b/frontend/rust-lib/flowy-user/Cargo.toml @@ -14,7 +14,7 @@ derive_more = {version = "0.99", features = ["display"]} flowy-database = { path = "../flowy-database" } dart-notify = { path = "../dart-notify" } lib-dispatch = { path = "../lib-dispatch" } -flowy-error = { path = "../flowy-error", features = ["db", "backend"] } +flowy-error = { path = "../flowy-error", features = ["db", "http_server"] } lib-sqlite = { path = "../lib-sqlite" } tracing = { version = "0.1", features = ["log"] } diff --git a/shared-lib/Cargo.lock b/shared-lib/Cargo.lock index 262980c653..326e71a4ac 100644 --- a/shared-lib/Cargo.lock +++ b/shared-lib/Cargo.lock @@ -2,206 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "actix-codec" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13895df506faee81e423febbae3a33b27fca71831b96bb3d60adf16ebcfea952" -dependencies = [ - "bitflags", - "bytes", - "futures-core", - "futures-sink", - "log", - "memchr", - "pin-project-lite", - "tokio", - "tokio-util", -] - -[[package]] -name = "actix-http" -version = "3.0.0-beta.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afaeb3d3fcb06b775ac62f05d580aae4afe5a149513333a73f688fdf26c06639" -dependencies = [ - "actix-codec", - "actix-rt", - "actix-service", - "actix-utils", - "ahash", - "base64", - "bitflags", - "brotli2", - "bytes", - "bytestring", - "derive_more", - "encoding_rs", - "flate2", - "futures-core", - "futures-util", - "h2", - "http", - "httparse", - "httpdate", - "itoa", - "language-tags", - "local-channel", - "log", - "mime", - "percent-encoding", - "pin-project", - "pin-project-lite", - "rand 0.8.4", - "sha-1", - "smallvec", - "zstd", -] - -[[package]] -name = "actix-macros" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" -dependencies = [ - "quote", - "syn", -] - -[[package]] -name = "actix-router" -version = "0.5.0-beta.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36b95ce0d76d1aa2f98b681702807475ade0f99bd4552546a6843a966d42ea3d" -dependencies = [ - "bytestring", - "firestorm", - "http", - "log", - "regex", - "serde", -] - -[[package]] -name = "actix-rt" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a0c218d0a17c120f10ee0c69c9f0c45d87319e8f66b1f065e8412b612fc3e24" -dependencies = [ - "actix-macros", - "futures-core", - "tokio", -] - -[[package]] -name = "actix-server" -version = "2.0.0-beta.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "411dd3296dd317ff5eff50baa13f31923ea40ec855dd7f2d3ed8639948f0195f" -dependencies = [ - "actix-rt", - "actix-service", - "actix-utils", - "futures-core", - "futures-util", - "log", - "mio", - "num_cpus", - "socket2", - "tokio", -] - -[[package]] -name = "actix-service" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d3dc6a618b082974a08d7a4781d24d4691cba51500059bfebe6656a61ebfe1e" -dependencies = [ - "futures-core", - "paste", - "pin-project-lite", -] - -[[package]] -name = "actix-utils" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e491cbaac2e7fc788dfff99ff48ef317e23b3cf63dbaf7aaab6418f40f92aa94" -dependencies = [ - "local-waker", - "pin-project-lite", -] - -[[package]] -name = "actix-web" -version = "4.0.0-beta.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e85aa9bb018d83a0db70f557ba0cde9c6170a5d1de4fede02e377f68c1ac5aa9" -dependencies = [ - "actix-codec", - "actix-http", - "actix-macros", - "actix-router", - "actix-rt", - "actix-server", - "actix-service", - "actix-utils", - "actix-web-codegen", - "ahash", - "bytes", - "cfg-if", - "cookie", - "derive_more", - "either", - "encoding_rs", - "futures-core", - "futures-util", - "itoa", - "language-tags", - "log", - "mime", - "once_cell", - "paste", - "pin-project", - "regex", - "serde", - "serde_json", - "serde_urlencoded", - "smallvec", - "socket2", - "time 0.3.5", - "url", -] - -[[package]] -name = "actix-web-codegen" -version = "0.5.0-beta.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfe80a8828fa88a0420dc8fdd4c16b8207326c917f17701881b063eadc2a8d3b" -dependencies = [ - "actix-router", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "ahash" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" -dependencies = [ - "getrandom 0.2.3", - "once_cell", - "version_check", -] - [[package]] name = "aho-corasick" version = "0.7.18" @@ -217,12 +17,6 @@ version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38d9ff5d688f1c13395289f67db01d4826b46dd694e7580accdc3e8430f2d98e" -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - [[package]] name = "async-stream" version = "0.3.2" @@ -261,39 +55,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" -[[package]] -name = "backend-service" -version = "0.1.0" -dependencies = [ - "actix-web", - "anyhow", - "bytes", - "config", - "derive_more", - "flowy-collaboration", - "flowy-folder-data-model", - "flowy-user-data-model", - "hyper", - "lazy_static", - "log", - "protobuf", - "reqwest", - "serde", - "serde-aux", - "serde_json", - "serde_repr", - "thiserror", - "tokio", - "tracing", - "uuid", -] - -[[package]] -name = "base-x" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" - [[package]] name = "base64" version = "0.13.0" @@ -330,32 +91,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "brotli-sys" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4445dea95f4c2b41cde57cc9fee236ae4dbae88d8fcbdb4750fc1bb5d86aaecd" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "brotli2" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cb036c3eade309815c15ddbacec5b22c4d1f3983a774ab2eac2e3e9ea85568e" -dependencies = [ - "brotli-sys", - "libc", -] - -[[package]] -name = "bumpalo" -version = "3.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" - [[package]] name = "bytecount" version = "0.6.2" @@ -373,27 +108,6 @@ name = "bytes" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" -dependencies = [ - "serde", -] - -[[package]] -name = "bytestring" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90706ba19e97b90786e19dc0d5e2abd80008d99d4c0c5d1ad0b5e72cec7c494d" -dependencies = [ - "bytes", -] - -[[package]] -name = "cc" -version = "1.0.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" -dependencies = [ - "jobserver", -] [[package]] name = "cfg-if" @@ -410,7 +124,7 @@ dependencies = [ "libc", "num-integer", "num-traits", - "time 0.1.44", + "time", "winapi", ] @@ -423,57 +137,12 @@ dependencies = [ "autocfg", ] -[[package]] -name = "config" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b076e143e1d9538dde65da30f8481c2a6c44040edb8e02b9bf1351edb92ce3" -dependencies = [ - "lazy_static", - "nom", - "serde", - "yaml-rust", -] - -[[package]] -name = "const_fn" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f92cfa0fd5690b3cf8c1ef2cabbd9b7ef22fa53cf5e1f92b05103f6d5d1cf6e7" - [[package]] name = "convert_case" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" -[[package]] -name = "cookie" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f1c7727e460397e56abc4bddc1d49e07a1ad78fc98eb2e1c8f032a58a2f80d" -dependencies = [ - "percent-encoding", - "time 0.2.27", - "version_check", -] - -[[package]] -name = "core-foundation" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - [[package]] name = "cpufeatures" version = "0.2.1" @@ -483,15 +152,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crc32fast" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" -dependencies = [ - "cfg-if", -] - [[package]] name = "dashmap" version = "4.0.2" @@ -511,7 +171,7 @@ dependencies = [ "convert_case", "proc-macro2", "quote", - "rustc_version 0.3.3", + "rustc_version", "syn", ] @@ -524,33 +184,12 @@ dependencies = [ "generic-array", ] -[[package]] -name = "discard" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" - [[package]] name = "dissimilar" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31ad93652f40969dead8d4bf897a41e9462095152eb21c56e5830537e41179dd" -[[package]] -name = "either" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" - -[[package]] -name = "encoding_rs" -version = "0.8.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a74ea89a0a1b98f6332de42c95baff457ada66d1cb4030f9ff151b2041a1c746" -dependencies = [ - "cfg-if", -] - [[package]] name = "env_logger" version = "0.7.1" @@ -602,24 +241,6 @@ dependencies = [ "regex", ] -[[package]] -name = "firestorm" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31586bda1b136406162e381a3185a506cdfc1631708dd40cba2f6628d8634499" - -[[package]] -name = "flate2" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" -dependencies = [ - "cfg-if", - "crc32fast", - "libc", - "miniz_oxide", -] - [[package]] name = "flowy-ast" version = "0.1.0" @@ -717,21 +338,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "form_urlencoded" version = "1.0.1" @@ -874,31 +480,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" -[[package]] -name = "h2" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fd819562fcebdac5afc5c113c3ec36f902840b70fd4fc458799c8ce4607ae55" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" - [[package]] name = "heck" version = "0.3.3" @@ -928,72 +509,18 @@ dependencies = [ "itoa", ] -[[package]] -name = "http-body" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - [[package]] name = "httparse" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" -[[package]] -name = "httpdate" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" - [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" -[[package]] -name = "hyper" -version = "0.14.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436ec0091e4f20e655156a30a0df3770fe2900aa301e548e08446ec794b6953c" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - [[package]] name = "idna" version = "0.2.3" @@ -1005,16 +532,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "indexmap" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" -dependencies = [ - "autocfg", - "hashbrown", -] - [[package]] name = "instant" version = "0.1.12" @@ -1024,61 +541,18 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "ipnet" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" - [[package]] name = "itoa" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" -[[package]] -name = "jobserver" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" -dependencies = [ - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "language-tags" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" - [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -[[package]] -name = "lexical-core" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" -dependencies = [ - "arrayvec", - "bitflags", - "cfg-if", - "ryu", - "static_assertions", -] - [[package]] name = "lib-infra" version = "0.1.0" @@ -1146,30 +620,6 @@ version = "0.2.107" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219" -[[package]] -name = "linked-hash-map" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" - -[[package]] -name = "local-channel" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6246c68cf195087205a0512559c97e15eaf95198bf0e206d662092cdcb03fe9f" -dependencies = [ - "futures-core", - "futures-sink", - "futures-util", - "local-waker", -] - -[[package]] -name = "local-waker" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84f9a2d3e27ce99ce2c3aad0b09b1a7b916293ea9b2bf624c13fe646fadd8da4" - [[package]] name = "lock_api" version = "0.4.5" @@ -1206,22 +656,6 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" -[[package]] -name = "mime" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" - -[[package]] -name = "miniz_oxide" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" -dependencies = [ - "adler", - "autocfg", -] - [[package]] name = "mio" version = "0.7.14" @@ -1244,35 +678,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "native-tls" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "nom" -version = "5.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" -dependencies = [ - "lexical-core", - "memchr", - "version_check", -] - [[package]] name = "ntapi" version = "0.3.6" @@ -1323,39 +728,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" -[[package]] -name = "openssl" -version = "0.10.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95" -dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-sys", -] - -[[package]] -name = "openssl-probe" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" - -[[package]] -name = "openssl-sys" -version = "0.9.71" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7df13d165e607909b363a4757a6f133f8a818a74e9d3a98d09c6128e15fa4c73" -dependencies = [ - "autocfg", - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "parking_lot" version = "0.11.2" @@ -1434,12 +806,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pkg-config" -version = "0.3.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f" - [[package]] name = "ppv-lite86" version = "0.2.15" @@ -1612,66 +978,13 @@ version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - -[[package]] -name = "reqwest" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d2927ca2f685faf0fc620ac4834690d29e7abb153add10f5812eef20b5e280" -dependencies = [ - "base64", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "http", - "http-body", - "hyper", - "hyper-tls", - "ipnet", - "js-sys", - "lazy_static", - "log", - "mime", - "native-tls", - "percent-encoding", - "pin-project-lite", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-native-tls", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg", -] - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver 0.9.0", -] - [[package]] name = "rustc_version" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" dependencies = [ - "semver 0.11.0", + "semver", ] [[package]] @@ -1680,69 +993,21 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" -[[package]] -name = "schannel" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" -dependencies = [ - "lazy_static", - "winapi", -] - [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -[[package]] -name = "security-framework" -version = "2.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525bc1abfda2e1998d152c45cf13e696f76d0a4972310b22fac1658b05df7c87" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser 0.7.0", -] - [[package]] name = "semver" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" dependencies = [ - "semver-parser 0.10.2", + "semver-parser", ] -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - [[package]] name = "semver-parser" version = "0.10.2" @@ -1761,17 +1026,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde-aux" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "905f2fc9f3d1574e8b5923a58118240021f01d4e239673937ffb9f42707a4f22" -dependencies = [ - "chrono", - "serde", - "serde_json", -] - [[package]] name = "serde_derive" version = "1.0.130" @@ -1794,29 +1048,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_repr" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98d0516900518c29efa217c298fa1f4e6c6ffc85ae29fd7f4ee48f176e1a9ed5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - [[package]] name = "serial_test" version = "0.5.1" @@ -1852,12 +1083,6 @@ dependencies = [ "opaque-debug", ] -[[package]] -name = "sha1" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" - [[package]] name = "signal-hook-registry" version = "1.4.0" @@ -1879,80 +1104,6 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" -[[package]] -name = "socket2" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "standback" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" -dependencies = [ - "version_check", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "stdweb" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" -dependencies = [ - "discard", - "rustc_version 0.2.3", - "stdweb-derive", - "stdweb-internal-macros", - "stdweb-internal-runtime", - "wasm-bindgen", -] - -[[package]] -name = "stdweb-derive" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "serde_derive", - "syn", -] - -[[package]] -name = "stdweb-internal-macros" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" -dependencies = [ - "base-x", - "proc-macro2", - "quote", - "serde", - "serde_derive", - "serde_json", - "sha1", - "syn", -] - -[[package]] -name = "stdweb-internal-runtime" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" - [[package]] name = "strum" version = "0.21.0" @@ -1982,20 +1133,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "tempfile" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" -dependencies = [ - "cfg-if", - "libc", - "rand 0.8.4", - "redox_syscall", - "remove_dir_all", - "winapi", -] - [[package]] name = "termcolor" version = "1.1.2" @@ -2036,54 +1173,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "time" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242" -dependencies = [ - "const_fn", - "libc", - "standback", - "stdweb", - "time-macros", - "version_check", - "winapi", -] - -[[package]] -name = "time" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41effe7cfa8af36f439fac33861b66b049edc6f9a32331e2312660529c1c24ad" -dependencies = [ - "itoa", - "libc", -] - -[[package]] -name = "time-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" -dependencies = [ - "proc-macro-hack", - "time-macros-impl", -] - -[[package]] -name = "time-macros-impl" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" -dependencies = [ - "proc-macro-hack", - "proc-macro2", - "quote", - "standback", - "syn", -] - [[package]] name = "tinyvec" version = "1.5.1" @@ -2130,16 +1219,6 @@ dependencies = [ "syn", ] -[[package]] -name = "tokio-native-tls" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" -dependencies = [ - "native-tls", - "tokio", -] - [[package]] name = "tokio-tungstenite" version = "0.15.0" @@ -2153,20 +1232,6 @@ dependencies = [ "tungstenite", ] -[[package]] -name = "tokio-util" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "log", - "pin-project-lite", - "tokio", -] - [[package]] name = "toml" version = "0.5.8" @@ -2176,12 +1241,6 @@ dependencies = [ "serde", ] -[[package]] -name = "tower-service" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" - [[package]] name = "tracing" version = "0.1.29" @@ -2215,12 +1274,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "try-lock" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" - [[package]] name = "trybuild" version = "1.0.52" @@ -2343,28 +1396,12 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad9680608df133af2c1ddd5eaf1ddce91d60d61b6bc51494ef326458365a470a" -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version_check" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" -[[package]] -name = "want" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" -dependencies = [ - "log", - "try-lock", -] - [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" @@ -2377,82 +1414,6 @@ version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" -[[package]] -name = "wasm-bindgen" -version = "0.2.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" -dependencies = [ - "bumpalo", - "lazy_static", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" - -[[package]] -name = "web-sys" -version = "0.3.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "winapi" version = "0.3.9" @@ -2483,50 +1444,3 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "winreg" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" -dependencies = [ - "winapi", -] - -[[package]] -name = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] - -[[package]] -name = "zstd" -version = "0.7.0+zstd.1.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9428752481d8372e15b1bf779ea518a179ad6c771cca2d2c60e4fbff3cc2cd52" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "3.1.0+zstd.1.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa1926623ad7fe406e090555387daf73db555b948134b4d73eac5eb08fb666d" -dependencies = [ - "libc", - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "1.5.0+zstd.1.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e6c094340240369025fc6b731b054ee2a834328fa584310ac96aa4baebdc465" -dependencies = [ - "cc", - "libc", -] diff --git a/shared-lib/Cargo.toml b/shared-lib/Cargo.toml index cf4dceaa68..ed805600e4 100644 --- a/shared-lib/Cargo.toml +++ b/shared-lib/Cargo.toml @@ -6,7 +6,6 @@ members = [ "lib-ot", "lib-ws", "lib-infra", - "backend-service", "flowy-derive", "flowy-ast", "error-code", diff --git a/shared-lib/backend-service/Cargo.toml b/shared-lib/backend-service/Cargo.toml deleted file mode 100644 index 5386524be5..0000000000 --- a/shared-lib/backend-service/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "backend-service" -version = "0.1.0" -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -flowy-folder-data-model = { path = "../flowy-folder-data-model" } -flowy-user-data-model = { path = "../flowy-user-data-model" } -flowy-collaboration = { path = "../flowy-collaboration" } - -log = "0.4.14" -lazy_static = "1.4.0" -tokio = { version = "1", features = ["rt"] } -anyhow = "1.0" -thiserror = "1.0.24" -bytes = { version = "1.0", features = ["serde"]} -reqwest = "0.11" -hyper = "0.14" -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -serde_repr = "0.1" -uuid = { version = "0.8", features = ["v4"] } -protobuf = {version = "2.18.0"} -derive_more = {version = "0.99", features = ["display"]} -tracing = { version = "0.1", features = ["log"] } -actix-web = {version = "4.0.0-beta.8", optional = true} -config = { version = "0.10.1", default-features = false, features = ["yaml"] } -serde-aux = "1.0.1" - -[features] -http_server = ["actix-web"] \ No newline at end of file diff --git a/shared-lib/backend-service/src/errors.rs b/shared-lib/backend-service/src/errors.rs deleted file mode 100644 index 8035798817..0000000000 --- a/shared-lib/backend-service/src/errors.rs +++ /dev/null @@ -1,138 +0,0 @@ -use crate::response::FlowyResponse; -use bytes::Bytes; -use serde::{Deserialize, Serialize, __private::Formatter}; -use serde_repr::*; -use std::{fmt, fmt::Debug}; - -pub type Result = std::result::Result; -use flowy_collaboration::errors::CollaborateError; -#[derive(thiserror::Error, Debug, Serialize, Deserialize, Clone)] -pub struct ServerError { - pub code: ErrorCode, - pub msg: String, -} - -macro_rules! static_error { - ($name:ident, $status:expr) => { - #[allow(non_snake_case, missing_docs)] - pub fn $name() -> ServerError { - ServerError { - code: $status, - msg: format!("{}", $status), - } - } - }; -} - -impl ServerError { - static_error!(internal, ErrorCode::InternalError); - static_error!(http, ErrorCode::HttpError); - static_error!(payload_none, ErrorCode::PayloadUnexpectedNone); - static_error!(unauthorized, ErrorCode::UserUnauthorized); - static_error!(password_not_match, ErrorCode::PasswordNotMatch); - static_error!(params_invalid, ErrorCode::ParamsInvalid); - static_error!(connect_timeout, ErrorCode::ConnectTimeout); - static_error!(connect_close, ErrorCode::ConnectClose); - static_error!(connect_cancel, ErrorCode::ConnectCancel); - static_error!(connect_refused, ErrorCode::ConnectRefused); - static_error!(record_not_found, ErrorCode::RecordNotFound); - - pub fn new(msg: String, code: ErrorCode) -> Self { - Self { code, msg } - } - - pub fn context(mut self, error: T) -> Self { - self.msg = format!("{:?}", error); - self - } - - pub fn is_record_not_found(&self) -> bool { - self.code == ErrorCode::RecordNotFound - } - - pub fn is_unauthorized(&self) -> bool { - self.code == ErrorCode::UserUnauthorized - } - - pub fn to_collaborate_error(&self) -> CollaborateError { - if self.is_record_not_found() { - CollaborateError::record_not_found() - } else { - CollaborateError::internal().context(self.msg.clone()) - } - } -} - -pub fn internal_error(e: T) -> ServerError -where - T: std::fmt::Debug, -{ - ServerError::internal().context(e) -} - -pub fn invalid_params(e: T) -> ServerError { - ServerError::params_invalid().context(e) -} - -impl std::fmt::Display for ServerError { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let msg = format!("{:?}:{}", self.code, self.msg); - f.write_str(&msg) - } -} - -impl std::convert::From<&ServerError> for FlowyResponse { - fn from(error: &ServerError) -> Self { - FlowyResponse { - data: Bytes::from(vec![]), - error: Some(error.clone()), - } - } -} - -#[derive(Serialize_repr, Deserialize_repr, PartialEq, Debug, Clone, derive_more::Display)] -#[repr(u16)] -pub enum ErrorCode { - #[display(fmt = "Unauthorized")] - UserUnauthorized = 1, - #[display(fmt = "Payload too large")] - PayloadOverflow = 2, - #[display(fmt = "Payload deserialize failed")] - PayloadSerdeFail = 3, - #[display(fmt = "Unexpected empty payload")] - PayloadUnexpectedNone = 4, - #[display(fmt = "Params is invalid")] - ParamsInvalid = 5, - - #[display(fmt = "Protobuf serde error")] - ProtobufError = 10, - #[display(fmt = "Json serde Error")] - SerdeError = 11, - - #[display(fmt = "Email address already exists")] - EmailAlreadyExists = 50, - - #[display(fmt = "Username and password do not match")] - PasswordNotMatch = 51, - - #[display(fmt = "Connect refused")] - ConnectRefused = 100, - - #[display(fmt = "Connection timeout")] - ConnectTimeout = 101, - #[display(fmt = "Connection closed")] - ConnectClose = 102, - #[display(fmt = "Connection canceled")] - ConnectCancel = 103, - - #[display(fmt = "Sql error")] - SqlError = 200, - #[display(fmt = "Record not found")] - RecordNotFound = 201, - - #[display(fmt = "Http request error")] - HttpError = 300, - - #[display(fmt = "Internal error")] - InternalError = 1000, -} diff --git a/shared-lib/backend-service/src/lib.rs b/shared-lib/backend-service/src/lib.rs deleted file mode 100644 index 18e02d6b95..0000000000 --- a/shared-lib/backend-service/src/lib.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod configuration; -pub mod errors; -pub mod request; -pub mod response; diff --git a/shared-lib/backend-service/src/request/mod.rs b/shared-lib/backend-service/src/request/mod.rs deleted file mode 100644 index fd1bf9d798..0000000000 --- a/shared-lib/backend-service/src/request/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -#![allow(clippy::module_inception)] -mod request; - -pub use request::*; diff --git a/shared-lib/backend-service/src/response/mod.rs b/shared-lib/backend-service/src/response/mod.rs deleted file mode 100644 index 886f694baf..0000000000 --- a/shared-lib/backend-service/src/response/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![allow(clippy::module_inception)] -mod response; - -#[cfg(feature = "http_server")] -mod response_http; - -pub use response::*; diff --git a/shared-lib/backend-service/src/response/response.rs b/shared-lib/backend-service/src/response/response.rs deleted file mode 100644 index 9815b3bcb8..0000000000 --- a/shared-lib/backend-service/src/response/response.rs +++ /dev/null @@ -1,100 +0,0 @@ -use crate::errors::{ErrorCode, ServerError}; -use bytes::Bytes; -use serde::{Deserialize, Serialize}; -use std::{convert::TryInto, error::Error, fmt::Debug}; -use tokio::sync::oneshot::error::RecvError; - -#[derive(Debug, Serialize, Deserialize)] -pub struct FlowyResponse { - pub data: Bytes, - #[serde(skip_serializing_if = "Option::is_none")] - pub error: Option, -} - -impl FlowyResponse { - pub fn new(data: Bytes, error: Option) -> Self { - FlowyResponse { data, error } - } - - pub fn success() -> Self { - Self::new(Bytes::new(), None) - } - - pub fn data>(mut self, data: T) -> Result { - let bytes: Bytes = data.try_into()?; - self.data = bytes; - Ok(self) - } - - pub fn pb(mut self, data: T) -> Result { - let bytes: Bytes = Bytes::from(data.write_to_bytes()?); - self.data = bytes; - Ok(self) - } -} - -impl std::convert::From for ServerError { - fn from(e: protobuf::ProtobufError) -> Self { - ServerError::internal().context(e) - } -} - -impl std::convert::From for ServerError { - fn from(error: RecvError) -> Self { - ServerError::internal().context(error) - } -} - -impl std::convert::From for ServerError { - fn from(e: serde_json::Error) -> Self { - ServerError::internal().context(e) - } -} - -impl std::convert::From for ServerError { - fn from(error: anyhow::Error) -> Self { - ServerError::internal().context(error) - } -} - -impl std::convert::From for ServerError { - fn from(error: reqwest::Error) -> Self { - if error.is_timeout() { - return ServerError::connect_timeout().context(error); - } - - if error.is_request() { - let hyper_error: Option<&hyper::Error> = error.source().unwrap().downcast_ref(); - return match hyper_error { - None => ServerError::connect_refused().context(error), - Some(hyper_error) => { - let mut code = ErrorCode::InternalError; - let msg = format!("{}", error); - if hyper_error.is_closed() { - code = ErrorCode::ConnectClose; - } - - if hyper_error.is_connect() { - code = ErrorCode::ConnectRefused; - } - - if hyper_error.is_canceled() { - code = ErrorCode::ConnectCancel; - } - - if hyper_error.is_timeout() {} - - ServerError { code, msg } - } - }; - } - - ServerError::internal().context(error) - } -} - -impl std::convert::From for ServerError { - fn from(e: uuid::Error) -> Self { - ServerError::internal().context(e) - } -} diff --git a/shared-lib/backend-service/src/response/response_http.rs b/shared-lib/backend-service/src/response/response_http.rs deleted file mode 100644 index f81bdd75a6..0000000000 --- a/shared-lib/backend-service/src/response/response_http.rs +++ /dev/null @@ -1,26 +0,0 @@ -use crate::response::*; -use actix_web::{error::ResponseError, HttpResponse}; - -use crate::errors::ServerError; -use actix_web::body::AnyBody; - -impl ResponseError for ServerError { - fn error_response(&self) -> HttpResponse { - let response: FlowyResponse = self.into(); - response.into() - } -} -impl std::convert::Into for FlowyResponse { - fn into(self) -> HttpResponse { - HttpResponse::Ok().json(self) - } -} - -impl std::convert::Into for FlowyResponse { - fn into(self) -> AnyBody { - match serde_json::to_string(&self) { - Ok(body) => AnyBody::from(body), - Err(_) => AnyBody::Empty, - } - } -} From 9f8e3b09c31d1b0a6770827b332eef3cf170a632 Mon Sep 17 00:00:00 2001 From: appflowy Date: Mon, 7 Feb 2022 15:47:16 +0800 Subject: [PATCH 8/9] fix: compile errors --- frontend/rust-lib/Cargo.lock | 81 +++++++++++++++++++ frontend/rust-lib/flowy-net/Cargo.toml | 7 ++ .../rust-lib/flowy-net/src/configuration.rs | 2 +- frontend/rust-lib/flowy-net/src/lib.rs | 1 - frontend/rust-lib/flowy-net/src/middleware.rs | 41 ---------- frontend/rust-lib/flowy-sdk/src/lib.rs | 2 +- 6 files changed, 90 insertions(+), 44 deletions(-) delete mode 100644 frontend/rust-lib/flowy-net/src/middleware.rs diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock index 4f2993f6dc..5f6b74990d 100755 --- a/frontend/rust-lib/Cargo.lock +++ b/frontend/rust-lib/Cargo.lock @@ -51,6 +51,12 @@ version = "1.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3" +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + [[package]] name = "async-stream" version = "0.3.2" @@ -284,6 +290,18 @@ dependencies = [ "owo-colors", ] +[[package]] +name = "config" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b076e143e1d9538dde65da30f8481c2a6c44040edb8e02b9bf1351edb92ce3" +dependencies = [ + "lazy_static", + "nom", + "serde", + "yaml-rust", +] + [[package]] name = "convert_case" version = "0.4.0" @@ -889,6 +907,7 @@ dependencies = [ "anyhow", "async-stream", "bytes", + "config", "dashmap", "flowy-collaboration", "flowy-derive", @@ -900,12 +919,18 @@ dependencies = [ "flowy-user-data-model", "futures-util", "http-response", + "hyper", "lazy_static", "lib-dispatch", "lib-infra", "lib-ws", + "log", "parking_lot", "protobuf", + "reqwest", + "serde", + "serde-aux", + "serde_json", "strum", "strum_macros", "tokio", @@ -1448,6 +1473,19 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lexical-core" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" +dependencies = [ + "arrayvec", + "bitflags", + "cfg-if", + "ryu", + "static_assertions", +] + [[package]] name = "lib-dispatch" version = "0.1.0" @@ -1586,6 +1624,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" + [[package]] name = "lock_api" version = "0.4.5" @@ -1717,6 +1761,17 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nom" +version = "5.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" +dependencies = [ + "lexical-core", + "memchr", + "version_check", +] + [[package]] name = "ntapi" version = "0.3.6" @@ -2279,6 +2334,17 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-aux" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "905f2fc9f3d1574e8b5923a58118240021f01d4e239673937ffb9f42707a4f22" +dependencies = [ + "chrono", + "serde", + "serde_json", +] + [[package]] name = "serde_cbor" version = "0.11.2" @@ -2432,6 +2498,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.10.0" @@ -3036,3 +3108,12 @@ checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" dependencies = [ "winapi", ] + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] diff --git a/frontend/rust-lib/flowy-net/Cargo.toml b/frontend/rust-lib/flowy-net/Cargo.toml index 04bb1886d9..a71db47beb 100644 --- a/frontend/rust-lib/flowy-net/Cargo.toml +++ b/frontend/rust-lib/flowy-net/Cargo.toml @@ -30,6 +30,13 @@ dashmap = {version = "4.0"} async-stream = "0.3.2" futures-util = "0.3.15" http-response = { git = "https://github.com/AppFlowy-IO/AppFlowy-Server", features = ["with_reqwest"]} +serde-aux = "1.0.1" +reqwest = "0.11" +hyper = "0.14" +config = { version = "0.10.1", default-features = false, features = ["yaml"] } +log = "0.4.14" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" [features] http_server = [] \ No newline at end of file diff --git a/frontend/rust-lib/flowy-net/src/configuration.rs b/frontend/rust-lib/flowy-net/src/configuration.rs index 6912188826..883151dc48 100644 --- a/frontend/rust-lib/flowy-net/src/configuration.rs +++ b/frontend/rust-lib/flowy-net/src/configuration.rs @@ -1,7 +1,6 @@ use config::FileFormat; use serde_aux::field_attributes::deserialize_number_from_string; use std::convert::{TryFrom, TryInto}; -pub const HOST: &str = "localhost:8000"; pub const HEADER_TOKEN: &str = "token"; #[derive(serde::Deserialize, Clone, Debug)] @@ -89,6 +88,7 @@ pub enum Environment { } impl Environment { + #[allow(dead_code)] pub fn as_str(&self) -> &'static str { match self { Environment::Local => "local", diff --git a/frontend/rust-lib/flowy-net/src/lib.rs b/frontend/rust-lib/flowy-net/src/lib.rs index 111f9e36e8..78f8a28f02 100644 --- a/frontend/rust-lib/flowy-net/src/lib.rs +++ b/frontend/rust-lib/flowy-net/src/lib.rs @@ -4,7 +4,6 @@ mod event; mod handlers; pub mod http_server; pub mod local_server; -mod middleware; pub mod module; pub mod protobuf; mod request; diff --git a/frontend/rust-lib/flowy-net/src/middleware.rs b/frontend/rust-lib/flowy-net/src/middleware.rs deleted file mode 100644 index c1e8b695c0..0000000000 --- a/frontend/rust-lib/flowy-net/src/middleware.rs +++ /dev/null @@ -1,41 +0,0 @@ -use crate::{request::ResponseMiddleware, response::FlowyResponse}; -use lazy_static::lazy_static; -use std::sync::Arc; -use tokio::sync::broadcast; -lazy_static! { - pub static ref BACKEND_API_MIDDLEWARE: Arc = Arc::new(WorkspaceMiddleware::new()); -} - -pub struct WorkspaceMiddleware { - invalid_token_sender: broadcast::Sender, -} - -impl WorkspaceMiddleware { - fn new() -> Self { - let (sender, _) = broadcast::channel(10); - WorkspaceMiddleware { - invalid_token_sender: sender, - } - } - - pub fn invalid_token_subscribe(&self) -> broadcast::Receiver { - self.invalid_token_sender.subscribe() - } -} - -impl ResponseMiddleware for WorkspaceMiddleware { - fn receive_response(&self, token: &Option, response: &FlowyResponse) { - if let Some(error) = &response.error { - if error.is_unauthorized() { - log::error!("user is unauthorized"); - match token { - None => {} - Some(token) => match self.invalid_token_sender.send(token.clone()) { - Ok(_) => {} - Err(e) => log::error!("{:?}", e), - }, - } - } - } - } -} diff --git a/frontend/rust-lib/flowy-sdk/src/lib.rs b/frontend/rust-lib/flowy-sdk/src/lib.rs index 7e3c0da37b..d19d19adf6 100644 --- a/frontend/rust-lib/flowy-sdk/src/lib.rs +++ b/frontend/rust-lib/flowy-sdk/src/lib.rs @@ -76,7 +76,7 @@ fn crate_log_filter(level: String) -> String { filters.push(format!("lib_ot={}", level)); filters.push(format!("lib_ws={}", level)); filters.push(format!("lib_infra={}", level)); - filters.push(format!("flowy_sync={}", level)); + filters.push(format!("flowy_sync={}", "info")); filters.join(",") } From 337161c3d723690bd3ff9116766a72bbcc0d4d4b Mon Sep 17 00:00:00 2001 From: appflowy Date: Mon, 7 Feb 2022 15:51:19 +0800 Subject: [PATCH 9/9] style: config log --- frontend/rust-lib/flowy-sdk/src/lib.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/frontend/rust-lib/flowy-sdk/src/lib.rs b/frontend/rust-lib/flowy-sdk/src/lib.rs index d19d19adf6..f3582ad92b 100644 --- a/frontend/rust-lib/flowy-sdk/src/lib.rs +++ b/frontend/rust-lib/flowy-sdk/src/lib.rs @@ -69,13 +69,14 @@ fn crate_log_filter(level: String) -> String { filters.push(format!("flowy_user={}", level)); filters.push(format!("flowy_document={}", level)); filters.push(format!("flowy_collaboration={}", level)); - filters.push(format!("flowy_net={}", level)); - filters.push(format!("dart_ffi={}", "info")); - filters.push(format!("dart_database={}", "info")); filters.push(format!("dart_notify={}", level)); filters.push(format!("lib_ot={}", level)); filters.push(format!("lib_ws={}", level)); filters.push(format!("lib_infra={}", level)); + + filters.push(format!("dart_ffi={}", "info")); + filters.push(format!("flowy_database={}", "info")); + filters.push(format!("flowy_net={}", "info")); filters.push(format!("flowy_sync={}", "info")); filters.join(",") }