Browse Source

feat: remove backend

appflowy 3 years ago
parent
commit
67dff1720b
100 changed files with 0 additions and 10690 deletions
  1. 0 1
      .dockerignore
  2. 0 156
      .github/workflows/backend_general.yml
  3. 0 3
      .gitignore
  4. 0 9
      backend/.dockerignore
  5. 0 1
      backend/.env
  6. 0 4131
      backend/Cargo.lock
  7. 0 109
      backend/Cargo.toml
  8. 0 23
      backend/Dockerfile
  9. 0 23
      backend/Makefile
  10. 0 9
      backend/configuration/base.yaml
  11. 0 5
      backend/configuration/local.yaml
  12. 0 6
      backend/configuration/production.yaml
  13. 0 70
      backend/doc/database_setup.md
  14. 0 200
      backend/doc/database_struct.md
  15. BIN
      backend/doc/img_1.png
  16. BIN
      backend/doc/img_2.png
  17. 0 23
      backend/docker-compose.yml
  18. 0 9
      backend/migrations/20210819065837_user.sql
  19. 0 10
      backend/migrations/20210824033032_workspace.sql
  20. 0 14
      backend/migrations/20210824033742_app.sql
  21. 0 12
      backend/migrations/20210824033748_view.sql
  22. 0 6
      backend/migrations/20210909115140_doc.sql
  23. 0 7
      backend/migrations/20211015065001_trash.sql
  24. 0 6
      backend/migrations/20211221061753_kv.sql
  25. 0 2
      backend/rust-toolchain.toml
  26. 0 18
      backend/rustfmt.toml
  27. 0 10
      backend/scripts/docker_env.sh
  28. 0 2
      backend/scripts/docker_test.sh
  29. 0 61
      backend/scripts/init_database.sh
  30. 0 19
      backend/sqlx-data.json
  31. 0 166
      backend/src/application.rs
  32. 0 106
      backend/src/config/configuration.rs
  33. 0 7
      backend/src/config/const_define.rs
  34. 0 17
      backend/src/config/env.rs
  35. 0 6
      backend/src/config/mod.rs
  36. 0 92
      backend/src/context.rs
  37. 0 118
      backend/src/entities/logged_user.rs
  38. 0 3
      backend/src/entities/mod.rs
  39. 0 94
      backend/src/entities/token.rs
  40. 0 13
      backend/src/entities/user.rs
  41. 0 7
      backend/src/lib.rs
  42. 0 14
      backend/src/main.rs
  43. 0 105
      backend/src/middleware/auth_middleware.rs
  44. 0 16
      backend/src/middleware/cors_middleware.rs
  45. 0 5
      backend/src/middleware/mod.rs
  46. 0 6
      backend/src/services/document/mod.rs
  47. 0 63
      backend/src/services/document/persistence.rs
  48. 0 48
      backend/src/services/document/router.rs
  49. 0 159
      backend/src/services/document/ws_actor.rs
  50. 0 176
      backend/src/services/document/ws_receiver.rs
  51. 0 113
      backend/src/services/folder/app/controller.rs
  52. 0 5
      backend/src/services/folder/app/mod.rs
  53. 0 140
      backend/src/services/folder/app/persistence.rs
  54. 0 114
      backend/src/services/folder/app/router.rs
  55. 0 6
      backend/src/services/folder/mod.rs
  56. 0 6
      backend/src/services/folder/trash/mod.rs
  57. 0 40
      backend/src/services/folder/trash/persistence.rs
  58. 0 106
      backend/src/services/folder/trash/router.rs
  59. 0 180
      backend/src/services/folder/trash/trash.rs
  60. 0 170
      backend/src/services/folder/view/controller.rs
  61. 0 6
      backend/src/services/folder/view/mod.rs
  62. 0 134
      backend/src/services/folder/view/persistence.rs
  63. 0 128
      backend/src/services/folder/view/router.rs
  64. 0 144
      backend/src/services/folder/workspace/controller.rs
  65. 0 6
      backend/src/services/folder/workspace/mod.rs
  66. 0 98
      backend/src/services/folder/workspace/persistence.rs
  67. 0 149
      backend/src/services/folder/workspace/router.rs
  68. 0 148
      backend/src/services/folder/ws_actor.rs
  69. 0 169
      backend/src/services/folder/ws_receiver.rs
  70. 0 210
      backend/src/services/kv/kv.rs
  71. 0 41
      backend/src/services/kv/mod.rs
  72. 0 128
      backend/src/services/kv/revision_kv.rs
  73. 0 50
      backend/src/services/log.rs
  74. 0 6
      backend/src/services/mod.rs
  75. 0 236
      backend/src/services/user/controller.rs
  76. 0 4
      backend/src/services/user/mod.rs
  77. 0 64
      backend/src/services/user/router.rs
  78. 0 50
      backend/src/services/web_socket/entities/connect.rs
  79. 0 27
      backend/src/services/web_socket/entities/message.rs
  80. 0 5
      backend/src/services/web_socket/entities/mod.rs
  81. 0 8
      backend/src/services/web_socket/mod.rs
  82. 0 61
      backend/src/services/web_socket/router.rs
  83. 0 181
      backend/src/services/web_socket/ws_client.rs
  84. 0 65
      backend/src/services/web_socket/ws_server.rs
  85. 0 3
      backend/src/util/mod.rs
  86. 0 46
      backend/src/util/serde_ext.rs
  87. 0 6
      backend/src/util/sqlx_ext/mod.rs
  88. 0 159
      backend/src/util/sqlx_ext/query.rs
  89. 0 11
      backend/src/util/sqlx_ext/utils.rs
  90. 0 31
      backend/src/util/user_ext.rs
  91. 0 126
      backend/tests/api_test/auth_test.rs
  92. 0 79
      backend/tests/api_test/kv_test.rs
  93. 0 3
      backend/tests/api_test/mod.rs
  94. 0 282
      backend/tests/api_test/workspace_test.rs
  95. 0 179
      backend/tests/document_test/edit_script.rs
  96. 0 207
      backend/tests/document_test/edit_test.rs
  97. 0 2
      backend/tests/document_test/mod.rs
  98. 0 3
      backend/tests/main.rs
  99. 0 388
      backend/tests/util/helper.rs
  100. 0 1
      backend/tests/util/mod.rs

+ 0 - 1
.dockerignore

@@ -1,5 +1,4 @@
 frontend/app_flowy/
 frontend/scripts/
 frontend/rust-lib/target
-backend/target/
 shared-lib/target/

+ 0 - 156
.github/workflows/backend_general.yml

@@ -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/
-
-

+ 0 - 3
.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

+ 0 - 9
backend/.dockerignore

@@ -1,9 +0,0 @@
-.env
-.dockerignore
-spec.yaml
-target/
-deploy/
-tests/
-Dockerfile
-scripts/
-migrations/

+ 0 - 1
backend/.env

@@ -1 +0,0 @@
-DATABASE_URL="postgres://postgres:password@localhost:5433/flowy"

+ 0 - 4131
backend/Cargo.lock

@@ -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",
-]

+ 0 - 109
backend/Cargo.toml

@@ -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"] }
-

+ 0 - 23
backend/Dockerfile

@@ -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"]

+ 0 - 23
backend/Makefile

@@ -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
-
-

+ 0 - 9
backend/configuration/base.yaml

@@ -1,9 +0,0 @@
-application:
-  port: 8000
-  host: 0.0.0.0
-database:
-  host: "localhost"
-  port: 5433
-  username: "postgres"
-  password: "password"
-  database_name: "flowy"

+ 0 - 5
backend/configuration/local.yaml

@@ -1,5 +0,0 @@
-application:
-  host: 127.0.0.1
-  base_url: "http://127.0.0.1"
-database:
-  require_ssl: false

+ 0 - 6
backend/configuration/production.yaml

@@ -1,6 +0,0 @@
-application:
-  host: 0.0.0.0
-database:
-  host: "db"
-  port: 5432
-  require_ssl: false

+ 0 - 70
backend/doc/database_setup.md

@@ -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/) 

+ 0 - 200
backend/doc/database_struct.md

@@ -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         |           |
-

BIN
backend/doc/img_1.png


BIN
backend/doc/img_2.png


+ 0 - 23
backend/docker-compose.yml

@@ -1,23 +0,0 @@
-version: '3'
-services:
-  db:
-    image: 'postgres:9.6-alpine'
-    environment:
-      - POSTGRES_USER=${POSTGRES_USER}
-      - POSTGRES_DB=${POSTGRES_DB}
-      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
-    ports:
-      - "5434:5432"
-  backend:
-    restart: on-failure
-    environment:
-      - APP_ENVIRONMENT=production
-      - DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db/${POSTGRES_DB}"
-    build:
-      context: ../
-      dockerfile: ./backend/Dockerfile
-    image: flowy_backend:${BACKEND_VERSION}
-    depends_on:
-      - db
-    ports:
-      - 8000:8000

+ 0 - 9
backend/migrations/20210819065837_user.sql

@@ -1,9 +0,0 @@
--- Add migration script here
-CREATE TABLE IF NOT EXISTS user_table(
-    id uuid NOT NULL,
-    PRIMARY KEY (id),
-    email TEXT NOT NULL UNIQUE,
-    name TEXT NOT NULL,
-    create_time timestamptz NOT NULL,
-    password TEXT NOT NULL
-);

+ 0 - 10
backend/migrations/20210824033032_workspace.sql

@@ -1,10 +0,0 @@
--- Add migration script here
-CREATE TABLE IF NOT EXISTS workspace_table(
-   id uuid NOT NULL,
-   PRIMARY KEY (id),
-   name TEXT NOT NULL,
-   description TEXT NOT NULL,
-   modified_time timestamptz NOT NULL,
-   create_time timestamptz NOT NULL,
-   user_id TEXT NOT NULL
-);

+ 0 - 14
backend/migrations/20210824033742_app.sql

@@ -1,14 +0,0 @@
--- Add migration script here
-CREATE TABLE IF NOT EXISTS app_table(
-    id uuid NOT NULL,
-    PRIMARY KEY (id),
-    workspace_id TEXT NOT NULL,
-    name TEXT NOT NULL,
-    description TEXT NOT NULL,
-    color_style BYTEA NOT NULL,
-    last_view_id TEXT DEFAULT '',
-    modified_time timestamptz NOT NULL,
-    create_time timestamptz NOT NULL,
-    user_id TEXT NOT NULL,
-    is_trash BOOL NOT NULL DEFAULT false
-);

+ 0 - 12
backend/migrations/20210824033748_view.sql

@@ -1,12 +0,0 @@
--- Add migration script here
-CREATE TABLE IF NOT EXISTS view_table(
-      id uuid NOT NULL,
-      PRIMARY KEY (id),
-      belong_to_id TEXT NOT NULL,
-      name TEXT NOT NULL,
-      description TEXT NOT NULL,
-      modified_time timestamptz NOT NULL,
-      create_time timestamptz NOT NULL,
-      thumbnail TEXT NOT NULL,
-      view_type INTEGER NOT NULL
-);

+ 0 - 6
backend/migrations/20210909115140_doc.sql

@@ -1,6 +0,0 @@
--- Add migration script here
-CREATE TABLE IF NOT EXISTS doc_table(
-    id uuid NOT NULL,
-    PRIMARY KEY (id),
-    rev_id bigint NOT NULL DEFAULT 0
-);

+ 0 - 7
backend/migrations/20211015065001_trash.sql

@@ -1,7 +0,0 @@
--- Add migration script here
-CREATE TABLE IF NOT EXISTS trash_table(
-    id uuid NOT NULL,
-    PRIMARY KEY (id),
-    user_id TEXT NOT NULL,
-    ty INTEGER NOT NULL DEFAULT 0
-);

+ 0 - 6
backend/migrations/20211221061753_kv.sql

@@ -1,6 +0,0 @@
--- Add migration script here
-CREATE TABLE IF NOT EXISTS kv_table(
-    id TEXT NOT NULL,
-    PRIMARY KEY (id),
-    blob bytea
-);

+ 0 - 2
backend/rust-toolchain.toml

@@ -1,2 +0,0 @@
-[toolchain]
-channel = "stable-2022-01-20"

+ 0 - 18
backend/rustfmt.toml

@@ -1,18 +0,0 @@
-# https://rust-lang.github.io/rustfmt/?version=master&search=
-max_width = 120
-tab_spaces = 4
-# fn_single_line = true
-# match_block_trailing_comma = true
-# normalize_comments = true
-# wrap_comments = true
-# use_field_init_shorthand = true
-# use_try_shorthand = true
-# normalize_doc_attributes = true
-# report_todo = "Never"
-# report_fixme = "Always"
-# imports_layout = "HorizontalVertical"
-# imports_granularity = "Crate"
-# reorder_modules = true
-# reorder_imports = true
-# enum_discrim_align_threshold = 20
-edition = "2018"

+ 0 - 10
backend/scripts/docker_env.sh

@@ -1,10 +0,0 @@
-#!/bin/bash
-export BACKEND_VERSION="v0.0.1"
-
-export POSTGRES_USER=postgres
-export POSTGRES_PASSWORD=password
-export POSTGRES_PORT=5432
-export POSTGRES_HOST=db
-export POSTGRES_DB=flowy
-
-export DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}

+ 0 - 2
backend/scripts/docker_test.sh

@@ -1,2 +0,0 @@
-#!/bin/bash
-curl -i --request Get --url http://0.0.0.0:8000/api/user --header 'content-type: application/json' --data '{"token":"123"}'

+ 0 - 61
backend/scripts/init_database.sh

@@ -1,61 +0,0 @@
-#!/usr/bin/env bash
-set -x
-set -eo pipefail
-
-if ! [ -x "$(command -v psql)" ]; then
-  echo >&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!"
-

+ 0 - 19
backend/sqlx-data.json

@@ -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": []
-    }
-  }
-}

+ 0 - 166
backend/src/application.rs

@@ -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<Self, std::io::Error> {
-        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<Server, std::io::Error> {
-    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<CookieIdentityPolicy> {
-    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<PgPool, sqlx::Error> {
-    PgPoolOptions::new()
-        .connect_timeout(std::time::Duration::from_secs(5))
-        .connect_with(configuration.with_db())
-        .await
-}

+ 0 - 106
backend/src/config/configuration.rs

@@ -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<Settings, config::ConfigError> {
-    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<String> for Environment {
-    type Error = String;
-
-    fn try_from(s: String) -> Result<Self, Self::Error> {
-        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
-            )),
-        }
-    }
-}

+ 0 - 7
backend/src/config/const_define.rs

@@ -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"];

+ 0 - 17
backend/src/config/env.rs

@@ -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
-}

+ 0 - 6
backend/src/config/mod.rs

@@ -1,6 +0,0 @@
-mod configuration;
-mod const_define;
-pub mod env;
-
-pub use configuration::*;
-pub use const_define::*;

+ 0 - 92
backend/src/context.rs

@@ -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<Addr<WSServer>>,
-    pub persistence: Data<Arc<FlowyPersistence>>,
-    pub ws_receivers: Data<WebSocketReceivers>,
-    pub document_manager: Data<Arc<ServerDocumentManager>>,
-    pub folder_manager: Data<Arc<ServerFolderManager>>,
-}
-
-impl AppContext {
-    pub fn new(ws_server: Addr<WSServer>, 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<DocumentRevisionKV> {
-    let kv_impl = Arc::new(PostgresKV { pg_pool });
-    Arc::new(DocumentRevisionKV::new(kv_impl))
-}
-
-fn make_folder_kv_store(pg_pool: PgPool) -> Arc<FolderRevisionKV> {
-    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<DocumentRevisionKV>,
-    folder_store: Arc<FolderRevisionKV>,
-}
-
-impl FlowyPersistence {
-    pub fn pg_pool(&self) -> PgPool {
-        self.pg_pool.clone()
-    }
-
-    pub fn document_kv_store(&self) -> Arc<DocumentRevisionKV> {
-        self.document_store.clone()
-    }
-
-    pub fn folder_kv_store(&self) -> Arc<FolderRevisionKV> {
-        self.folder_store.clone()
-    }
-}

+ 0 - 118
backend/src/entities/logged_user.rs

@@ -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<Claim> 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<Self, ServerError> {
-        let user: LoggedUser = Token::decode_token(&token.into())?.into();
-        Ok(user)
-    }
-
-    pub fn as_uuid(&self) -> Result<uuid::Uuid, ServerError> {
-        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<Result<Self, Self::Error>>;
-
-    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<Self, Self::Error> {
-        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<Utc>),
-    NotAuthorized,
-}
-
-pub const EXPIRED_DURATION_DAYS: i64 = 30;
-
-pub struct AuthorizedUsers(DashMap<LoggedUser, AuthStatus>);
-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);
-    }
-}

+ 0 - 3
backend/src/entities/mod.rs

@@ -1,3 +0,0 @@
-pub mod logged_user;
-pub mod token;
-pub mod user;

+ 0 - 94
backend/src/entities/token.rs

@@ -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<Claim> 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<Self, ServerError> {
-        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<Claim, ServerError> {
-        decode::<Claim>(
-            &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<Self, ServerError> {
-        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<Result<Self, Self::Error>>;
-
-    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)),
-        }
-    }
-}

+ 0 - 13
backend/src/entities/user.rs

@@ -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<Utc>,
-    pub(crate) password: String,
-}

+ 0 - 7
backend/src/lib.rs

@@ -1,7 +0,0 @@
-pub mod application;
-pub mod config;
-pub mod context;
-mod entities;
-pub mod middleware;
-pub mod services;
-pub mod util;

+ 0 - 14
backend/src/main.rs

@@ -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(())
-}

+ 0 - 105
backend/src/middleware/auth_middleware.rs

@@ -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<S, B> Transform<S, ServiceRequest> for AuthenticationService
-where
-    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
-    S::Future: 'static,
-    B: MessageBody + 'static,
-    B::Error: StdError,
-{
-    type Response = ServiceResponse;
-    type Error = Error;
-    type Transform = AuthenticationMiddleware<S>;
-    type InitError = ();
-    type Future = Ready<Result<Self::Transform, Self::InitError>>;
-
-    fn new_transform(&self, service: S) -> Self::Future {
-        ok(AuthenticationMiddleware { service })
-    }
-}
-pub struct AuthenticationMiddleware<S> {
-    service: S,
-}
-
-impl<S, B> Service<ServiceRequest> for AuthenticationMiddleware<S>
-where
-    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
-    S::Future: 'static,
-    B: MessageBody + 'static,
-    B::Error: StdError,
-{
-    type Response = ServiceResponse;
-    type Error = Error;
-    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
-
-    fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
-        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<LoggedUser, ServerError> = 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()
-}

+ 0 - 16
backend/src/middleware/cors_middleware.rs

@@ -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)
-}

+ 0 - 5
backend/src/middleware/mod.rs

@@ -1,5 +0,0 @@
-mod auth_middleware;
-mod cors_middleware;
-
-pub use auth_middleware::*;
-pub use cors_middleware::*;

+ 0 - 6
backend/src/services/document/mod.rs

@@ -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;

+ 0 - 63
backend/src/services/document/persistence.rs

@@ -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<RevisionKVPersistence>,
-    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<RevisionKVPersistence>,
-    params: DocumentId,
-) -> Result<DocumentInfo, ServerError> {
-    let _ = Uuid::parse_str(&params.doc_id).context("Parse document id to uuid failed")?;
-    let revisions = document_store.get_revisions(&params.doc_id, None).await?;
-    match make_document_info_pb_from_revisions_pb(&params.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<ServerDocumentManager>,
-    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<RevisionKVPersistence>,
-    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(())
-}

+ 0 - 48
backend/src/services/document/router.rs

@@ -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<Arc<FlowyPersistence>>,
-) -> Result<HttpResponse, ServerError> {
-    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<Arc<FlowyPersistence>>,
-) -> Result<HttpResponse, ServerError> {
-    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<Arc<ServerDocumentManager>>,
-) -> Result<HttpResponse, ServerError> {
-    let params: ResetDocumentParamsPB = parse_from_payload(payload).await?;
-    let _ = reset_document(document_manager.get_ref(), params).await?;
-    Ok(FlowyResponse::success().into())
-}

+ 0 - 159
backend/src/services/document/ws_actor.rs

@@ -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<FlowyPersistence>,
-        ret: oneshot::Sender<Result<()>>,
-    },
-}
-
-pub struct DocumentWebSocketActor {
-    actor_msg_receiver: Option<mpsc::Receiver<DocumentWSActorMessage>>,
-    doc_manager: Arc<ServerDocumentManager>,
-}
-
-impl DocumentWebSocketActor {
-    pub fn new(receiver: mpsc::Receiver<DocumentWSActorMessage>, manager: Arc<ServerDocumentManager>) -> 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::<ClientRevisionWSDataPB>(&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<WSUser>,
-    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),
-        }
-    }
-}

+ 0 - 176
backend/src/services/document/ws_receiver.rs

@@ -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<FlowyPersistence>,
-    document_manager: Arc<ServerDocumentManager>,
-) -> Arc<DocumentWebSocketReceiver> {
-    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<DocumentWSActorMessage>,
-    persistence: Arc<FlowyPersistence>,
-}
-
-impl DocumentWebSocketReceiver {
-    pub fn new(persistence: Arc<FlowyPersistence>, actor_msg_sender: mpsc::Sender<DocumentWSActorMessage>) -> 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<DocumentRevisionKV>);
-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<DocumentInfo, CollaborateError> {
-        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<Option<DocumentInfo>, 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<Vec<i64>>,
-    ) -> BoxResultFuture<Vec<RevisionPB>, 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::<Vec<RevisionPB>, 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()) })
-    }
-}

+ 0 - 113
backend/src/services/folder/app/controller.rs

@@ -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<AppPB, ServerError> {
-    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<AppPB, ServerError> {
-    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<AppTable, ServerError> {
-    let (sql, args) = SqlBuilder::select(APP_TABLE)
-        .add_field("*")
-        .and_where_eq("id", app_id)
-        .build()?;
-
-    let table = sqlx::query_as_with::<Postgres, AppTable, PgArguments>(&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<String>,
-    desc: Option<String>,
-    color_style: Option<Vec<u8>>,
-) -> 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(())
-}

+ 0 - 5
backend/src/services/folder/app/mod.rs

@@ -1,5 +0,0 @@
-#![allow(clippy::module_inception)]
-pub mod controller;
-pub mod router;
-
-pub mod persistence;

+ 0 - 140
backend/src/services/folder/app/persistence.rs

@@ -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<Self, ServerError> {
-        let app_id = check_app_id(app.id)?;
-        let create_time = DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(app.create_time, 0), Utc);
-        let modified_time = DateTime::<Utc>::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<u8> {
-    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<Uuid, ServerError> {
-    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<u8>,
-    pub(crate) last_view_id: String,
-    pub(crate) modified_time: chrono::DateTime<Utc>,
-    pub(crate) create_time: chrono::DateTime<Utc>,
-    #[allow(dead_code)]
-    pub(crate) user_id: String,
-}
-impl std::convert::From<AppTable> 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
-    }
-}

+ 0 - 114
backend/src/services/folder/app/router.rs

@@ -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<PgPool>,
-    logged_user: LoggedUser,
-) -> Result<HttpResponse, ServerError> {
-    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<PgPool>, user: LoggedUser) -> Result<HttpResponse, ServerError> {
-    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<PgPool>) -> Result<HttpResponse, ServerError> {
-    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<PgPool>) -> Result<HttpResponse, ServerError> {
-    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())
-}

+ 0 - 6
backend/src/services/folder/mod.rs

@@ -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;

+ 0 - 6
backend/src/services/folder/trash/mod.rs

@@ -1,6 +0,0 @@
-#![allow(clippy::module_inception)]
-mod persistence;
-pub mod router;
-mod trash;
-
-pub(crate) use trash::*;

+ 0 - 40
backend/src/services/folder/trash/persistence.rs

@@ -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<AppTable> 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<ViewTable> 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(),
-        }
-    }
-}

+ 0 - 106
backend/src/services/folder/trash/router.rs

@@ -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<PgPool>,
-    logged_user: LoggedUser,
-) -> Result<HttpResponse, ServerError> {
-    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<Arc<FlowyPersistence>>,
-    logged_user: LoggedUser,
-) -> Result<HttpResponse, ServerError> {
-    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<PgPool>, logged_user: LoggedUser) -> Result<HttpResponse, ServerError> {
-    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<Uuid, ServerError> {
-    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<Vec<(Uuid, i32)>, 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)
-}

+ 0 - 180
backend/src/services/folder/trash/trash.rs

@@ -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<DocumentRevisionKV>,
-    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::<Vec<(Uuid, i32)>>();
-    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<DocumentRevisionKV>,
-    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::<Postgres, TrashTable, PgArguments>(&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<DocumentRevisionKV>,
-    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<Vec<String>, ServerError> {
-    let repeated_trash = read_trash(transaction, user).await?.take_items().into_vec();
-    let ids = repeated_trash
-        .into_iter()
-        .map(|trash| trash.id)
-        .collect::<Vec<String>>();
-
-    Ok(ids)
-}
-
-#[tracing::instrument(skip(transaction, user), err)]
-pub(crate) async fn read_trash(
-    transaction: &mut DBTransaction<'_>,
-    user: &LoggedUser,
-) -> Result<RepeatedTrash, ServerError> {
-    let (sql, args) = SqlBuilder::select(TRASH_TABLE)
-        .add_field("*")
-        .and_where_eq("user_id", &user.user_id)
-        .build()?;
-
-    let tables = sqlx::query_as_with::<Postgres, TrashTable, PgArguments>(&sql, args)
-        .fetch_all(transaction as &mut DBTransaction<'_>)
-        .await
-        .map_err(map_sqlx_error)?;
-
-    let mut trash: Vec<Trash> = 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)
-}

+ 0 - 170
backend/src/services/folder/view/controller.rs

@@ -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<String>,
-    desc: Option<String>,
-    thumbnail: Option<String>,
-) -> 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<DocumentRevisionKV>,
-    view_ids: Vec<Uuid>,
-) -> 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<DocumentRevisionKV>,
-    params: CreateViewParamsPB,
-    user_id: &str,
-) -> Result<ViewPB, ServerError> {
-    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<ViewPB, ServerError> {
-    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<ViewTable, ServerError> {
-    let (sql, args) = SqlBuilder::select(VIEW_TABLE)
-        .add_field("*")
-        .and_where_eq("id", view_id)
-        .build()?;
-
-    let table = sqlx::query_as_with::<Postgres, ViewTable, PgArguments>(&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<Vec<ViewPB>, 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::<Postgres, ViewTable, PgArguments>(&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::<Vec<ViewPB>>();
-
-    Ok(views)
-}

+ 0 - 6
backend/src/services/folder/view/mod.rs

@@ -1,6 +0,0 @@
-#![allow(clippy::module_inception)]
-mod controller;
-pub mod persistence;
-pub mod router;
-
-pub(crate) use controller::*;

+ 0 - 134
backend/src/services/folder/view/persistence.rs

@@ -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<Self, ServerError> {
-        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::<Utc>::from_utc(NaiveDateTime::from_timestamp(view.create_time, 0), Utc);
-        let modified_time = DateTime::<Utc>::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<String>) -> Result<Vec<Uuid>, 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<Uuid, ServerError> {
-    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<Utc>,
-    pub(crate) create_time: chrono::DateTime<Utc>,
-    pub(crate) thumbnail: String,
-    pub(crate) view_type: i32,
-}
-impl std::convert::From<ViewTable> 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
-    }
-}

+ 0 - 128
backend/src/services/folder/view/router.rs

@@ -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<Arc<FlowyPersistence>>,
-    user: LoggedUser,
-) -> Result<HttpResponse, ServerError> {
-    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<PgPool>, user: LoggedUser) -> Result<HttpResponse, ServerError> {
-    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<PgPool>) -> Result<HttpResponse, ServerError> {
-    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<Arc<FlowyPersistence>>,
-) -> Result<HttpResponse, ServerError> {
-    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())
-}

+ 0 - 144
backend/src/services/folder/workspace/controller.rs

@@ -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<WorkspacePB, ServerError> {
-    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<String>,
-    desc: Option<String>,
-) -> 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<String>,
-    logged_user: LoggedUser,
-) -> Result<RepeatedWorkspacePB, ServerError> {
-    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::<Postgres, WorkspaceTable, PgArguments>(&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<RepeatedAppPB, ServerError> {
-    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::<Postgres, AppTable, PgArguments>(&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)
-}

+ 0 - 6
backend/src/services/folder/workspace/mod.rs

@@ -1,6 +0,0 @@
-#![allow(clippy::module_inception)]
-mod controller;
-pub mod persistence;
-pub mod router;
-
-pub use controller::*;

+ 0 - 98
backend/src/services/folder/workspace/persistence.rs

@@ -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<Self, ServerError> {
-        let workspace_id = check_workspace_id(workspace.id)?;
-        let create_time = DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(workspace.create_time, 0), Utc);
-        let modified_time = DateTime::<Utc>::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<Uuid, ServerError> {
-    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<Utc>,
-    pub(crate) create_time: chrono::DateTime<Utc>,
-    pub(crate) user_id: String,
-}
-impl std::convert::From<WorkspaceTable> 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
-    }
-}

+ 0 - 149
backend/src/services/folder/workspace/router.rs

@@ -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<PgPool>,
-    logged_user: LoggedUser,
-) -> Result<HttpResponse, ServerError> {
-    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<PgPool>,
-    logged_user: LoggedUser,
-) -> Result<HttpResponse, ServerError> {
-    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<PgPool>,
-    _logged_user: LoggedUser,
-) -> Result<HttpResponse, ServerError> {
-    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<PgPool>,
-    _logged_user: LoggedUser,
-) -> Result<HttpResponse, ServerError> {
-    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<PgPool>, logged_user: LoggedUser) -> Result<HttpResponse, ServerError> {
-    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())
-}

+ 0 - 148
backend/src/services/folder/ws_actor.rs

@@ -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<FlowyPersistence>,
-        ret: oneshot::Sender<Result<()>>,
-    },
-}
-
-pub struct FolderWebSocketActor {
-    actor_msg_receiver: Option<mpsc::Receiver<FolderWSActorMessage>>,
-    folder_manager: Arc<ServerFolderManager>,
-}
-
-impl FolderWebSocketActor {
-    pub fn new(receiver: mpsc::Receiver<FolderWSActorMessage>, folder_manager: Arc<ServerFolderManager>) -> 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::<ClientRevisionWSDataPB>(&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<WSUser>,
-    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),
-        }
-    }
-}

+ 0 - 169
backend/src/services/folder/ws_receiver.rs

@@ -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<FlowyPersistence>,
-    folder_manager: Arc<ServerFolderManager>,
-) -> Arc<FolderWebSocketReceiver> {
-    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<FolderWSActorMessage>,
-    persistence: Arc<FlowyPersistence>,
-}
-
-impl FolderWebSocketReceiver {
-    pub fn new(persistence: Arc<FlowyPersistence>, actor_msg_sender: mpsc::Sender<FolderWSActorMessage>) -> 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<FolderRevisionKV>);
-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<FolderInfo, CollaborateError> {
-        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<Option<FolderInfo>, 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<RevisionPB> = 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<RevisionPB> = 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<Vec<i64>>,
-    ) -> BoxResultFuture<Vec<RevisionPB>, 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<RevisionPB> = 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(())
-        })
-    }
-}

+ 0 - 210
backend/src/services/kv/kv.rs

@@ -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<Option<Bytes>, 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<KeyValue>) -> Result<(), ServerError> {
-        self.transaction(|mut transaction| Box::pin(async move { transaction.batch_set(kvs).await }))
-            .await
-    }
-
-    pub async fn batch_get(&self, keys: Vec<String>) -> Result<Vec<KeyValue>, ServerError> {
-        self.transaction(|mut transaction| Box::pin(async move { transaction.batch_get(keys).await }))
-            .await
-    }
-
-    pub async fn transaction<F, O>(&self, f: F) -> Result<O, ServerError>
-    where
-        F: for<'a> FnOnce(Box<dyn KVTransaction + 'a>) -> BoxResultFuture<O, ServerError>,
-    {
-        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<Option<Bytes>, 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::<Postgres, KVTable, PgArguments>(&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<KeyValue>) -> 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<String>) -> Result<Vec<KeyValue>, 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::<Vec<KeyValue>, ServerError>(kvs)
-    }
-
-    async fn batch_delete(&mut self, keys: Vec<String>) -> 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<Vec<KeyValue>, 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::<Vec<KeyValue>, 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<PgRow>) -> Vec<KeyValue> {
-    rows.into_iter()
-        .map(|row| {
-            let bytes: Vec<u8> = row.get("blob");
-            KeyValue {
-                key: row.get("id"),
-                value: Bytes::from(bytes),
-            }
-        })
-        .collect::<Vec<KeyValue>>()
-}
-
-#[derive(Debug, Clone, sqlx::FromRow)]
-struct KVTable {
-    #[allow(dead_code)]
-    pub(crate) id: String,
-    pub(crate) blob: Vec<u8>,
-}

+ 0 - 41
backend/src/services/kv/mod.rs

@@ -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<Option<Bytes>, 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<KeyValue>) -> Result<(), ServerError>;
-    async fn batch_get(&mut self, keys: Vec<String>) -> Result<Vec<KeyValue>, ServerError>;
-    async fn batch_delete(&mut self, keys: Vec<String>) -> Result<(), ServerError>;
-
-    async fn batch_get_start_with(&mut self, key: &str) -> Result<Vec<KeyValue>, 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,
-}

+ 0 - 128
backend/src/services/kv/revision_kv.rs

@@ -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<KVStore>,
-}
-
-impl std::ops::Deref for RevisionKVPersistence {
-    type Target = Arc<KVStore>;
-
-    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<KVStore>) -> Self {
-        RevisionKVPersistence { inner: kv_store }
-    }
-
-    pub(crate) async fn set_revision(&self, revisions: Vec<RevisionPB>) -> 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<T: Into<Option<Vec<i64>>>>(
-        &self,
-        object_id: &str,
-        rev_ids: T,
-    ) -> Result<RepeatedRevisionPB, ServerError> {
-        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::<Vec<String>>();
-
-                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<T: Into<Option<Vec<i64>>>>(
-        &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::<Vec<String>>();
-
-                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<RevisionPB>) -> Result<Vec<KeyValue>, 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<KeyValue>) -> RepeatedRevisionPB {
-    let mut revisions = items
-        .into_iter()
-        .filter_map(|kv| parse_from_bytes::<RevisionPB>(&kv.value).ok())
-        .collect::<Vec<RevisionPB>>();
-
-    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)
-}

+ 0 - 50
backend/src/services/log.rs

@@ -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(())
-    }
-}

+ 0 - 6
backend/src/services/mod.rs

@@ -1,6 +0,0 @@
-pub mod document;
-pub mod folder;
-pub mod kv;
-pub(crate) mod log;
-pub mod user;
-pub mod web_socket;

+ 0 - 236
backend/src/services/user/controller.rs

@@ -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<SignInResponsePB, ServerError> {
-    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<FlowyResponse, ServerError> {
-    AUTHORIZED_USERS.store_auth(logged_user, false);
-    Ok(FlowyResponse::success())
-}
-
-pub async fn register_user(pool: &PgPool, params: SignUpParamsPB) -> Result<FlowyResponse, ServerError> {
-    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<FlowyResponse, ServerError> {
-    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::<Postgres, UserTable>("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<FlowyResponse, ServerError> {
-    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<UserTable, ServerError> {
-    let user = sqlx::query_as::<Postgres, UserTable>("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<SignUpResponsePB, ServerError> {
-    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)
-}

+ 0 - 4
backend/src/services/user/mod.rs

@@ -1,4 +0,0 @@
-pub use controller::*;
-
-mod controller;
-pub mod router;

+ 0 - 64
backend/src/services/user/router.rs

@@ -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<PgPool>) -> Result<HttpResponse, ServerError> {
-    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<HttpResponse, ServerError> {
-    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<PgPool>,
-) -> Result<HttpResponse, ServerError> {
-    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<PgPool>,
-    payload: Payload,
-) -> Result<HttpResponse, ServerError> {
-    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<PgPool>) -> Result<HttpResponse, ServerError> {
-    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<PgPool>,
-) -> Result<HttpResponse, ServerError> {
-    unimplemented!()
-}

+ 0 - 50
backend/src/services/web_socket/entities/connect.rs

@@ -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<WebSocketMessage>;
-
-#[derive(Serialize, Deserialize, Debug, Clone, Hash, PartialEq, Eq)]
-pub struct SessionId(pub String);
-
-impl<T: AsRef<str>> std::convert::From<T> 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<Connect> 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,
-}

+ 0 - 27
backend/src/services/web_socket/entities/message.rs

@@ -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)
-}

+ 0 - 5
backend/src/services/web_socket/entities/mod.rs

@@ -1,5 +0,0 @@
-pub use connect::*;
-pub use message::*;
-
-mod connect;
-pub mod message;

+ 0 - 8
backend/src/services/web_socket/mod.rs

@@ -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;

+ 0 - 61
backend/src/services/web_socket/router.rs

@@ -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<String>,
-    server: Data<Addr<WSServer>>,
-    ws_receivers: Data<WebSocketReceivers>,
-) -> Result<HttpResponse, Error> {
-    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))
-            }
-        },
-    }
-}

+ 0 - 181
backend/src/services/web_socket/ws_client.rs

@@ -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<WSChannel, Arc<dyn WebSocketReceiver>>,
-}
-
-impl WebSocketReceivers {
-    pub fn new() -> Self {
-        WebSocketReceivers::default()
-    }
-
-    pub fn set(&mut self, channel: WSChannel, receiver: Arc<dyn WebSocketReceiver>) {
-        tracing::trace!("Add {:?} receiver", channel);
-        self.inner.insert(channel, receiver);
-    }
-
-    pub fn get(&self, source: &WSChannel) -> Option<Arc<dyn WebSocketReceiver>> {
-        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<WSUser>,
-    pub(crate) socket: Socket,
-    pub(crate) data: Bytes,
-}
-
-pub struct WSClient {
-    user: Arc<WSUser>,
-    server: Addr<WSServer>,
-    ws_receivers: Data<WebSocketReceivers>,
-    hb: Instant,
-}
-
-impl WSClient {
-    pub fn new(user: WSUser, server: Addr<WSServer>, ws_receivers: Data<WebSocketReceivers>) -> Self {
-        Self {
-            user: Arc::new(user),
-            server,
-            ws_receivers,
-            hb: Instant::now(),
-        }
-    }
-
-    fn hb(&self, ctx: &mut ws::WebsocketContext<Self>) {
-        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<Result<ws::Message, ws::ProtocolError>> for WSClient {
-    fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, 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<WebSocketMessage> 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<Self>;
-
-    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
-    }
-}

+ 0 - 65
backend/src/services/web_socket/ws_server.rs

@@ -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<SessionId, Session>,
-}
-
-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<Self>;
-    fn started(&mut self, _ctx: &mut Self::Context) {}
-}
-
-impl Handler<Connect> for WSServer {
-    type Result = Result<(), ServerError>;
-    fn handle(&mut self, msg: Connect, _ctx: &mut Context<Self>) -> Self::Result {
-        let session: Session = msg.into();
-        self.sessions.insert(session.id.clone(), session);
-
-        Ok(())
-    }
-}
-
-impl Handler<Disconnect> for WSServer {
-    type Result = Result<(), ServerError>;
-    fn handle(&mut self, msg: Disconnect, _: &mut Context<Self>) -> Self::Result {
-        self.sessions.remove(&msg.sid);
-        Ok(())
-    }
-}
-
-impl Handler<WebSocketMessage> for WSServer {
-    type Result = ();
-
-    fn handle(&mut self, _msg: WebSocketMessage, _ctx: &mut Context<Self>) -> Self::Result {
-        unimplemented!()
-    }
-}
-
-impl actix::Supervised for WSServer {
-    fn restarting(&mut self, _ctx: &mut Context<WSServer>) {
-        log::warn!("restarting");
-    }
-}

+ 0 - 3
backend/src/util/mod.rs

@@ -1,3 +0,0 @@
-pub mod serde_ext;
-pub mod sqlx_ext;
-pub mod user_ext;

+ 0 - 46
backend/src/util/serde_ext.rs

@@ -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<T: Message>(payload: web::Payload) -> Result<T, ServerError> {
-    let bytes = poll_payload(&mut payload.into_inner()).await?;
-    parse_from_bytes(&bytes)
-}
-
-#[allow(dead_code)]
-pub async fn parse_from_dev_payload<T: Message>(payload: &mut actix_web::dev::Payload) -> Result<T, ServerError> {
-    let bytes = poll_payload(payload).await?;
-    parse_from_bytes(&bytes)
-}
-
-#[inline]
-pub fn md5<T: AsRef<[u8]>>(data: T) -> String {
-    let md5 = format!("{:x}", md5::compute(data));
-    md5
-}
-
-pub fn parse_from_bytes<T: Message>(bytes: &[u8]) -> Result<T, ServerError> {
-    let result: ProtobufResult<T> = 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<web::BytesMut, ServerError> {
-    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)
-}

+ 0 - 6
backend/src/util/sqlx_ext/mod.rs

@@ -1,6 +0,0 @@
-mod query;
-mod utils;
-
-pub use utils::*;
-
-pub use query::*;

+ 0 - 159
backend/src/util/sqlx_ext/query.rs

@@ -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<String>,
-    filters: Vec<String>,
-    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<Postgres>,
-    {
-        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<Postgres>,
-    {
-        if add {
-            self.add_field_with_arg(field, arg)
-        } else {
-            self
-        }
-    }
-
-    pub fn add_some_arg<'a, T>(self, field: &str, arg: Option<T>) -> Self
-    where
-        T: 'a + Send + Encode<'a, Postgres> + Type<Postgres>,
-    {
-        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<Postgres>,
-    {
-        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::<Vec<String>>();
-
-                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))
-            }
-        }
-    }
-}

+ 0 - 11
backend/src/util/sqlx_ext/utils.rs

@@ -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),
-    }
-}

+ 0 - 31
backend/src/util/user_ext.rs

@@ -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<String, ServerError> {
-    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<bool, ServerError> {
-    match verify(source, hash) {
-        Ok(true) => Ok(true),
-        _ => Err(ServerError::new(
-            "Username and password don't match".to_string(),
-            ErrorCode::PasswordNotMatch,
-        )),
-    }
-}

+ 0 - 126
backend/tests/api_test/auth_test.rs

@@ -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, "[email protected]", "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 = "[email protected]";
-    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 = "[email protected]";
-    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 = "[email protected]";
-    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 = "[email protected]".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 = "[email protected]";
-    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
-}

+ 0 - 79
backend/tests/api_test/kv_test.rs

@@ -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::<Vec<String>>())
-        .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();
-}

+ 0 - 3
backend/tests/api_test/mod.rs

@@ -1,3 +0,0 @@
-mod auth_test;
-mod kv_test;
-mod workspace_test;

+ 0 - 282
backend/tests/api_test/workspace_test.rs

@@ -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::<Vec<String>>();
-    // 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<Revision> = 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::<PlainDoc>();
-    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);
-}

+ 0 - 179
backend/tests/document_test/edit_script.rs

@@ -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<DocScript>) {
-        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<Arc<ClientDocumentEditor>>,
-    client_sdk: FlowySDKTest,
-    client_user_session: Arc<UserSession>,
-    #[allow(dead_code)]
-    ws_conn: Arc<FlowyWebSocketConnect>,
-    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<ClientDocumentEditor> { self.client_editor.as_ref().unwrap().clone() }
-}
-
-async fn run_scripts(context: Arc<RwLock<ScriptContext>>, scripts: Vec<DocScript>) {
-    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<ServerDocumentManager>) {
-    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();
-}

+ 0 - 207
backend/tests/document_test/edit_test.rs

@@ -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::<NewlineDoc>();
-    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::<NewlineDoc>();
-    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::<NewlineDoc>();
-    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::<NewlineDoc>();
-    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;
-}

+ 0 - 2
backend/tests/document_test/mod.rs

@@ -1,2 +0,0 @@
-mod edit_script;
-mod edit_test;

+ 0 - 3
backend/tests/main.rs

@@ -1,3 +0,0 @@
-mod api_test;
-mod document_test;
-pub mod util;

+ 0 - 388
backend/tests/util/helper.rs

@@ -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<String>,
-    pub user_id: Option<String>,
-}
-
-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<SignInResponse, ServerError> {
-        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<App> {
-        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<View> {
-        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<DocumentInfo> {
-        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: "[email protected]".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<TestServer> 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,
-        }
-    }
-}

+ 0 - 1
backend/tests/util/mod.rs

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