diff --git a/.dockerignore b/.dockerignore index cfeb9009a9..18f578ca48 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,4 @@ frontend/app_flowy/ frontend/scripts/ frontend/rust-lib/target -backend/target/ shared-lib/target/ \ No newline at end of file diff --git a/.github/workflows/backend_general.yml b/.github/workflows/backend_general.yml deleted file mode 100644 index 0532e9a724..0000000000 --- a/.github/workflows/backend_general.yml +++ /dev/null @@ -1,156 +0,0 @@ -name: Backend - -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - -jobs: - test: - name: Test - runs-on: ubuntu-latest - services: - postgres: - image: postgres:12 - env: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: password - POSTGRES_DB: postgres - ports: - - 5433:5432 - env: - SQLX_VERSION: 0.5.7 - SQLX_FEATURES: postgres - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Cache dependencies - id: cache-dependencies - uses: actions/cache@v2 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - - name: Install stable toolchain - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - - - name: Cache sqlx-cli - uses: actions/cache@v2 - id: cache-sqlx - with: - path: | - ~/.cargo/bin/sqlx - ~/.cargo/bin/cargo-sqlx - key: ${{ runner.os }}-sqlx-${{ env.SQLX_VERSION }}-${{ env.SQLX_FEATURES }} - - - name: Install sqlx-cli - uses: actions-rs/cargo@v1 - if: steps.cache-sqlx.outputs.cache-hit == false - with: - command: install - args: > - sqlx-cli - --force - --version=${{ env.SQLX_VERSION }} - --features=${{ env.SQLX_FEATURES }} - --no-default-features - --locked - - - name: Migrate database - working-directory: backend/ - run: | - sudo apt-get install libpq-dev -y - SKIP_DOCKER=true POSTGRES_PORT=5433 ./scripts/init_database.sh - - - name: Check sqlx-data.json is up-to-date - working-directory: backend/ - run: | - cargo sqlx prepare --check -- --bin backend - - - name: Run cargo test - working-directory: backend/ - run: cargo test - - fmt: - name: Rustfmt - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - override: true - - run: rustup component add rustfmt - working-directory: backend/ - - run: cargo fmt --all -- --check - working-directory: backend/ - - - clippy: - name: Clippy - runs-on: ubuntu-latest - services: - postgres: - image: postgres:12 - env: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: password - POSTGRES_DB: postgres - ports: - - 5433:5432 - env: - SQLX_VERSION: 0.5.7 - SQLX_FEATURES: postgres - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Install stable toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - components: clippy - override: true - - - name: Cache sqlx-cli - uses: actions/cache@v2 - id: cache-sqlx - with: - path: | - ~/.cargo/bin/sqlx - key: ${{ runner.os }}-sqlx-${{ env.SQLX_VERSION }}-${{ env.SQLX_FEATURES }} - - - name: Install sqlx-cli - uses: actions-rs/cargo@v1 - if: steps.cache-sqlx.outputs.cache-hit == false - with: - command: install - args: > - sqlx-cli - --force - --version=${{ env.SQLX_VERSION }} - --features=${{ env.SQLX_FEATURES }} - --no-default-features - --locked - - - name: Migrate database - working-directory: backend/ - run: | - sudo apt-get install libpq-dev -y - SKIP_DOCKER=true POSTGRES_PORT=5433 ./scripts/init_database.sh - - - run: rustup component add clippy - working-directory: backend/ - - run: cargo clippy -- -D warnings - working-directory: backend/ - - diff --git a/.gitignore b/.gitignore index 8f201fa699..517b5e2f75 100644 --- a/.gitignore +++ b/.gitignore @@ -4,9 +4,6 @@ # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html # backend /target/ -./backend/.env -./backend/configuration/base.yaml -Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk diff --git a/backend/.dockerignore b/backend/.dockerignore deleted file mode 100644 index 6c23612e00..0000000000 --- a/backend/.dockerignore +++ /dev/null @@ -1,9 +0,0 @@ -.env -.dockerignore -spec.yaml -target/ -deploy/ -tests/ -Dockerfile -scripts/ -migrations/ \ No newline at end of file diff --git a/backend/.env b/backend/.env deleted file mode 100644 index 8ee6c6b1d5..0000000000 --- a/backend/.env +++ /dev/null @@ -1 +0,0 @@ -DATABASE_URL="postgres://postgres:password@localhost:5433/flowy" \ No newline at end of file diff --git a/backend/Cargo.lock b/backend/Cargo.lock deleted file mode 100644 index a5049a77ff..0000000000 --- a/backend/Cargo.lock +++ /dev/null @@ -1,4131 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "actix" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3720d0064a0ce5c0de7bd93bdb0a6caebab2a9b5668746145d7b3b0c5da02914" -dependencies = [ - "actix-rt", - "actix_derive", - "bitflags", - "bytes", - "crossbeam-channel", - "futures-core", - "futures-sink", - "futures-task", - "futures-util", - "log", - "once_cell", - "parking_lot", - "pin-project-lite", - "smallvec", - "tokio", - "tokio-util", -] - -[[package]] -name = "actix-codec" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d5dbeb2d9e51344cb83ca7cc170f1217f9fe25bfc50160e6e200b5c31c1019a" -dependencies = [ - "bitflags", - "bytes", - "futures-core", - "futures-sink", - "log", - "pin-project-lite", - "tokio", - "tokio-util", -] - -[[package]] -name = "actix-cors" -version = "0.6.0-beta.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa59d36d7ad063401d94ada05264a303791796ad5222d0954cc21f2e1052e99b" -dependencies = [ - "actix-service", - "actix-web", - "derive_more", - "futures-util", - "log", - "once_cell", - "smallvec", -] - -[[package]] -name = "actix-http" -version = "3.0.0-beta.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afaeb3d3fcb06b775ac62f05d580aae4afe5a149513333a73f688fdf26c06639" -dependencies = [ - "actix-codec", - "actix-rt", - "actix-service", - "actix-utils", - "ahash", - "base64 0.13.0", - "bitflags", - "brotli2", - "bytes", - "bytestring", - "derive_more", - "encoding_rs", - "flate2", - "futures-core", - "futures-util", - "h2", - "http", - "httparse", - "httpdate", - "itoa", - "language-tags", - "local-channel", - "log", - "mime", - "percent-encoding", - "pin-project 1.0.8", - "pin-project-lite", - "rand", - "sha-1", - "smallvec", - "zstd", -] - -[[package]] -name = "actix-identity" -version = "0.4.0-beta.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bfff0a087e890d3b9bed74951a554dc709c3ac92f855b07a6767d42bca9c7c2" -dependencies = [ - "actix-service", - "actix-web", - "futures-util", - "serde", - "serde_json", - "time 0.2.27", -] - -[[package]] -name = "actix-macros" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" -dependencies = [ - "quote", - "syn", -] - -[[package]] -name = "actix-router" -version = "0.5.0-beta.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36b95ce0d76d1aa2f98b681702807475ade0f99bd4552546a6843a966d42ea3d" -dependencies = [ - "bytestring", - "firestorm", - "http", - "log", - "regex", - "serde", -] - -[[package]] -name = "actix-rt" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a0c218d0a17c120f10ee0c69c9f0c45d87319e8f66b1f065e8412b612fc3e24" -dependencies = [ - "actix-macros", - "futures-core", - "tokio", -] - -[[package]] -name = "actix-server" -version = "2.0.0-beta.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "411dd3296dd317ff5eff50baa13f31923ea40ec855dd7f2d3ed8639948f0195f" -dependencies = [ - "actix-rt", - "actix-service", - "actix-utils", - "futures-core", - "futures-util", - "log", - "mio", - "num_cpus", - "socket2", - "tokio", -] - -[[package]] -name = "actix-service" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d3dc6a618b082974a08d7a4781d24d4691cba51500059bfebe6656a61ebfe1e" -dependencies = [ - "futures-core", - "paste", - "pin-project-lite", -] - -[[package]] -name = "actix-utils" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e491cbaac2e7fc788dfff99ff48ef317e23b3cf63dbaf7aaab6418f40f92aa94" -dependencies = [ - "local-waker", - "pin-project-lite", -] - -[[package]] -name = "actix-web" -version = "4.0.0-beta.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e85aa9bb018d83a0db70f557ba0cde9c6170a5d1de4fede02e377f68c1ac5aa9" -dependencies = [ - "actix-codec", - "actix-http", - "actix-macros", - "actix-router", - "actix-rt", - "actix-server", - "actix-service", - "actix-utils", - "actix-web-codegen", - "ahash", - "bytes", - "cfg-if", - "cookie", - "derive_more", - "either", - "encoding_rs", - "futures-core", - "futures-util", - "itoa", - "language-tags", - "log", - "mime", - "once_cell", - "paste", - "pin-project 1.0.8", - "regex", - "serde", - "serde_json", - "serde_urlencoded", - "smallvec", - "socket2", - "time 0.3.5", - "url", -] - -[[package]] -name = "actix-web-actors" -version = "4.0.0-beta.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b43c8e03f1877bc010316de455ee479aa0de0ae0717d49041db5f38fa192de" -dependencies = [ - "actix", - "actix-codec", - "actix-http", - "actix-web", - "bytes", - "bytestring", - "futures-core", - "pin-project 1.0.8", - "tokio", -] - -[[package]] -name = "actix-web-codegen" -version = "0.5.0-beta.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfe80a8828fa88a0420dc8fdd4c16b8207326c917f17701881b063eadc2a8d3b" -dependencies = [ - "actix-router", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "actix_derive" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d44b8fee1ced9671ba043476deddef739dd0959bf77030b26b738cc591737a7" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "addr2line" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61f2b7f93d2c7d2b08263acaa4a363b3e276806c68af6134c44f523bf1aacd" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "aead" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331" -dependencies = [ - "generic-array", -] - -[[package]] -name = "aes" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" -dependencies = [ - "aes-soft", - "aesni", - "cipher 0.2.5", -] - -[[package]] -name = "aes-gcm" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5278b5fabbb9bd46e24aa69b2fdea62c99088e0a950a9be40e3e0101298f88da" -dependencies = [ - "aead", - "aes", - "cipher 0.2.5", - "ctr", - "ghash", - "subtle", -] - -[[package]] -name = "aes-soft" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" -dependencies = [ - "cipher 0.2.5", - "opaque-debug", -] - -[[package]] -name = "aesni" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" -dependencies = [ - "cipher 0.2.5", - "opaque-debug", -] - -[[package]] -name = "ahash" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43bb833f0bf979d8475d38fbf09ed3b8a55e1885fe93ad3f93239fc6a4f17b98" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - -[[package]] -name = "aho-corasick" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" -dependencies = [ - "memchr", -] - -[[package]] -name = "allo-isolate" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff09da612ca86c794c3c8f70613a3e6652b5f1313b938937b32cae80df28fdb1" -dependencies = [ - "pin-project 0.4.28", -] - -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - -[[package]] -name = "anyhow" -version = "1.0.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28ae2b3dec75a406790005a200b1bd89785afc02517a00ca99ecfe093ee9e6cf" - -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - -[[package]] -name = "async-stream" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "171374e7e3b2504e0e5236e3b59260560f9fe94bfe9ac39ba5e4e929c5590625" -dependencies = [ - "async-stream-impl", - "futures-core", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "648ed8c8d2ce5409ccd57453d9d1b214b342a0d69376a6feda1fd6cae3299308" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "async-trait" -version = "0.1.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "atoi" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616896e05fc0e2649463a93a15183c6a16bf03413a7af88ef1285ddedfa9cda5" -dependencies = [ - "num-traits", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" - -[[package]] -name = "backend" -version = "0.1.0" -dependencies = [ - "actix", - "actix-codec", - "actix-cors", - "actix-http", - "actix-identity", - "actix-rt", - "actix-service", - "actix-web", - "actix-web-actors", - "anyhow", - "async-stream", - "async-trait", - "backend", - "backend-service", - "bcrypt", - "byteorder", - "bytes", - "chrono", - "config", - "dashmap", - "derive_more", - "flowy-collaboration", - "flowy-document", - "flowy-folder-data-model", - "flowy-net", - "flowy-sdk", - "flowy-test", - "flowy-user", - "flowy-user-data-model", - "futures", - "futures-core", - "futures-util", - "jsonwebtoken", - "lazy_static", - "lib-infra", - "lib-ot", - "lib-ws", - "linkify", - "log", - "md5", - "once_cell", - "ormx", - "parking_lot", - "pin-project 1.0.8", - "protobuf", - "serde", - "serde-aux", - "serde_json", - "serde_repr", - "sql-builder", - "sqlx", - "thiserror", - "tokio", - "toml", - "tracing", - "tracing-appender", - "tracing-bunyan-formatter", - "tracing-core", - "tracing-futures", - "tracing-log", - "tracing-subscriber", - "uuid", -] - -[[package]] -name = "backend-service" -version = "0.1.0" -dependencies = [ - "actix-web", - "anyhow", - "bytes", - "config", - "derive_more", - "flowy-collaboration", - "flowy-folder-data-model", - "flowy-user-data-model", - "hyper", - "lazy_static", - "log", - "protobuf", - "reqwest", - "serde", - "serde-aux", - "serde_json", - "serde_repr", - "thiserror", - "tokio", - "tracing", - "uuid", -] - -[[package]] -name = "backtrace" -version = "0.3.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7a905d892734eea339e896738c14b9afce22b5318f64b951e70bf3844419b01" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base-x" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" - -[[package]] -name = "base64" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" - -[[package]] -name = "base64" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" - -[[package]] -name = "bcrypt" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f691e63585950d8c1c43644d11bab9073e40f5060dd2822734ae7c3dc69a3a80" -dependencies = [ - "base64 0.13.0", - "blowfish", - "getrandom", -] - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bit-set" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitvec" -version = "0.19.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8942c8d352ae1838c9dda0b0ca2ab657696ef2232a20147cf1b30ae1a9cb4321" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - -[[package]] -name = "blowfish" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe3ff3fc1de48c1ac2e3341c4df38b0d1bfb8fdf04632a187c8b75aaa319a7ab" -dependencies = [ - "byteorder", - "cipher 0.3.0", - "opaque-debug", -] - -[[package]] -name = "brotli-sys" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4445dea95f4c2b41cde57cc9fee236ae4dbae88d8fcbdb4750fc1bb5d86aaecd" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "brotli2" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cb036c3eade309815c15ddbacec5b22c4d1f3983a774ab2eac2e3e9ea85568e" -dependencies = [ - "brotli-sys", - "libc", -] - -[[package]] -name = "bumpalo" -version = "3.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" - -[[package]] -name = "bytecount" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" -dependencies = [ - "serde", -] - -[[package]] -name = "bytestring" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90706ba19e97b90786e19dc0d5e2abd80008d99d4c0c5d1ad0b5e72cec7c494d" -dependencies = [ - "bytes", -] - -[[package]] -name = "cc" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2" -dependencies = [ - "jobserver", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" -dependencies = [ - "libc", - "num-integer", - "num-traits", - "serde", - "time 0.1.44", - "winapi", -] - -[[package]] -name = "cipher" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" -dependencies = [ - "generic-array", -] - -[[package]] -name = "cipher" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" -dependencies = [ - "generic-array", -] - -[[package]] -name = "claim" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81099d6bb72e1df6d50bb2347224b666a670912bb7f06dbe867a4a070ab3ce8" -dependencies = [ - "autocfg", -] - -[[package]] -name = "color-eyre" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f1885697ee8a177096d42f158922251a41973117f6d8a234cee94b9509157b7" -dependencies = [ - "backtrace", - "eyre", - "indenter", - "once_cell", - "owo-colors", -] - -[[package]] -name = "config" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b076e143e1d9538dde65da30f8481c2a6c44040edb8e02b9bf1351edb92ce3" -dependencies = [ - "lazy_static", - "nom 5.1.2", - "serde", - "yaml-rust", -] - -[[package]] -name = "const_fn" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f92cfa0fd5690b3cf8c1ef2cabbd9b7ef22fa53cf5e1f92b05103f6d5d1cf6e7" - -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - -[[package]] -name = "cookie" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f1c7727e460397e56abc4bddc1d49e07a1ad78fc98eb2e1c8f032a58a2f80d" -dependencies = [ - "aes-gcm", - "base64 0.13.0", - "hkdf", - "hmac 0.10.1", - "percent-encoding", - "rand", - "sha2", - "subtle", - "time 0.2.27", - "version_check", -] - -[[package]] -name = "core-foundation" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" - -[[package]] -name = "cpufeatures" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66c99696f6c9dd7f35d486b9d04d7e6e202aa3e8c40d553f2fdf5e7e0c6a71ef" -dependencies = [ - "libc", -] - -[[package]] -name = "cpuid-bool" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" - -[[package]] -name = "crc" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10c2722795460108a7872e1cd933a85d6ec38abc4baecad51028f702da28889f" -dependencies = [ - "crc-catalog", -] - -[[package]] -name = "crc-catalog" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccaeedb56da03b09f598226e25e80088cb4cd25f316e6e4df7d695f0feeb1403" - -[[package]] -name = "crc32fast" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae5588f6b3c3cb05239e90bd110f257254aecd01e4635400391aeae07497845" -dependencies = [ - "cfg-if", - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-epoch", - "crossbeam-queue", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" -dependencies = [ - "cfg-if", - "crossbeam-utils", - "lazy_static", - "memoffset", - "scopeguard", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b10ddc024425c88c2ad148c1b0fd53f4c6d38db9697c9f1588381212fa657c9" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" -dependencies = [ - "cfg-if", - "lazy_static", -] - -[[package]] -name = "crypto-mac" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "crypto-mac" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "ctr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f" -dependencies = [ - "cipher 0.2.5", -] - -[[package]] -name = "darling" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "757c0ded2af11d8e739c4daea1ac623dd1624b06c844cf3f5a39f1bdbd99bb12" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c34d8efb62d0c2d7f60ece80f75e5c63c1588ba68032740494b0b9a996466e3" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn", -] - -[[package]] -name = "darling_macro" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade7bff147130fe5e6d39f089c6bd49ec0250f35d70b2eebf72afdfc919f15cc" -dependencies = [ - "darling_core", - "quote", - "syn", -] - -[[package]] -name = "dart-notify" -version = "0.1.0" -dependencies = [ - "allo-isolate", - "bytes", - "flowy-derive", - "lazy_static", - "lib-dispatch", - "log", - "protobuf", -] - -[[package]] -name = "dashmap" -version = "4.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" -dependencies = [ - "cfg-if", - "num_cpus", -] - -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "derive_more" -version = "0.99.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40eebddd2156ce1bb37b20bbe5151340a31828b1f2d22ba4141f3531710e38df" -dependencies = [ - "convert_case", - "proc-macro2", - "quote", - "rustc_version 0.3.3", - "syn", -] - -[[package]] -name = "diesel" -version = "1.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b28135ecf6b7d446b43e27e225622a038cc4e2930a1022f51cdb97ada19b8e4d" -dependencies = [ - "byteorder", - "diesel_derives", - "libsqlite3-sys", -] - -[[package]] -name = "diesel_derives" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "diesel_migrations" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf3cde8413353dc7f5d72fa8ce0b99a560a359d2c5ef1e5817ca731cd9008f4c" -dependencies = [ - "migrations_internals", - "migrations_macros", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - -[[package]] -name = "dirs" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "discard" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" - -[[package]] -name = "dissimilar" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31ad93652f40969dead8d4bf897a41e9462095152eb21c56e5830537e41179dd" - -[[package]] -name = "dotenv" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" - -[[package]] -name = "dyn-clone" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee2626afccd7561a06cf1367e2950c4718ea04565e20fb5029b6c7d8ad09abcf" - -[[package]] -name = "either" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" -dependencies = [ - "serde", -] - -[[package]] -name = "encoding_rs" -version = "0.8.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80df024fbc5ac80f87dfef0d9f5209a252f2a497f7f42944cff24d8253cac065" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "env_logger" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "error-chain" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e791d3be96241c77c43846b665ef1384606da2cd2a48730abe606a12906e02" -dependencies = [ - "backtrace", -] - -[[package]] -name = "error-code" -version = "0.1.0" -dependencies = [ - "derive_more", - "flowy-derive", - "protobuf", -] - -[[package]] -name = "eyre" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "221239d1d5ea86bf5d6f91c9d6bc3646ffe471b08ff9b0f91c44f115ac969d2b" -dependencies = [ - "indenter", - "once_cell", -] - -[[package]] -name = "fancy-regex" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe09872bd11351a75f22b24c3769fc863e8212d926d6db46b94ad710d14cc5cc" -dependencies = [ - "bit-set", - "regex", -] - -[[package]] -name = "firestorm" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31586bda1b136406162e381a3185a506cdfc1631708dd40cba2f6628d8634499" - -[[package]] -name = "flate2" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0" -dependencies = [ - "cfg-if", - "crc32fast", - "libc", - "miniz_oxide", -] - -[[package]] -name = "flowy-ast" -version = "0.1.0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "flowy-collaboration" -version = "0.1.0" -dependencies = [ - "async-stream", - "bytes", - "chrono", - "dashmap", - "dissimilar", - "flowy-derive", - "flowy-folder-data-model", - "futures", - "lib-infra", - "lib-ot", - "log", - "md5", - "parking_lot", - "protobuf", - "serde", - "serde_json", - "strum", - "strum_macros", - "tokio", - "tracing", - "url", -] - -[[package]] -name = "flowy-database" -version = "0.1.0" -dependencies = [ - "diesel", - "diesel_derives", - "diesel_migrations", - "lazy_static", - "lib-sqlite", - "log", -] - -[[package]] -name = "flowy-derive" -version = "0.1.0" -dependencies = [ - "flowy-ast", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "flowy-document" -version = "0.1.0" -dependencies = [ - "async-stream", - "async-trait", - "bytecount", - "byteorder", - "bytes", - "chrono", - "dart-notify", - "dashmap", - "derive_more", - "diesel", - "diesel_derives", - "flowy-collaboration", - "flowy-database", - "flowy-derive", - "flowy-error", - "flowy-sync", - "futures", - "futures-util", - "lib-dispatch", - "lib-infra", - "lib-ot", - "lib-ws", - "log", - "parking_lot", - "pin-project 1.0.8", - "protobuf", - "serde", - "serde_json", - "strum", - "strum_macros", - "tokio", - "tracing", - "unicode-segmentation", - "url", -] - -[[package]] -name = "flowy-error" -version = "0.1.0" -dependencies = [ - "backend-service", - "bytes", - "error-code", - "flowy-collaboration", - "flowy-database", - "flowy-derive", - "lib-dispatch", - "lib-ot", - "lib-sqlite", - "protobuf", - "r2d2", - "serde_json", -] - -[[package]] -name = "flowy-folder" -version = "0.1.0" -dependencies = [ - "bincode", - "bytes", - "chrono", - "crossbeam", - "crossbeam-utils", - "dart-notify", - "derive_more", - "diesel", - "diesel_derives", - "flowy-collaboration", - "flowy-database", - "flowy-derive", - "flowy-document", - "flowy-error", - "flowy-folder-data-model", - "flowy-sync", - "futures", - "futures-core", - "lazy_static", - "lib-dispatch", - "lib-infra", - "lib-ot", - "lib-sqlite", - "log", - "parking_lot", - "pin-project 1.0.8", - "protobuf", - "serde", - "strum", - "strum_macros", - "tokio", - "tracing", -] - -[[package]] -name = "flowy-folder-data-model" -version = "0.1.0" -dependencies = [ - "bytes", - "chrono", - "derive_more", - "error-code", - "flowy-derive", - "log", - "protobuf", - "serde", - "serde_json", - "strum", - "strum_macros", - "unicode-segmentation", - "uuid", -] - -[[package]] -name = "flowy-net" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-stream", - "backend-service", - "bytes", - "dashmap", - "flowy-collaboration", - "flowy-derive", - "flowy-document", - "flowy-error", - "flowy-folder", - "flowy-folder-data-model", - "flowy-user", - "flowy-user-data-model", - "futures-util", - "lazy_static", - "lib-dispatch", - "lib-infra", - "lib-ws", - "parking_lot", - "protobuf", - "strum", - "strum_macros", - "tokio", - "tracing", -] - -[[package]] -name = "flowy-sdk" -version = "0.1.0" -dependencies = [ - "backend-service", - "bytes", - "color-eyre", - "flowy-collaboration", - "flowy-database", - "flowy-document", - "flowy-folder", - "flowy-net", - "flowy-sync", - "flowy-user", - "futures-core", - "lib-dispatch", - "lib-infra", - "lib-log", - "lib-ws", - "log", - "parking_lot", - "tokio", - "tracing", -] - -[[package]] -name = "flowy-sync" -version = "0.1.0" -dependencies = [ - "async-stream", - "bytes", - "dashmap", - "diesel", - "diesel_derives", - "flowy-collaboration", - "flowy-database", - "flowy-error", - "futures-util", - "lib-infra", - "lib-ot", - "lib-ws", - "parking_lot", - "protobuf", - "serde", - "serde_json", - "strum", - "strum_macros", - "tokio", - "tracing", -] - -[[package]] -name = "flowy-test" -version = "0.1.0" -dependencies = [ - "backend-service", - "bincode", - "bytes", - "claim", - "flowy-collaboration", - "flowy-folder", - "flowy-net", - "flowy-sdk", - "flowy-user", - "futures-util", - "lib-dispatch", - "lib-infra", - "lib-ot", - "log", - "protobuf", - "serde", - "serde_json", - "thread-id", - "tokio", -] - -[[package]] -name = "flowy-user" -version = "0.1.0" -dependencies = [ - "bytes", - "dart-notify", - "dashmap", - "derive_more", - "diesel", - "diesel_derives", - "flowy-database", - "flowy-derive", - "flowy-error", - "flowy-user-data-model", - "futures-core", - "lazy_static", - "lib-dispatch", - "lib-infra", - "lib-sqlite", - "log", - "once_cell", - "parking_lot", - "pin-project 1.0.8", - "protobuf", - "r2d2", - "serde", - "serde_json", - "strum", - "strum_macros", - "thread-id", - "thread_local", - "tokio", - "tracing", -] - -[[package]] -name = "flowy-user-data-model" -version = "0.1.0" -dependencies = [ - "bytes", - "derive_more", - "error-code", - "fancy-regex", - "flowy-derive", - "lazy_static", - "log", - "protobuf", - "serde", - "unicode-segmentation", - "validator", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" -dependencies = [ - "matches", - "percent-encoding", -] - -[[package]] -name = "funty" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" - -[[package]] -name = "futures" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12aa0eb539080d55c3f2d45a67c3b58b6b0773c1a3ca2dfec66d58c97fd66ca" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d" - -[[package]] -name = "futures-executor" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45025be030969d763025784f7f355043dc6bc74093e4ecc5000ca4dc50d8745c" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-intrusive" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62007592ac46aa7c2b6416f7deb9a8a8f63a01e0f1d6e1787d5630170db2b63e" -dependencies = [ - "futures-core", - "lock_api", - "parking_lot", -] - -[[package]] -name = "futures-io" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377" - -[[package]] -name = "futures-macro" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18e4a4b95cea4b4ccbcf1c5675ca7c4ee4e9e75eb79944d07defde18068f79bb" -dependencies = [ - "autocfg", - "proc-macro-hack", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "futures-sink" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36ea153c13024fe480590b3e3d4cad89a0cfacecc24577b68f86c6ced9c2bc11" - -[[package]] -name = "futures-task" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99" - -[[package]] -name = "futures-util" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481" -dependencies = [ - "autocfg", - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "proc-macro-hack", - "proc-macro-nested", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "gethostname" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e692e296bfac1d2533ef168d0b60ff5897b8b70a4009276834014dd8924cc028" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "getrandom" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "ghash" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97304e4cd182c3846f7575ced3890c53012ce534ad9114046b0a9e00bb30a375" -dependencies = [ - "opaque-debug", - "polyval", -] - -[[package]] -name = "gimli" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0a01e0497841a3b2db4f8afa483cce65f7e96a3498bd6c541734792aeac8fe7" - -[[package]] -name = "h2" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "825343c4eef0b63f541f8903f395dc5beb362a979b5799a84062527ef1e37726" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashlink" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf" -dependencies = [ - "hashbrown", -] - -[[package]] -name = "heck" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hkdf" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f" -dependencies = [ - "digest", - "hmac 0.10.1", -] - -[[package]] -name = "hmac" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" -dependencies = [ - "crypto-mac 0.10.1", - "digest", -] - -[[package]] -name = "hmac" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" -dependencies = [ - "crypto-mac 0.11.1", - "digest", -] - -[[package]] -name = "http" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "399c583b2979440c60be0821a6199eca73bc3c8dcd9d070d75ac726e2c6186e5" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" - -[[package]] -name = "httpdate" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6456b8a6c8f33fee7d958fcd1b60d55b11940a79e63ae87013e6d22e26034440" - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "hyper" -version = "0.14.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b61cf2d1aebcf6e6352c97b81dc2244ca29194be1b276f5d8ad5c6330fffb11" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "indenter" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" - -[[package]] -name = "indexmap" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" -dependencies = [ - "autocfg", - "hashbrown", -] - -[[package]] -name = "instant" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "ipnet" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" - -[[package]] -name = "itertools" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" - -[[package]] -name = "jobserver" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" -dependencies = [ - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce791b7ca6638aae45be056e068fc756d871eb3b3b10b8efa62d1c9cec616752" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "jsonwebtoken" -version = "7.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afabcc15e437a6484fc4f12d0fd63068fe457bf93f1c148d3d9649c60b103f32" -dependencies = [ - "base64 0.12.3", - "pem", - "ring", - "serde", - "serde_json", - "simple_asn1", -] - -[[package]] -name = "language-tags" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "lexical-core" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" -dependencies = [ - "arrayvec", - "bitflags", - "cfg-if", - "ryu", - "static_assertions", -] - -[[package]] -name = "lib-dispatch" -version = "0.1.0" -dependencies = [ - "bytes", - "dashmap", - "derivative", - "dyn-clone", - "env_logger", - "futures", - "futures-channel", - "futures-core", - "futures-util", - "lazy_static", - "log", - "paste", - "pin-project 1.0.8", - "protobuf", - "serde", - "serde_json", - "serde_with", - "thread-id", - "tokio", - "tracing", - "uuid", -] - -[[package]] -name = "lib-infra" -version = "0.1.0" -dependencies = [ - "bytes", - "chrono", - "futures-core", - "log", - "pin-project 1.0.8", - "rand", - "tokio", - "uuid", -] - -[[package]] -name = "lib-log" -version = "0.1.0" -dependencies = [ - "chrono", - "lazy_static", - "log", - "serde", - "serde_json", - "tracing", - "tracing-appender", - "tracing-bunyan-formatter", - "tracing-core", - "tracing-futures", - "tracing-log", - "tracing-subscriber", -] - -[[package]] -name = "lib-ot" -version = "0.1.0" -dependencies = [ - "anyhow", - "bytecount", - "bytes", - "dashmap", - "derive_more", - "lazy_static", - "log", - "md5", - "serde", - "serde_json", - "strum", - "strum_macros", - "thiserror", - "tokio", - "tracing", -] - -[[package]] -name = "lib-sqlite" -version = "0.1.0" -dependencies = [ - "diesel", - "diesel_derives", - "diesel_migrations", - "error-chain", - "lazy_static", - "libsqlite3-sys", - "log", - "r2d2", - "scheduled-thread-pool", -] - -[[package]] -name = "lib-ws" -version = "0.1.0" -dependencies = [ - "backend-service", - "bytes", - "dashmap", - "flowy-derive", - "futures", - "futures-channel", - "futures-core", - "futures-util", - "lib-infra", - "log", - "parking_lot", - "paste", - "pin-project 1.0.8", - "protobuf", - "strum", - "strum_macros", - "tokio", - "tokio-tungstenite", - "tracing", - "url", -] - -[[package]] -name = "libc" -version = "0.2.99" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765" - -[[package]] -name = "libsqlite3-sys" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e9eb7b8e152b6a01be6a4a2917248381875758250dc3df5d46caf9250341dda" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "linked-hash-map" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" - -[[package]] -name = "linkify" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78d59d732ba6d7eeefc418aab8057dc8e3da4374bd5802ffa95bebc04b4d1dfb" -dependencies = [ - "memchr", -] - -[[package]] -name = "local-channel" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6246c68cf195087205a0512559c97e15eaf95198bf0e206d662092cdcb03fe9f" -dependencies = [ - "futures-core", - "futures-sink", - "futures-util", - "local-waker", -] - -[[package]] -name = "local-waker" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84f9a2d3e27ce99ce2c3aad0b09b1a7b916293ea9b2bf624c13fe646fadd8da4" - -[[package]] -name = "lock_api" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "maplit" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" - -[[package]] -name = "matchers" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" -dependencies = [ - "regex-automata", -] - -[[package]] -name = "matches" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" - -[[package]] -name = "md-5" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" -dependencies = [ - "block-buffer", - "digest", - "opaque-debug", -] - -[[package]] -name = "md5" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" - -[[package]] -name = "memchr" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" - -[[package]] -name = "memoffset" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" -dependencies = [ - "autocfg", -] - -[[package]] -name = "migrations_internals" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b4fc84e4af020b837029e017966f86a1c2d5e83e64b589963d5047525995860" -dependencies = [ - "diesel", -] - -[[package]] -name = "migrations_macros" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9753f12909fd8d923f75ae5c3258cae1ed3c8ec052e1b38c93c21a6d157f789c" -dependencies = [ - "migrations_internals", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "mime" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" - -[[package]] -name = "miniz_oxide" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" -dependencies = [ - "adler", - "autocfg", -] - -[[package]] -name = "mio" -version = "0.7.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16" -dependencies = [ - "libc", - "log", - "miow", - "ntapi", - "winapi", -] - -[[package]] -name = "miow" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" -dependencies = [ - "winapi", -] - -[[package]] -name = "native-tls" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "nom" -version = "5.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" -dependencies = [ - "lexical-core", - "memchr", - "version_check", -] - -[[package]] -name = "nom" -version = "6.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2" -dependencies = [ - "bitvec", - "funty", - "lexical-core", - "memchr", - "version_check", -] - -[[package]] -name = "ntapi" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" -dependencies = [ - "winapi", -] - -[[package]] -name = "num-bigint" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "object" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c55827317fb4c08822499848a14237d2874d6f139828893017237e7ab93eb386" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "openssl" -version = "0.10.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d9facdb76fec0b73c406f125d44d86fdad818d66fef0531eec9233ca425ff4a" -dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-sys", -] - -[[package]] -name = "openssl-probe" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" - -[[package]] -name = "openssl-sys" -version = "0.9.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1996d2d305e561b70d1ee0c53f1542833f4e1ac6ce9a6708b6ff2738ca67dc82" -dependencies = [ - "autocfg", - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "ormx" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d99c9ec0597b587e61d52d8dc5914845a48a5a71f412a0a20ac4edef5db4f98" -dependencies = [ - "futures", - "ormx-macros", - "sqlx", -] - -[[package]] -name = "ormx-macros" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afe154fda773c54b533b54d4c9b97949aa37ebe5d77cdabc497e494f3ec05418" -dependencies = [ - "itertools", - "once_cell", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "owo-colors" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2386b4ebe91c2f7f51082d4cefa145d030e33a1842a96b12e4885cc3c01f7a55" - -[[package]] -name = "parking_lot" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall 0.2.10", - "smallvec", - "winapi", -] - -[[package]] -name = "paste" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf547ad0c65e31259204bd90935776d1c693cec2f4ff7abb7a1bbbd40dfe58" - -[[package]] -name = "pem" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" -dependencies = [ - "base64 0.13.0", - "once_cell", - "regex", -] - -[[package]] -name = "percent-encoding" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" - -[[package]] -name = "pest" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" -dependencies = [ - "ucd-trie", -] - -[[package]] -name = "pin-project" -version = "0.4.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "918192b5c59119d51e0cd221f4d49dde9112824ba717369e903c97d076083d0f" -dependencies = [ - "pin-project-internal 0.4.28", -] - -[[package]] -name = "pin-project" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "576bc800220cc65dac09e99e97b08b358cfab6e17078de8dc5fee223bd2d0c08" -dependencies = [ - "pin-project-internal 1.0.8", -] - -[[package]] -name = "pin-project-internal" -version = "0.4.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be26700300be6d9d23264c73211d8190e755b6b5ca7a1b28230025511b52a5e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "pin-project-internal" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e8fe8163d14ce7f0cdac2e040116f22eac817edabff0be91e8aff7e9accf389" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" - -[[package]] -name = "polyval" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eebcc4aa140b9abd2bc40d9c3f7ccec842679cd79045ac3a7ac698c1a064b7cd" -dependencies = [ - "cpuid-bool", - "opaque-debug", - "universal-hash", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro-hack" -version = "0.5.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" - -[[package]] -name = "proc-macro-nested" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" - -[[package]] -name = "proc-macro2" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "protobuf" -version = "2.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020f86b07722c5c4291f7c723eac4676b3892d47d9a7708dc2779696407f039b" - -[[package]] -name = "quote" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r2d2" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545c5bc2b880973c9c10e4067418407a0ccaa3091781d1671d46eb35107cb26f" -dependencies = [ - "log", - "parking_lot", - "scheduled-thread-pool", -] - -[[package]] -name = "radium" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8" - -[[package]] -name = "rand" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", - "rand_hc", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_hc" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" -dependencies = [ - "rand_core", -] - -[[package]] -name = "redox_syscall" -version = "0.1.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" - -[[package]] -name = "redox_syscall" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" -dependencies = [ - "bitflags", -] - -[[package]] -name = "redox_users" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" -dependencies = [ - "getrandom", - "redox_syscall 0.2.10", -] - -[[package]] -name = "regex" -version = "1.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.6.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" - -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - -[[package]] -name = "reqwest" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "246e9f61b9bb77df069a947682be06e31ac43ea37862e244a69f177694ea6d22" -dependencies = [ - "base64 0.13.0", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "http", - "http-body", - "hyper", - "hyper-tls", - "ipnet", - "js-sys", - "lazy_static", - "log", - "mime", - "native-tls", - "percent-encoding", - "pin-project-lite", - "serde", - "serde_urlencoded", - "tokio", - "tokio-native-tls", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg", -] - -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin", - "untrusted", - "web-sys", - "winapi", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dead70b0b5e03e9c814bcb6b01e03e68f7c57a80aa48c72ec92152ab3e818d49" - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver 0.9.0", -] - -[[package]] -name = "rustc_version" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" -dependencies = [ - "semver 0.11.0", -] - -[[package]] -name = "rustls" -version = "0.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" -dependencies = [ - "base64 0.13.0", - "log", - "ring", - "sct", - "webpki", -] - -[[package]] -name = "rustversion" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088" - -[[package]] -name = "ryu" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" - -[[package]] -name = "schannel" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" -dependencies = [ - "lazy_static", - "winapi", -] - -[[package]] -name = "scheduled-thread-pool" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc6f74fd1204073fa02d5d5d68bec8021be4c38690b61264b2fdb48083d0e7d7" -dependencies = [ - "parking_lot", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "sct" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "security-framework" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23a2ac85147a3a11d77ecf1bc7166ec0b92febfa4461c37944e180f319ece467" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4effb91b4b8b6fb7732e670b6cee160278ff8e6bf485c7805d9e319d76e284" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser 0.7.0", -] - -[[package]] -name = "semver" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser 0.10.2", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] - -[[package]] -name = "serde" -version = "1.0.127" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f03b9878abf6d14e6779d3f24f07b2cfa90352cfec4acc5aab8f1ac7f146fae8" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-aux" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "905f2fc9f3d1574e8b5923a58118240021f01d4e239673937ffb9f42707a4f22" -dependencies = [ - "chrono", - "serde", - "serde_json", -] - -[[package]] -name = "serde_derive" -version = "1.0.127" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a024926d3432516606328597e0f224a51355a493b49fdd67e9209187cbe55ecc" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "336b10da19a12ad094b59d870ebde26a45402e5b470add4b5fd03c5048a32127" -dependencies = [ - "indexmap", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_repr" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98d0516900518c29efa217c298fa1f4e6c6ffc85ae29fd7f4ee48f176e1a9ed5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_with" -version = "1.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad9fdbb69badc8916db738c25efd04f0a65297d26c2f8de4b62e57b8c12bc72" -dependencies = [ - "rustversion", - "serde", - "serde_with_macros", -] - -[[package]] -name = "serde_with_macros" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1569374bd54623ec8bd592cf22ba6e03c0f177ff55fbc8c29a49e296e7adecf" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "sha-1" -version = "0.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a0c8611594e2ab4ebbf06ec7cbbf0a99450b8570e96cbf5188b5d5f6ef18d81" -dependencies = [ - "block-buffer", - "cfg-if", - "cpufeatures", - "digest", - "opaque-debug", -] - -[[package]] -name = "sha1" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" - -[[package]] -name = "sha2" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b362ae5752fd2137731f9fa25fd4d9058af34666ca1966fb969119cc35719f12" -dependencies = [ - "block-buffer", - "cfg-if", - "cpufeatures", - "digest", - "opaque-debug", -] - -[[package]] -name = "sharded-slab" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740223c51853f3145fe7c90360d2d4232f2b62e3449489c207eccde818979982" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" -dependencies = [ - "libc", -] - -[[package]] -name = "simple_asn1" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692ca13de57ce0613a363c8c2f1de925adebc81b04c923ac60c5488bb44abe4b" -dependencies = [ - "chrono", - "num-bigint", - "num-traits", -] - -[[package]] -name = "slab" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c307a32c1c5c437f38c7fd45d753050587732ba8628319fbdf12a7e289ccc590" - -[[package]] -name = "smallvec" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" - -[[package]] -name = "socket2" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "sql-builder" -version = "3.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1008d95d2ec2d062959352527be30e10fec42a1aa5e5a48d990a5ff0fb9bdc0" -dependencies = [ - "anyhow", - "thiserror", -] - -[[package]] -name = "sqlformat" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d86e3c77ff882a828346ba401a7ef4b8e440df804491c6064fe8295765de71c" -dependencies = [ - "lazy_static", - "maplit", - "nom 6.1.2", - "regex", - "unicode_categories", -] - -[[package]] -name = "sqlx" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7911b0031a0247af40095838002999c7a52fba29d9739e93326e71a5a1bc9d43" -dependencies = [ - "sqlx-core", - "sqlx-macros", -] - -[[package]] -name = "sqlx-core" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aec89bfaca8f7737439bad16d52b07f1ccd0730520d3bf6ae9d069fe4b641fb1" -dependencies = [ - "ahash", - "atoi", - "base64 0.13.0", - "bitflags", - "byteorder", - "bytes", - "chrono", - "crc", - "crossbeam-channel", - "crossbeam-queue", - "crossbeam-utils", - "dirs", - "either", - "futures-channel", - "futures-core", - "futures-intrusive", - "futures-util", - "hashlink", - "hex", - "hmac 0.11.0", - "indexmap", - "itoa", - "libc", - "log", - "md-5", - "memchr", - "once_cell", - "parking_lot", - "percent-encoding", - "rand", - "rustls", - "serde", - "serde_json", - "sha-1", - "sha2", - "smallvec", - "sqlformat", - "sqlx-rt", - "stringprep", - "thiserror", - "tokio-stream", - "url", - "uuid", - "webpki", - "webpki-roots", - "whoami", -] - -[[package]] -name = "sqlx-macros" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "584866c833511b1a152e87a7ee20dee2739746f60c858b3c5209150bc4b466f5" -dependencies = [ - "dotenv", - "either", - "heck", - "hex", - "once_cell", - "proc-macro2", - "quote", - "serde", - "serde_json", - "sha2", - "sqlx-core", - "sqlx-rt", - "syn", - "url", -] - -[[package]] -name = "sqlx-rt" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d1bd069de53442e7a320f525a6d4deb8bb0621ac7a55f7eccbc2b58b57f43d0" -dependencies = [ - "actix-rt", - "once_cell", - "tokio", - "tokio-rustls", -] - -[[package]] -name = "standback" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" -dependencies = [ - "version_check", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "stdweb" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" -dependencies = [ - "discard", - "rustc_version 0.2.3", - "stdweb-derive", - "stdweb-internal-macros", - "stdweb-internal-runtime", - "wasm-bindgen", -] - -[[package]] -name = "stdweb-derive" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "serde_derive", - "syn", -] - -[[package]] -name = "stdweb-internal-macros" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" -dependencies = [ - "base-x", - "proc-macro2", - "quote", - "serde", - "serde_derive", - "serde_json", - "sha1", - "syn", -] - -[[package]] -name = "stdweb-internal-runtime" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" - -[[package]] -name = "stringprep" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "strum" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaf86bbcfd1fa9670b7a129f64fc0c9fcbbfe4f1bc4210e9e98fe71ffc12cde2" - -[[package]] -name = "strum_macros" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "syn" -version = "1.0.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "tempfile" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" -dependencies = [ - "cfg-if", - "libc", - "rand", - "redox_syscall 0.2.10", - "remove_dir_all", - "winapi", -] - -[[package]] -name = "termcolor" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "thiserror" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "thread-id" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1" -dependencies = [ - "libc", - "redox_syscall 0.1.57", - "winapi", -] - -[[package]] -name = "thread_local" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" -dependencies = [ - "once_cell", -] - -[[package]] -name = "time" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" -dependencies = [ - "libc", - "wasi", - "winapi", -] - -[[package]] -name = "time" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242" -dependencies = [ - "const_fn", - "libc", - "standback", - "stdweb", - "time-macros", - "version_check", - "winapi", -] - -[[package]] -name = "time" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41effe7cfa8af36f439fac33861b66b049edc6f9a32331e2312660529c1c24ad" -dependencies = [ - "itoa", - "libc", -] - -[[package]] -name = "time-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" -dependencies = [ - "proc-macro-hack", - "time-macros-impl", -] - -[[package]] -name = "time-macros-impl" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" -dependencies = [ - "proc-macro-hack", - "proc-macro2", - "quote", - "standback", - "syn", -] - -[[package]] -name = "tinyvec" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "848a1e1181b9f6753b5e96a092749e29b11d19ede67dfbbd6c7dc7e0f49b5338" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" - -[[package]] -name = "tokio" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cf844b23c6131f624accf65ce0e4e9956a8bb329400ea5bcc26ae3a5c20b0b" -dependencies = [ - "autocfg", - "bytes", - "libc", - "memchr", - "mio", - "num_cpus", - "once_cell", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "tokio-macros", - "winapi", -] - -[[package]] -name = "tokio-macros" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54473be61f4ebe4efd09cec9bd5d16fa51d70ea0192213d754d2d500457db110" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" -dependencies = [ - "rustls", - "tokio", - "webpki", -] - -[[package]] -name = "tokio-stream" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b2f3f698253f03119ac0102beaa64f67a67e08074d03a22d18784104543727f" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "511de3f85caf1c98983545490c3d09685fa8eb634e57eec22bb4db271f46cbd8" -dependencies = [ - "futures-util", - "log", - "pin-project 1.0.8", - "tokio", - "tungstenite", -] - -[[package]] -name = "tokio-util" -version = "0.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1caa0b0c8d94a049db56b5acf8cba99dc0623aab1b26d5b5f5e2d945846b3592" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "log", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "toml" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" -dependencies = [ - "serde", -] - -[[package]] -name = "tower-service" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" - -[[package]] -name = "tracing" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d" -dependencies = [ - "cfg-if", - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-appender" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9965507e507f12c8901432a33e31131222abac31edd90cabbcf85cf544b7127a" -dependencies = [ - "chrono", - "crossbeam-channel", - "tracing-subscriber", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42e6fa53307c8a17e4ccd4dc81cf5ec38db9209f59b222210375b54ee40d1e2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tracing-bunyan-formatter" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c408910c9b7eabc0215fe2b4a89f8ec95581a91cea1f7619f7c78caf14cbc2a1" -dependencies = [ - "chrono", - "gethostname", - "log", - "serde", - "serde_json", - "tracing", - "tracing-core", - "tracing-log", - "tracing-subscriber", -] - -[[package]] -name = "tracing-core" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ca517f43f0fb96e0c3072ed5c275fe5eece87e8cb52f4a77b69226d3b1c9df8" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project 1.0.8", - "tracing", -] - -[[package]] -name = "tracing-log" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6923477a48e41c1951f1999ef8bb5a3023eb723ceadafe78ffb65dc366761e3" -dependencies = [ - "lazy_static", - "log", - "tracing-core", -] - -[[package]] -name = "tracing-serde" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b" -dependencies = [ - "serde", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9cbe87a2fa7e35900ce5de20220a582a9483a7063811defce79d7cbd59d4cfe" -dependencies = [ - "ansi_term", - "chrono", - "lazy_static", - "matchers", - "regex", - "serde", - "serde_json", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", - "tracing-serde", -] - -[[package]] -name = "try-lock" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" - -[[package]] -name = "tungstenite" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0b2d8558abd2e276b0a8df5c05a2ec762609344191e5fd23e292c910e9165b5" -dependencies = [ - "base64 0.13.0", - "byteorder", - "bytes", - "http", - "httparse", - "log", - "rand", - "sha-1", - "thiserror", - "url", - "utf-8", -] - -[[package]] -name = "typenum" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" - -[[package]] -name = "ucd-trie" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" - -[[package]] -name = "unicode-bidi" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "246f4c42e67e7a4e3c6106ff716a5d067d4132a642840b242e357e468a2a0085" - -[[package]] -name = "unicode-normalization" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-segmentation" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" - -[[package]] -name = "unicode-xid" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - -[[package]] -name = "unicode_categories" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" - -[[package]] -name = "universal-hash" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - -[[package]] -name = "url" -version = "2.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" -dependencies = [ - "form_urlencoded", - "idna", - "matches", - "percent-encoding", -] - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "uuid" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" -dependencies = [ - "getrandom", - "serde", -] - -[[package]] -name = "validator" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d6937c33ec6039d8071bcf72933146b5bbe378d645d8fa59bdadabfc2a249" -dependencies = [ - "idna", - "lazy_static", - "regex", - "serde", - "serde_derive", - "serde_json", - "url", - "validator_types", -] - -[[package]] -name = "validator_types" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad9680608df133af2c1ddd5eaf1ddce91d60d61b6bc51494ef326458365a470a" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" - -[[package]] -name = "want" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" -dependencies = [ - "log", - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - -[[package]] -name = "wasm-bindgen" -version = "0.2.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b608ecc8f4198fe8680e2ed18eccab5f0cd4caaf3d83516fa5fb2e927fda2586" -dependencies = [ - "cfg-if", - "serde", - "serde_json", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "580aa3a91a63d23aac5b6b267e2d13cb4f363e31dce6c352fca4752ae12e479f" -dependencies = [ - "bumpalo", - "lazy_static", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16646b21c3add8e13fdb8f20172f8a28c3dbf62f45406bcff0233188226cfe0c" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "171ebf0ed9e1458810dfcb31f2e766ad6b3a89dbda42d8901f2b268277e5f09c" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c2657dd393f03aa2a659c25c6ae18a13a4048cebd220e147933ea837efc589f" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e0c4a743a309662d45f4ede961d7afa4ba4131a59a639f29b0069c3798bbcc2" - -[[package]] -name = "web-sys" -version = "0.3.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01c70a82d842c9979078c772d4a1344685045f1a5628f677c2b2eab4dd7d2696" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki" -version = "0.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "webpki-roots" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" -dependencies = [ - "webpki", -] - -[[package]] -name = "whoami" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7741161a40200a867c96dfa5574544efa4178cf4c8f770b62dd1cc0362d7ae1" -dependencies = [ - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "winreg" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" -dependencies = [ - "winapi", -] - -[[package]] -name = "wyz" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" - -[[package]] -name = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] - -[[package]] -name = "zstd" -version = "0.7.0+zstd.1.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9428752481d8372e15b1bf779ea518a179ad6c771cca2d2c60e4fbff3cc2cd52" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "3.1.0+zstd.1.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa1926623ad7fe406e090555387daf73db555b948134b4d73eac5eb08fb666d" -dependencies = [ - "libc", - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "1.5.0+zstd.1.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e6c094340240369025fc6b731b054ee2a834328fa584310ac96aa4baebdc465" -dependencies = [ - "cc", - "libc", -] diff --git a/backend/Cargo.toml b/backend/Cargo.toml deleted file mode 100644 index f16966df15..0000000000 --- a/backend/Cargo.toml +++ /dev/null @@ -1,109 +0,0 @@ -[package] -name = "backend" -version = "0.1.0" -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -actix = "0.12" -#actix-web = "3" -#actix-http = "2.2.1" -#actix-web-actors = "3" -actix-codec = "0.4" -actix-web = "4.0.0-beta.11" -actix-http = "3.0.0-beta.12" -actix-rt = "2" -actix-web-actors = { version = "4.0.0-beta.7" } -actix-service = "2.0.1" -actix-identity = "0.4.0-beta.3" -actix-cors = "0.6.0-beta.3" - -futures = "0.3.15" -bytes = "1" -toml = "0.5.8" -dashmap = "4.0" -log = "0.4.14" -async-trait = "0.1.52" - -# tracing -tracing = { version = "0.1", features = ["log"] } -tracing-futures = "0.2.4" -tracing-subscriber = { version = "0.2.12", features = ["registry", "env-filter", "ansi", "json"] } -tracing-bunyan-formatter = "0.2.2" -tracing-appender = "0.1" -tracing-core = "0.1" -tracing-log = { version = "0.1.1"} - - -# serde -serde_json = "1.0" -serde = { version = "1.0", features = ["derive"] } -serde_repr = "0.1" -serde-aux = "1.0.1" - -derive_more = {version = "0.99"} -protobuf = {version = "2.20.0"} -uuid = { version = "0.8", features = ["serde", "v4"] } -config = { version = "0.10.1", default-features = false, features = ["yaml"] } -chrono = { version = "0.4", features = ["serde"] } -anyhow = "1.0.40" -thiserror = "1.0.24" -bcrypt = "0.10" -jsonwebtoken = "7.2" -sql-builder = "3.1.1" -lazy_static = "1.4" -tokio = { version = "1", features = ["full"] } -parking_lot = "0.11" -md5 = "0.7.0" -futures-core = { version = "0.3", default-features = false } -pin-project = "1.0.0" -byteorder = {version = "1.3.4"} -async-stream = "0.3.2" - -flowy-user-data-model = { path = "../shared-lib/flowy-user-data-model" } -flowy-folder-data-model = { path = "../shared-lib/flowy-folder-data-model" } -flowy-collaboration = { path = "../shared-lib/flowy-collaboration" } -lib-ws = { path = "../shared-lib/lib-ws" } -lib-ot = { path = "../shared-lib/lib-ot" } -lib-infra = { path = "../shared-lib/lib-infra" } -backend-service = { path = "../shared-lib/backend-service", features = ["http_server"] } - -ormx = { version = "0.7", features = ["postgres"]} -[dependencies.sqlx] -version = "0.5.7" -default-features = false -features = [ - "runtime-actix-rustls", - "macros", - "postgres", - "uuid", - "chrono", - "migrate", - "offline", -] - - -[lib] -path = "src/lib.rs" - -[[bin]] -name = "backend" -path = "src/main.rs" - -[features] -flowy_test = [] -ignore_auth = [] - -[dev-dependencies] -parking_lot = "0.11" -once_cell = "1.7.2" -linkify = "0.5.0" -futures-util = "0.3.15" -backend = { path = ".", features = ["flowy_test"]} -flowy-sdk = { path = "../frontend/rust-lib/flowy-sdk", features = ["http_server"] } -flowy-user = { path = "../frontend/rust-lib/flowy-user", features = ["http_server"] } -flowy-document = { path = "../frontend/rust-lib/flowy-document", features = ["flowy_unit_test", "http_server"] } -flowy-test = { path = "../frontend/rust-lib/flowy-test" } -flowy-net = { path = "../frontend/rust-lib/flowy-net", features = ["http_server"] } - diff --git a/backend/Dockerfile b/backend/Dockerfile deleted file mode 100644 index 38c62d794d..0000000000 --- a/backend/Dockerfile +++ /dev/null @@ -1,23 +0,0 @@ -FROM rust:1.56.1 as builder -WORKDIR /app - -COPY . . -WORKDIR /app/backend -ENV SQLX_OFFLINE true -RUN RUSTFLAGS="-C opt-level=2" cargo build --release --bin backend -# Size optimization -#RUN strip ./target/release/backend - -FROM debian:bullseye-slim AS runtime -WORKDIR /app -RUN apt-get update -y \ - && apt-get install -y --no-install-recommends openssl \ - # Clean up - && apt-get autoremove -y \ - && apt-get clean -y \ - && rm -rf /var/lib/apt/lists/* - -COPY --from=builder /app/backend/target/release/backend /usr/local/bin/backend -COPY --from=builder /app/backend/configuration configuration -ENV APP_ENVIRONMENT production -CMD ["backend"] diff --git a/backend/Makefile b/backend/Makefile deleted file mode 100644 index e5e7138e22..0000000000 --- a/backend/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -ROOT = "./scripts" -SEMVER_VERSION=$(shell grep version Cargo.toml | awk -F"\"" '{print $$2}' | head -n 1) - -.PHONY: init_database run_docker run_test - -init_database: - POSTGRES_PORT=5433 ${ROOT}/init_database.sh - -docker_image: - source $(ROOT)/docker_env.sh && docker-compose up -d db - source $(ROOT)/docker_env.sh && docker-compose up -d backend - -local_server: - cargo run - -docker_test: - sh $(ROOT)/docker_test.sh - -local_test: - # 🔥 Must run init_database first - SQLX_OFFLINE=true cargo test - - diff --git a/backend/configuration/base.yaml b/backend/configuration/base.yaml deleted file mode 100644 index 4b78eb1b42..0000000000 --- a/backend/configuration/base.yaml +++ /dev/null @@ -1,9 +0,0 @@ -application: - port: 8000 - host: 0.0.0.0 -database: - host: "localhost" - port: 5433 - username: "postgres" - password: "password" - database_name: "flowy" diff --git a/backend/configuration/local.yaml b/backend/configuration/local.yaml deleted file mode 100644 index d7f541e28d..0000000000 --- a/backend/configuration/local.yaml +++ /dev/null @@ -1,5 +0,0 @@ -application: - host: 127.0.0.1 - base_url: "http://127.0.0.1" -database: - require_ssl: false diff --git a/backend/configuration/production.yaml b/backend/configuration/production.yaml deleted file mode 100644 index 936886c05e..0000000000 --- a/backend/configuration/production.yaml +++ /dev/null @@ -1,6 +0,0 @@ -application: - host: 0.0.0.0 -database: - host: "db" - port: 5432 - require_ssl: false diff --git a/backend/doc/database_setup.md b/backend/doc/database_setup.md deleted file mode 100644 index 3a89974cc9..0000000000 --- a/backend/doc/database_setup.md +++ /dev/null @@ -1,70 +0,0 @@ - - - -### Docker - -1. follow the [instructions](https://docs.docker.com/desktop/mac/install/) to install docker. -2. open terminal and run: `docker pull postgres` -3run `make init_database`. It will create the database scheme on remote specified by DATABASE_URL. You can connect you database using -pgAdmin. - -![img_2.png](img_2.png) - -The information you enter must be the same as the `make init_postgres`. e.g. -``` -export DB_USER=postgres -export DB_PASSWORD=password -export DB_NAME=flowy -export DB_PORT=5432 -``` - -![img_1.png](img_1.png) - -[Docker command](https://docs.docker.com/engine/reference/commandline/builder_prune/) - -### Run -By default, Docker images do not expose their ports to the underlying host machine. We need to do it explicitly using the -p flag. -`docker run -p 8000:8000 backend` - - -### Sqlx - -**sqlx-cli** -* [install sqlx-cli](https://github.com/launchbadge/sqlx/tree/master/sqlx-cli) - -**Sqlx and Diesel commands** -* create migration - * sqlx: sqlx migrate add $(table) - * diesel: diesel migration generation $(table) - -* run migration - * sqlx: sqlx migrate run - * diesel: diesel migration run - -* reset database - * sqlx: sqlx database reset - * diesel: diesel database reset - -**offline mode** - -`cargo sqlx prepare -- --bin backend` - -**Type mapping** -* [postgres type map](https://docs.rs/sqlx/0.5.7/sqlx/postgres/types/index.html) -* [postgres and diesel type map](https://kotiri.com/2018/01/31/postgresql-diesel-rust-types.html) - - -## Q&A -1. Receive` { code: 24, kind: Other, message: "Too many open files" } on arbiter` after running cargo test on backend. -> This is due to a limit enforced by the operating system on the maximum number of open file descriptors (including sockets) for each process. -> Raising the file descriptor limit using `ulimit -n 2048` to solve this issue. It won't stay after reboot so check on google how to persist -> that value if you want to. -> -> or you can try: -> `launchctl limit maxfiles 2048 2048` -> `launchctl limit maxfiles` -> -> Don't forget to relaunch your terminal. - -## More -* [11-database-drivers](https://blog.logrocket.com/11-database-drivers-and-orms-for-rust-that-are-ready-for-production/) diff --git a/backend/doc/database_struct.md b/backend/doc/database_struct.md deleted file mode 100644 index 6a8ef645c1..0000000000 --- a/backend/doc/database_struct.md +++ /dev/null @@ -1,200 +0,0 @@ - -# Table Struct - -## Table: user_table - -- `Name`: UserTable -- `Comment`: UserTable - -### `Primary Key` - -- `Columns`: id - -### `Indexes[]` - -| `Columns` | `Unique` | -| --------- | -------- | -| email | `true` | - -### `Foreign Keys[]` - -| `Columns` | `Ref Table` | `Ref Columns` | `Options` | -| --------- | ----------- | ------------- | --------- | - - -### `Columns[]` - -| `Label` | `Name` | `Type` | `Nullable` | `Default` | `Comment` | -| ----------- | ----------- | ----------- | ---------- | --------- | --------- | -| id | id | uuid | `false` | | | -| email | email | text | `false` | | | -| name | name | text | `false` | | | -| password | password | text | `false` | | | -| create_time | create_time | timestamptz | `false` | | | - - -## Table: workspace_table - -- `Name`: WorkspaceTable -- `Comment`: WorkspaceTable - -### `Primary Key` - -- `Columns`: id - -### `Indexes[]` - -| `Columns` | `Unique` | -| --------- | -------- | - -### `Foreign Keys[]` - -| `Columns` | `Ref Table` | `Ref Columns` | `Options` | -| --------- | ----------- | ------------- | --------- | -| user_id | user_table | id | | - -### `Columns[]` - -| `Label` | `Name` | `Type` | `Nullable` | `Default` | `Comment` | -| ------------- | ------------- | ----------- | ---------- | --------- | --------- | -| id | id | uuid | `false` | | | -| user_id | user_id | text | `false` | | | -| name | name | text | `false` | | | -| description | description | text | `false` | | | -| create_time | create_time | timestamptz | `false` | | | -| modified_time | modified_time | timestamptz | `false` | | | - - -## Table: app_table - -- `Name`: AppTable -- `Comment`: AppTable - -### `Primary Key` - -- `Columns`: id - -### `Indexes[]` - -| `Columns` | `Unique` | -| --------- | -------- | - -### `Foreign Keys[]` - -| `Columns` | `Ref Table` | `Ref Columns` | `Options` | -| ------------ | --------------- | ------------- | --------- | -| user_id | user_table | id | | -| workspace_id | workspace_table | id | | -| last_view_id | view_table | id | | - -### `Columns[]` - -| `Label` | `Name` | `Type` | `Nullable` | `Default` | `Comment` | -| ------------- | ------------- | ----------- | ---------- | --------- | --------- | -| id | id | uuid | `false` | | | -| user_id | user_id | text | `false` | | | -| workspace_id | workspace_id | text | `false` | | | -| last_view_id | workspace_id | text | `false` | | | -| name | name | text | `false` | | | -| description | description | text | `false` | | | -| color_style | color_style | text | `false` | | | -| is_trash | is_trash | bool | `false` | `false` | | -| create_time | create_time | timestamptz | `false` | | | -| modified_time | modified_time | timestamptz | `false` | | | - - -## Table: view_table - -- `Name`: ViewTable -- `Comment`: ViewTable - -### `Primary Key` - -- `Columns`: id - -### `Indexes[]` - -| `Columns` | `Unique` | -| --------- | -------- | - -### `Foreign Keys[]` - -| `Columns` | `Ref Table` | `Ref Columns` | `Options` | -| ------------ | ----------- | ------------- | --------- | -| user_id | user_table | id | | -| belong_to_id | app_table | id | | - -### `Columns[]` - -| `Label` | `Name` | `Type` | `Nullable` | `Default` | `Comment` | -| ------------- | ------------- | ----------- | ---------- | --------- | --------- | -| id | id | uuid | `false` | | | -| belong_to_id | belong_to_id | text | `false` | | | -| name | name | text | `false` | | | -| description | description | text | `false` | | | -| thumbnail | thumbnail | text | `false` | | | -| view_type | view_type | int | `false` | | | -| create_time | create_time | timestamptz | `false` | | | -| modified_time | modified_time | timestamptz | `false` | | | - - -## Table: doc_table - -- `Name`: DocTable -- `Comment`: DocTable - -### `Primary Key` - -- `Columns`: id - -### `Indexes[]` - -| `Columns` | `Unique` | -| --------- | -------- | - -### `Foreign Keys[]` - -| `Columns` | `Ref Table` | `Ref Columns` | `Options` | -| --------- | ----------- | ------------- | --------- | -| rev_id | doc_table | id | | - - - -### `Columns[]` - -| `Label` | `Name` | `Type` | `Nullable` | `Default` | `Comment` | -| ------- | ------ | ------ | ---------- | --------- | --------- | -| id | id | uuid | `false` | | | -| rev_id | rev_id | text | `false` | | | -| data | data | text | `false` | | | - - -## Table: trash_table - -- `Name`: TrashTable -- `Comment`: TrashTable - -### `Primary Key` - -- `Columns`: id - -### `Indexes[]` - -| `Columns` | `Unique` | -| --------- | -------- | - -### `Foreign Keys[]` - -| `Columns` | `Ref Table` | `Ref Columns` | `Options` | -| --------- | ----------- | ------------- | --------- | -| user_id | user_table | id | | - - -### `Columns[]` - -| `Label` | `Name` | `Type` | `Nullable` | `Default` | `Comment` | -| ------- | ------- | ------ | ---------- | --------- | --------- | -| id | id | uuid | `false` | | | -| user_id | user_id | text | `false` | | | -| ty | ty | int4 | `false` | 0 | | - diff --git a/backend/doc/img_1.png b/backend/doc/img_1.png deleted file mode 100644 index 64fcbddfd9..0000000000 Binary files a/backend/doc/img_1.png and /dev/null differ diff --git a/backend/doc/img_2.png b/backend/doc/img_2.png deleted file mode 100644 index 1a9ddc53a2..0000000000 Binary files a/backend/doc/img_2.png and /dev/null differ diff --git a/backend/docker-compose.yml b/backend/docker-compose.yml deleted file mode 100644 index a24d1cdc53..0000000000 --- a/backend/docker-compose.yml +++ /dev/null @@ -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 diff --git a/backend/migrations/20210819065837_user.sql b/backend/migrations/20210819065837_user.sql deleted file mode 100644 index 17f7998435..0000000000 --- a/backend/migrations/20210819065837_user.sql +++ /dev/null @@ -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 -); \ No newline at end of file diff --git a/backend/migrations/20210824033032_workspace.sql b/backend/migrations/20210824033032_workspace.sql deleted file mode 100644 index 24d86432a2..0000000000 --- a/backend/migrations/20210824033032_workspace.sql +++ /dev/null @@ -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 -); \ No newline at end of file diff --git a/backend/migrations/20210824033742_app.sql b/backend/migrations/20210824033742_app.sql deleted file mode 100644 index 29a80618b0..0000000000 --- a/backend/migrations/20210824033742_app.sql +++ /dev/null @@ -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 -); \ No newline at end of file diff --git a/backend/migrations/20210824033748_view.sql b/backend/migrations/20210824033748_view.sql deleted file mode 100644 index a3e532e2a9..0000000000 --- a/backend/migrations/20210824033748_view.sql +++ /dev/null @@ -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 -); \ No newline at end of file diff --git a/backend/migrations/20210909115140_doc.sql b/backend/migrations/20210909115140_doc.sql deleted file mode 100644 index 40782f9875..0000000000 --- a/backend/migrations/20210909115140_doc.sql +++ /dev/null @@ -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 -); \ No newline at end of file diff --git a/backend/migrations/20211015065001_trash.sql b/backend/migrations/20211015065001_trash.sql deleted file mode 100644 index 65fe72bc9b..0000000000 --- a/backend/migrations/20211015065001_trash.sql +++ /dev/null @@ -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 -); \ No newline at end of file diff --git a/backend/migrations/20211221061753_kv.sql b/backend/migrations/20211221061753_kv.sql deleted file mode 100644 index 833353c43e..0000000000 --- a/backend/migrations/20211221061753_kv.sql +++ /dev/null @@ -1,6 +0,0 @@ --- Add migration script here -CREATE TABLE IF NOT EXISTS kv_table( - id TEXT NOT NULL, - PRIMARY KEY (id), - blob bytea -); \ No newline at end of file diff --git a/backend/rust-toolchain.toml b/backend/rust-toolchain.toml deleted file mode 100644 index 48a0631f2e..0000000000 --- a/backend/rust-toolchain.toml +++ /dev/null @@ -1,2 +0,0 @@ -[toolchain] -channel = "stable-2022-01-20" \ No newline at end of file diff --git a/backend/rustfmt.toml b/backend/rustfmt.toml deleted file mode 100644 index 2464575494..0000000000 --- a/backend/rustfmt.toml +++ /dev/null @@ -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" diff --git a/backend/scripts/docker_env.sh b/backend/scripts/docker_env.sh deleted file mode 100644 index 4bd668745e..0000000000 --- a/backend/scripts/docker_env.sh +++ /dev/null @@ -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} \ No newline at end of file diff --git a/backend/scripts/docker_test.sh b/backend/scripts/docker_test.sh deleted file mode 100644 index 38b23db263..0000000000 --- a/backend/scripts/docker_test.sh +++ /dev/null @@ -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"}' \ No newline at end of file diff --git a/backend/scripts/init_database.sh b/backend/scripts/init_database.sh deleted file mode 100755 index 95c5c421f2..0000000000 --- a/backend/scripts/init_database.sh +++ /dev/null @@ -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!" - diff --git a/backend/sqlx-data.json b/backend/sqlx-data.json deleted file mode 100644 index 12665e96ba..0000000000 --- a/backend/sqlx-data.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "db": "PostgreSQL", - "e8c487b4314c267f6da2667b95f6c8003fabc2461c10df2d6d39d081e74e167f": { - "query": "\n INSERT INTO user_table (id, email, name, create_time, password)\n VALUES ($1, $2, $3, $4, $5)\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Text", - "Text", - "Timestamptz", - "Text" - ] - }, - "nullable": [] - } - } -} \ No newline at end of file diff --git a/backend/src/application.rs b/backend/src/application.rs deleted file mode 100644 index 5092f22751..0000000000 --- a/backend/src/application.rs +++ /dev/null @@ -1,166 +0,0 @@ -use actix::Actor; -use actix_identity::{CookieIdentityPolicy, IdentityService}; -use actix_web::{dev::Server, middleware, web, web::Data, App, HttpServer, Scope}; -use sqlx::{postgres::PgPoolOptions, PgPool}; -use std::{net::TcpListener, time::Duration}; -use tokio::time::interval; - -use crate::{ - config::{ - env::{domain, secret, use_https}, - DatabaseSettings, Settings, - }, - context::AppContext, - services::{ - document::router as doc, - folder::{app::router as app, trash::router as trash, view::router as view, workspace::router as workspace}, - user::router as user, - web_socket::WSServer, - }, -}; - -pub struct Application { - port: u16, - server: Server, -} - -impl Application { - pub async fn build(configuration: Settings, app_ctx: AppContext) -> Result { - let address = format!("{}:{}", configuration.application.host, configuration.application.port); - let listener = TcpListener::bind(&address)?; - let port = listener.local_addr().unwrap().port(); - let server = run(listener, app_ctx)?; - Ok(Self { port, server }) - } - - pub async fn run_until_stopped(self) -> Result<(), std::io::Error> { - self.server.await - } - - pub fn port(&self) -> u16 { - self.port - } -} - -pub fn run(listener: TcpListener, app_ctx: AppContext) -> Result { - let domain = domain(); - let secret: String = secret(); - actix_rt::spawn(period_check(app_ctx.persistence.pg_pool())); - - let server = HttpServer::new(move || { - App::new() - .wrap(middleware::Logger::default()) - .wrap(identify_service(&domain, &secret)) - .wrap(crate::middleware::default_cors()) - .wrap(crate::middleware::AuthenticationService) - .app_data(web::JsonConfig::default().limit(4096)) - .service(ws_scope()) - .service(user_scope()) - .app_data(app_ctx.ws_server.clone()) - .app_data(app_ctx.persistence.clone()) - .app_data(Data::new(app_ctx.persistence.pg_pool())) - .app_data(app_ctx.ws_receivers.clone()) - .app_data(app_ctx.document_manager.clone()) - }) - .listen(listener)? - .run(); - Ok(server) -} - -#[allow(dead_code)] -async fn period_check(_pool: PgPool) { - let mut i = interval(Duration::from_secs(60)); - loop { - i.tick().await; - } -} - -fn ws_scope() -> Scope { - web::scope("/ws").service(crate::services::web_socket::router::establish_ws_connection) -} - -fn user_scope() -> Scope { - // https://developer.mozilla.org/en-US/docs/Web/HTTP - // TODO: replace GET body with query params - web::scope("/api") - // authentication - .service( - web::resource("/auth") - .route(web::post().to(user::sign_in_handler)) - .route(web::delete().to(user::sign_out_handler)), - ) - .service( - web::resource("/user") - .route(web::patch().to(user::set_user_profile_handler)) - .route(web::get().to(user::get_user_profile_handler)), - ) - .service(web::resource("/register").route(web::post().to(user::register_handler))) - .service( - web::resource("/workspace") - .route(web::post().to(workspace::create_handler)) - .route(web::delete().to(workspace::delete_handler)) - .route(web::get().to(workspace::read_handler)) - .route(web::patch().to(workspace::update_handler)), - ) - .service(web::resource("/workspace_list/{user_id}").route(web::get().to(workspace::workspace_list))) - .service( - web::resource("/app") - .route(web::post().to(app::create_handler)) - .route(web::get().to(app::read_handler)) - .route(web::delete().to(app::delete_handler)) - .route(web::patch().to(app::update_handler)), - ) - .service( - web::resource("/view") - .route(web::post().to(view::create_handler)) - .route(web::delete().to(view::delete_handler)) - .route(web::get().to(view::read_handler)) - .route(web::patch().to(view::update_handler)), - ) - .service( - web::resource("/doc") - .route(web::post().to(doc::create_document_handler)) - .route(web::get().to(doc::read_document_handler)) - .route(web::patch().to(doc::reset_document_handler)), - ) - .service( - web::resource("/trash") - .route(web::post().to(trash::create_handler)) - .route(web::delete().to(trash::delete_handler)) - .route(web::get().to(trash::read_handler)), - ) - .service(web::resource("/sync").route(web::post().to(trash::create_handler))) - // password - .service(web::resource("/password_change").route(web::post().to(user::change_password))) -} - -pub async fn init_app_context(configuration: &Settings) -> AppContext { - let level = std::env::var("RUST_LOG").unwrap_or_else(|_| "info".to_owned()); - let _ = crate::services::log::Builder::new("flowy-server") - .env_filter(&level) - .build(); - let pg_pool = get_connection_pool(&configuration.database) - .await - .unwrap_or_else(|_| panic!("Failed to connect to Postgres at {:?}.", configuration.database)); - - let ws_server = WSServer::new().start(); - AppContext::new(ws_server, pg_pool) -} - -pub fn identify_service(domain: &str, secret: &str) -> IdentityService { - IdentityService::new( - CookieIdentityPolicy::new(secret.as_bytes()) - .name("auth") - .path("/") - .domain(domain) - .max_age_secs(24 * 3600) - .secure(use_https()), - ) -} - -pub async fn get_connection_pool(configuration: &DatabaseSettings) -> Result { - PgPoolOptions::new() - .connect_timeout(std::time::Duration::from_secs(5)) - .connect_with(configuration.with_db()) - .await -} diff --git a/backend/src/config/configuration.rs b/backend/src/config/configuration.rs deleted file mode 100644 index d18522a2dd..0000000000 --- a/backend/src/config/configuration.rs +++ /dev/null @@ -1,106 +0,0 @@ -use serde_aux::field_attributes::deserialize_number_from_string; -use sqlx::postgres::{PgConnectOptions, PgSslMode}; -use std::convert::{TryFrom, TryInto}; - -#[derive(serde::Deserialize, Clone, Debug)] -pub struct Settings { - pub database: DatabaseSettings, - pub application: ApplicationSettings, -} - -// We are using 127.0.0.1 as our host in address, we are instructing our -// application to only accept connections coming from the same machine. However, -// request from the hose machine which is not seen as local by our Docker image. -// -// Using 0.0.0.0 as host to instruct our application to accept connections from -// any network interface. So using 127.0.0.1 for our local development and set -// it to 0.0.0.0 in our Docker images. -// -#[derive(serde::Deserialize, Clone, Debug)] -pub struct ApplicationSettings { - #[serde(deserialize_with = "deserialize_number_from_string")] - pub port: u16, - pub host: String, -} - -#[derive(serde::Deserialize, Clone, Debug)] -pub struct DatabaseSettings { - pub username: String, - pub password: String, - #[serde(deserialize_with = "deserialize_number_from_string")] - pub port: u16, - pub host: String, - pub database_name: String, - pub require_ssl: bool, -} - -impl DatabaseSettings { - pub fn without_db(&self) -> PgConnectOptions { - let ssl_mode = if self.require_ssl { - PgSslMode::Require - } else { - PgSslMode::Prefer - }; - PgConnectOptions::new() - .host(&self.host) - .username(&self.username) - .password(&self.password) - .port(self.port) - .ssl_mode(ssl_mode) - } - - pub fn with_db(&self) -> PgConnectOptions { - self.without_db().database(&self.database_name) - } -} - -pub fn get_configuration() -> Result { - let mut settings = config::Config::default(); - let base_path = std::env::current_dir().expect("Failed to determine the current directory"); - let configuration_dir = base_path.join("configuration"); - settings.merge(config::File::from(configuration_dir.join("base")).required(true))?; - - let environment: Environment = std::env::var("APP_ENVIRONMENT") - .unwrap_or_else(|_| "local".into()) - .try_into() - .expect("Failed to parse APP_ENVIRONMENT."); - - settings.merge(config::File::from(configuration_dir.join(environment.as_str())).required(true))?; - - // Add in settings from environment variables (with a prefix of APP and '__' as - // separator) E.g. `APP_APPLICATION__PORT=5001 would set - // `Settings.application.port` - settings.merge(config::Environment::with_prefix("app").separator("__"))?; - - settings.try_into() -} - -/// The possible runtime environment for our application. -pub enum Environment { - Local, - Production, -} - -impl Environment { - pub fn as_str(&self) -> &'static str { - match self { - Environment::Local => "local", - Environment::Production => "production", - } - } -} - -impl TryFrom for Environment { - type Error = String; - - fn try_from(s: String) -> Result { - match s.to_lowercase().as_str() { - "local" => Ok(Self::Local), - "production" => Ok(Self::Production), - other => Err(format!( - "{} is not a supported environment. Use either `local` or `production`.", - other - )), - } - } -} diff --git a/backend/src/config/const_define.rs b/backend/src/config/const_define.rs deleted file mode 100644 index 33e81e22a0..0000000000 --- a/backend/src/config/const_define.rs +++ /dev/null @@ -1,7 +0,0 @@ -use std::time::Duration; - -pub const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(8); -pub const PING_TIMEOUT: Duration = Duration::from_secs(60); -pub const MAX_PAYLOAD_SIZE: usize = 262_144; // max payload size is 256k - -pub const IGNORE_ROUTES: [&str; 3] = ["/api/register", "/api/auth", "/ws"]; diff --git a/backend/src/config/env.rs b/backend/src/config/env.rs deleted file mode 100644 index b4e1c2e376..0000000000 --- a/backend/src/config/env.rs +++ /dev/null @@ -1,17 +0,0 @@ -use std::env; - -pub fn domain() -> String { - env::var("DOMAIN").unwrap_or_else(|_| "localhost".to_string()) -} - -pub fn jwt_secret() -> String { - env::var("JWT_SECRET").unwrap_or_else(|_| "my secret".into()) -} - -pub fn secret() -> String { - env::var("SECRET_KEY").unwrap_or_else(|_| "0123".repeat(8)) -} - -pub fn use_https() -> bool { - false -} diff --git a/backend/src/config/mod.rs b/backend/src/config/mod.rs deleted file mode 100644 index af27ede86d..0000000000 --- a/backend/src/config/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -mod configuration; -mod const_define; -pub mod env; - -pub use configuration::*; -pub use const_define::*; diff --git a/backend/src/context.rs b/backend/src/context.rs deleted file mode 100644 index d3b80af564..0000000000 --- a/backend/src/context.rs +++ /dev/null @@ -1,92 +0,0 @@ -use crate::services::{ - kv::PostgresKV, - web_socket::{WSServer, WebSocketReceivers}, -}; -use actix::Addr; -use actix_web::web::Data; - -use crate::services::{ - document::ws_receiver::{make_document_ws_receiver, HttpDocumentCloudPersistence}, - folder::ws_receiver::{make_folder_ws_receiver, HttpFolderCloudPersistence}, - kv::revision_kv::RevisionKVPersistence, -}; -use flowy_collaboration::{server_document::ServerDocumentManager, server_folder::ServerFolderManager}; -use lib_ws::WSChannel; -use sqlx::PgPool; -use std::sync::Arc; - -#[derive(Clone)] -pub struct AppContext { - pub ws_server: Data>, - pub persistence: Data>, - pub ws_receivers: Data, - pub document_manager: Data>, - pub folder_manager: Data>, -} - -impl AppContext { - pub fn new(ws_server: Addr, pg_pool: PgPool) -> Self { - let ws_server = Data::new(ws_server); - let mut ws_receivers = WebSocketReceivers::new(); - - let document_store = make_document_kv_store(pg_pool.clone()); - let folder_store = make_folder_kv_store(pg_pool.clone()); - let flowy_persistence = Arc::new(FlowyPersistence { - pg_pool, - document_store, - folder_store, - }); - - let document_persistence = Arc::new(HttpDocumentCloudPersistence(flowy_persistence.document_kv_store())); - let document_manager = Arc::new(ServerDocumentManager::new(document_persistence)); - let document_ws_receiver = make_document_ws_receiver(flowy_persistence.clone(), document_manager.clone()); - ws_receivers.set(WSChannel::Document, document_ws_receiver); - - let folder_persistence = Arc::new(HttpFolderCloudPersistence(flowy_persistence.folder_kv_store())); - let folder_manager = Arc::new(ServerFolderManager::new(folder_persistence)); - let folder_ws_receiver = make_folder_ws_receiver(flowy_persistence.clone(), folder_manager.clone()); - ws_receivers.set(WSChannel::Folder, folder_ws_receiver); - - AppContext { - ws_server, - persistence: Data::new(flowy_persistence), - ws_receivers: Data::new(ws_receivers), - document_manager: Data::new(document_manager), - folder_manager: Data::new(folder_manager), - } - } -} - -pub type DocumentRevisionKV = RevisionKVPersistence; -pub type FolderRevisionKV = RevisionKVPersistence; - -fn make_document_kv_store(pg_pool: PgPool) -> Arc { - let kv_impl = Arc::new(PostgresKV { pg_pool }); - Arc::new(DocumentRevisionKV::new(kv_impl)) -} - -fn make_folder_kv_store(pg_pool: PgPool) -> Arc { - let kv_impl = Arc::new(PostgresKV { pg_pool }); - Arc::new(FolderRevisionKV::new(kv_impl)) -} - -#[derive(Clone)] -pub struct FlowyPersistence { - pg_pool: PgPool, - document_store: Arc, - folder_store: Arc, -} - -impl FlowyPersistence { - pub fn pg_pool(&self) -> PgPool { - self.pg_pool.clone() - } - - pub fn document_kv_store(&self) -> Arc { - self.document_store.clone() - } - - pub fn folder_kv_store(&self) -> Arc { - self.folder_store.clone() - } -} diff --git a/backend/src/entities/logged_user.rs b/backend/src/entities/logged_user.rs deleted file mode 100644 index 9930afa035..0000000000 --- a/backend/src/entities/logged_user.rs +++ /dev/null @@ -1,118 +0,0 @@ -use crate::entities::token::{Claim, Token}; -use actix_web::http::HeaderValue; -use backend_service::errors::ServerError; -use chrono::{DateTime, Utc}; -use dashmap::DashMap; -use lazy_static::lazy_static; - -lazy_static! { - pub static ref AUTHORIZED_USERS: AuthorizedUsers = AuthorizedUsers::new(); -} - -#[derive(Debug, PartialEq, Eq, Hash, Clone)] -pub struct LoggedUser { - pub user_id: String, -} - -impl std::convert::From for LoggedUser { - fn from(c: Claim) -> Self { - Self { user_id: c.user_id() } - } -} - -impl LoggedUser { - pub fn new(user_id: &str) -> Self { - Self { - user_id: user_id.to_owned(), - } - } - - pub fn from_token(token: String) -> Result { - let user: LoggedUser = Token::decode_token(&token.into())?.into(); - Ok(user) - } - - pub fn as_uuid(&self) -> Result { - let id = uuid::Uuid::parse_str(&self.user_id)?; - Ok(id) - } -} - -use actix_web::{dev::Payload, FromRequest, HttpRequest}; - -use futures::future::{ready, Ready}; - -impl FromRequest for LoggedUser { - type Error = ServerError; - type Future = Ready>; - - fn from_request(request: &HttpRequest, _payload: &mut Payload) -> Self::Future { - match Token::parser_from_request(request) { - Ok(token) => ready(LoggedUser::from_token(token.0)), - Err(err) => ready(Err(err)), - } - } -} - -impl std::convert::TryFrom<&HeaderValue> for LoggedUser { - type Error = ServerError; - - fn try_from(header: &HeaderValue) -> Result { - match header.to_str() { - Ok(val) => LoggedUser::from_token(val.to_owned()), - Err(e) => { - log::error!("Header to string failed: {:?}", e); - Err(ServerError::unauthorized()) - } - } - } -} - -#[derive(Clone, Debug, Copy)] -enum AuthStatus { - Authorized(DateTime), - NotAuthorized, -} - -pub const EXPIRED_DURATION_DAYS: i64 = 30; - -pub struct AuthorizedUsers(DashMap); -impl std::default::Default for AuthorizedUsers { - fn default() -> Self { - Self(DashMap::new()) - } -} -impl AuthorizedUsers { - pub fn new() -> Self { - AuthorizedUsers::default() - } - - pub fn is_authorized(&self, user: &LoggedUser) -> bool { - match self.0.get(user) { - None => { - tracing::debug!("user not login yet or server was reboot"); - false - } - Some(status) => match *status { - AuthStatus::Authorized(last_time) => { - let current_time = Utc::now(); - let days = (current_time - last_time).num_days(); - days < EXPIRED_DURATION_DAYS - } - AuthStatus::NotAuthorized => { - tracing::debug!("user logout already"); - false - } - }, - } - } - - pub fn store_auth(&self, user: LoggedUser, is_auth: bool) { - let status = if is_auth { - AuthStatus::Authorized(Utc::now()) - } else { - AuthStatus::NotAuthorized - }; - self.0.insert(user, status); - } -} diff --git a/backend/src/entities/mod.rs b/backend/src/entities/mod.rs deleted file mode 100644 index 70409638ca..0000000000 --- a/backend/src/entities/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod logged_user; -pub mod token; -pub mod user; diff --git a/backend/src/entities/token.rs b/backend/src/entities/token.rs deleted file mode 100644 index 2f5630699a..0000000000 --- a/backend/src/entities/token.rs +++ /dev/null @@ -1,94 +0,0 @@ -use crate::{ - config::env::{domain, jwt_secret}, - entities::logged_user::EXPIRED_DURATION_DAYS, -}; -use actix_web::{dev::Payload, FromRequest, HttpRequest}; -use backend_service::{configuration::HEADER_TOKEN, errors::ServerError}; -use chrono::{Duration, Local}; -use derive_more::{From, Into}; -use futures::future::{ready, Ready}; -use jsonwebtoken::{decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation}; -use serde::{Deserialize, Serialize}; - -const DEFAULT_ALGORITHM: Algorithm = Algorithm::HS256; - -#[derive(Debug, Serialize, Deserialize)] -pub struct Claim { - // issuer - iss: String, - // subject - sub: String, - // issue at - iat: i64, - // expiry - exp: i64, - user_id: String, -} - -impl Claim { - pub fn with_user_id(user_id: &str) -> Self { - let domain = domain(); - Self { - iss: domain, - sub: "auth".to_string(), - user_id: user_id.to_string(), - iat: Local::now().timestamp(), - exp: (Local::now() + Duration::days(EXPIRED_DURATION_DAYS)).timestamp(), - } - } - - pub fn user_id(self) -> String { - self.user_id - } -} - -// impl From for User { -// fn from(claim: Claim) -> Self { Self { email: claim.email } } -// } - -#[derive(From, Into, Clone)] -pub struct Token(pub String); -impl Token { - pub fn create_token(user_id: &str) -> Result { - let claims = Claim::with_user_id(user_id); - encode( - &Header::new(DEFAULT_ALGORITHM), - &claims, - &EncodingKey::from_secret(jwt_secret().as_ref()), - ) - .map(Into::into) - .map_err(|err| ServerError::internal().context(err)) - } - - pub fn decode_token(token: &Self) -> Result { - decode::( - &token.0, - &DecodingKey::from_secret(jwt_secret().as_ref()), - &Validation::new(DEFAULT_ALGORITHM), - ) - .map(|data| Ok(data.claims)) - .map_err(|err| ServerError::unauthorized().context(err))? - } - - pub fn parser_from_request(request: &HttpRequest) -> Result { - match request.headers().get(HEADER_TOKEN) { - Some(header) => match header.to_str() { - Ok(val) => Ok(Token(val.to_owned())), - Err(_) => Err(ServerError::unauthorized()), - }, - None => Err(ServerError::unauthorized()), - } - } -} - -impl FromRequest for Token { - type Error = ServerError; - type Future = Ready>; - - fn from_request(request: &HttpRequest, _payload: &mut Payload) -> Self::Future { - match Token::parser_from_request(request) { - Ok(token) => ready(Ok(token)), - Err(err) => ready(Err(err)), - } - } -} diff --git a/backend/src/entities/user.rs b/backend/src/entities/user.rs deleted file mode 100644 index 7a9b27b137..0000000000 --- a/backend/src/entities/user.rs +++ /dev/null @@ -1,13 +0,0 @@ -// type mapped https://kotiri.com/2018/01/31/postgresql-diesel-rust-types.html - -use chrono::Utc; - -#[derive(Debug, Clone, sqlx::FromRow)] -pub struct UserTable { - pub(crate) id: uuid::Uuid, - pub(crate) email: String, - pub(crate) name: String, - #[allow(dead_code)] - pub(crate) create_time: chrono::DateTime, - pub(crate) password: String, -} diff --git a/backend/src/lib.rs b/backend/src/lib.rs deleted file mode 100644 index 57cdcc8ce6..0000000000 --- a/backend/src/lib.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub mod application; -pub mod config; -pub mod context; -mod entities; -pub mod middleware; -pub mod services; -pub mod util; diff --git a/backend/src/main.rs b/backend/src/main.rs deleted file mode 100644 index 06cd255713..0000000000 --- a/backend/src/main.rs +++ /dev/null @@ -1,14 +0,0 @@ -use backend::{ - application::{init_app_context, Application}, - config::get_configuration, -}; - -#[actix_web::main] -async fn main() -> std::io::Result<()> { - let configuration = get_configuration().expect("Failed to read configuration."); - let app_ctx = init_app_context(&configuration).await; - let application = Application::build(configuration, app_ctx).await?; - application.run_until_stopped().await?; - - Ok(()) -} diff --git a/backend/src/middleware/auth_middleware.rs b/backend/src/middleware/auth_middleware.rs deleted file mode 100644 index bd4b5421e3..0000000000 --- a/backend/src/middleware/auth_middleware.rs +++ /dev/null @@ -1,105 +0,0 @@ -use actix_service::{Service, Transform}; -use actix_web::{ - dev::{ServiceRequest, ServiceResponse}, - Error, HttpResponse, ResponseError, -}; - -use crate::{ - config::IGNORE_ROUTES, - entities::logged_user::{LoggedUser, AUTHORIZED_USERS}, -}; -use actix_web::{body::AnyBody, dev::MessageBody}; -use backend_service::{configuration::HEADER_TOKEN, errors::ServerError}; -use futures::future::{ok, LocalBoxFuture, Ready}; -use std::{ - convert::TryInto, - error::Error as StdError, - task::{Context, Poll}, -}; - -pub struct AuthenticationService; - -impl Transform for AuthenticationService -where - S: Service, Error = Error> + 'static, - S::Future: 'static, - B: MessageBody + 'static, - B::Error: StdError, -{ - type Response = ServiceResponse; - type Error = Error; - type Transform = AuthenticationMiddleware; - type InitError = (); - type Future = Ready>; - - fn new_transform(&self, service: S) -> Self::Future { - ok(AuthenticationMiddleware { service }) - } -} -pub struct AuthenticationMiddleware { - service: S, -} - -impl Service for AuthenticationMiddleware -where - S: Service, Error = Error> + 'static, - S::Future: 'static, - B: MessageBody + 'static, - B::Error: StdError, -{ - type Response = ServiceResponse; - type Error = Error; - type Future = LocalBoxFuture<'static, Result>; - - fn poll_ready(&self, cx: &mut Context<'_>) -> Poll> { - self.service.poll_ready(cx) - } - - fn call(&self, req: ServiceRequest) -> Self::Future { - let mut authenticate_pass: bool = false; - for ignore_route in IGNORE_ROUTES.iter() { - // tracing::info!("ignore: {}, path: {}", ignore_route, req.path()); - if req.path().starts_with(ignore_route) { - authenticate_pass = true; - break; - } - } - - if !authenticate_pass { - if let Some(header) = req.headers().get(HEADER_TOKEN) { - let result: Result = header.try_into(); - match result { - Ok(logged_user) => { - if cfg!(feature = "ignore_auth") { - authenticate_pass = true; - AUTHORIZED_USERS.store_auth(logged_user, true); - } else { - authenticate_pass = AUTHORIZED_USERS.is_authorized(&logged_user); - if authenticate_pass { - AUTHORIZED_USERS.store_auth(logged_user, true); - } - } - } - Err(e) => log::error!("{:?}", e), - } - } else { - tracing::debug!("Can't find any token from request: {:?}", req); - } - } - - if authenticate_pass { - let fut = self.service.call(req); - Box::pin(async move { - let res = fut.await?; - Ok(res.map_body(|_, body| AnyBody::from_message(body))) - }) - } else { - Box::pin(async move { Ok(req.into_response(unauthorized_response())) }) - } - } -} - -fn unauthorized_response() -> HttpResponse { - let error = ServerError::unauthorized(); - error.error_response() -} diff --git a/backend/src/middleware/cors_middleware.rs b/backend/src/middleware/cors_middleware.rs deleted file mode 100644 index 516c16c9df..0000000000 --- a/backend/src/middleware/cors_middleware.rs +++ /dev/null @@ -1,16 +0,0 @@ -use actix_cors::Cors; -use actix_web::http; - -// https://javascript.info/fetch-crossorigin#cors-for-safe-requests -// https://docs.rs/actix-cors/0.5.4/actix_cors/index.html -// http://www.ruanyifeng.com/blog/2016/04/cors.html -// Cors short for Cross-Origin Resource Sharing. -pub fn default_cors() -> Cors { - Cors::default() // allowed_origin return access-control-allow-origin: * by default - // .allowed_origin("http://127.0.0.1:8080") - .send_wildcard() - .allowed_methods(vec!["GET", "POST", "PUT", "DELETE"]) - .allowed_headers(vec![http::header::ACCEPT]) - .allowed_header(http::header::CONTENT_TYPE) - .max_age(3600) -} diff --git a/backend/src/middleware/mod.rs b/backend/src/middleware/mod.rs deleted file mode 100644 index 28e0ccaac9..0000000000 --- a/backend/src/middleware/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod auth_middleware; -mod cors_middleware; - -pub use auth_middleware::*; -pub use cors_middleware::*; diff --git a/backend/src/services/document/mod.rs b/backend/src/services/document/mod.rs deleted file mode 100644 index b9d15fe4a6..0000000000 --- a/backend/src/services/document/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -#![allow(clippy::module_inception)] - -pub mod persistence; -pub(crate) mod router; -pub(crate) mod ws_actor; -pub(crate) mod ws_receiver; diff --git a/backend/src/services/document/persistence.rs b/backend/src/services/document/persistence.rs deleted file mode 100644 index 2fffcad697..0000000000 --- a/backend/src/services/document/persistence.rs +++ /dev/null @@ -1,63 +0,0 @@ -use anyhow::Context; -use backend_service::errors::{internal_error, ServerError}; - -use flowy_collaboration::{ - protobuf::{CreateDocParams, DocumentId, DocumentInfo, ResetDocumentParams}, - server_document::ServerDocumentManager, - util::make_document_info_pb_from_revisions_pb, -}; - -use crate::services::kv::revision_kv::RevisionKVPersistence; -use std::sync::Arc; -use uuid::Uuid; - -#[tracing::instrument(level = "trace", skip(document_store, params), err)] -pub(crate) async fn create_document( - document_store: &Arc, - mut params: CreateDocParams, -) -> Result<(), ServerError> { - let revisions = params.take_revisions().take_items(); - let _ = document_store.set_revision(revisions.into()).await?; - Ok(()) -} - -#[tracing::instrument(level = "trace", skip(document_store), err)] -pub async fn read_document( - document_store: &Arc, - params: DocumentId, -) -> Result { - let _ = Uuid::parse_str(¶ms.doc_id).context("Parse document id to uuid failed")?; - let revisions = document_store.get_revisions(¶ms.doc_id, None).await?; - match make_document_info_pb_from_revisions_pb(¶ms.doc_id, revisions) { - Ok(Some(document_info)) => Ok(document_info), - Ok(None) => Err(ServerError::record_not_found().context(format!("{} not exist", params.doc_id))), - Err(e) => Err(ServerError::internal().context(e)), - } -} - -#[tracing::instrument(level = "debug", skip(document_manager, params), err)] -pub async fn reset_document( - document_manager: &Arc, - mut params: ResetDocumentParams, -) -> Result<(), ServerError> { - let repeated_revision = params.take_revisions(); - if repeated_revision.get_items().is_empty() { - return Err(ServerError::payload_none().context("Revisions should not be empty when reset the document")); - } - let doc_id = params.doc_id.clone(); - let _ = document_manager - .handle_document_reset(&doc_id, repeated_revision) - .await - .map_err(internal_error)?; - Ok(()) -} - -#[tracing::instrument(level = "trace", skip(document_store), err)] -pub(crate) async fn delete_document( - document_store: &Arc, - doc_id: Uuid, -) -> Result<(), ServerError> { - // TODO: delete revisions may cause time issue. Maybe delete asynchronously? - let _ = document_store.delete_revisions(&doc_id.to_string(), None).await?; - Ok(()) -} diff --git a/backend/src/services/document/router.rs b/backend/src/services/document/router.rs deleted file mode 100644 index e83a391659..0000000000 --- a/backend/src/services/document/router.rs +++ /dev/null @@ -1,48 +0,0 @@ -use crate::{ - context::FlowyPersistence, - services::document::persistence::{create_document, read_document, reset_document}, - util::serde_ext::parse_from_payload, -}; -use actix_web::{ - web::{Data, Payload}, - HttpResponse, -}; -use backend_service::{errors::ServerError, response::FlowyResponse}; -use flowy_collaboration::{ - protobuf::{ - CreateDocParams as CreateDocParamsPB, DocumentId as DocumentIdPB, ResetDocumentParams as ResetDocumentParamsPB, - }, - server_document::ServerDocumentManager, -}; -use std::sync::Arc; - -pub async fn create_document_handler( - payload: Payload, - persistence: Data>, -) -> Result { - let params: CreateDocParamsPB = parse_from_payload(payload).await?; - let kv_store = persistence.document_kv_store(); - let _ = create_document(&kv_store, params).await?; - Ok(FlowyResponse::success().into()) -} - -#[tracing::instrument(level = "debug", skip(payload, persistence), err)] -pub async fn read_document_handler( - payload: Payload, - persistence: Data>, -) -> Result { - let params: DocumentIdPB = parse_from_payload(payload).await?; - let kv_store = persistence.document_kv_store(); - let doc = read_document(&kv_store, params).await?; - let response = FlowyResponse::success().pb(doc)?; - Ok(response.into()) -} - -pub async fn reset_document_handler( - payload: Payload, - document_manager: Data>, -) -> Result { - let params: ResetDocumentParamsPB = parse_from_payload(payload).await?; - let _ = reset_document(document_manager.get_ref(), params).await?; - Ok(FlowyResponse::success().into()) -} diff --git a/backend/src/services/document/ws_actor.rs b/backend/src/services/document/ws_actor.rs deleted file mode 100644 index d48055a4a3..0000000000 --- a/backend/src/services/document/ws_actor.rs +++ /dev/null @@ -1,159 +0,0 @@ -use crate::{ - context::FlowyPersistence, - services::web_socket::{entities::Socket, WSClientData, WSUser, WebSocketMessage}, - util::serde_ext::{md5, parse_from_bytes}, -}; -use actix_rt::task::spawn_blocking; -use async_stream::stream; -use backend_service::errors::{internal_error, Result, ServerError}; - -use crate::services::web_socket::revision_data_to_ws_message; -use flowy_collaboration::{ - protobuf::{ - ClientRevisionWSData as ClientRevisionWSDataPB, ClientRevisionWSDataType as ClientRevisionWSDataTypePB, - Revision as RevisionPB, - }, - server_document::ServerDocumentManager, - synchronizer::{RevisionSyncResponse, RevisionUser}, -}; -use futures::stream::StreamExt; -use lib_ws::WSChannel; -use std::sync::Arc; -use tokio::sync::{mpsc, oneshot}; - -pub enum DocumentWSActorMessage { - ClientData { - client_data: WSClientData, - persistence: Arc, - ret: oneshot::Sender>, - }, -} - -pub struct DocumentWebSocketActor { - actor_msg_receiver: Option>, - doc_manager: Arc, -} - -impl DocumentWebSocketActor { - pub fn new(receiver: mpsc::Receiver, manager: Arc) -> Self { - Self { - actor_msg_receiver: Some(receiver), - doc_manager: manager, - } - } - - pub async fn run(mut self) { - let mut actor_msg_receiver = self - .actor_msg_receiver - .take() - .expect("DocumentWebSocketActor's receiver should only take one time"); - - let stream = stream! { - loop { - match actor_msg_receiver.recv().await { - Some(msg) => yield msg, - None => break, - } - } - }; - - stream.for_each(|msg| self.handle_message(msg)).await; - } - - async fn handle_message(&self, msg: DocumentWSActorMessage) { - match msg { - DocumentWSActorMessage::ClientData { - client_data, - persistence: _, - ret, - } => { - let _ = ret.send(self.handle_document_data(client_data).await); - } - } - } - - async fn handle_document_data(&self, client_data: WSClientData) -> Result<()> { - let WSClientData { user, socket, data } = client_data; - let document_client_data = spawn_blocking(move || parse_from_bytes::(&data)) - .await - .map_err(internal_error)??; - - tracing::trace!( - "[DocumentWebSocketActor]: receive: {}:{}, {:?}", - document_client_data.object_id, - document_client_data.data_id, - document_client_data.ty - ); - - let user = Arc::new(DocumentRevisionUser { user, socket }); - match &document_client_data.ty { - ClientRevisionWSDataTypePB::ClientPushRev => { - let _ = self - .doc_manager - .handle_client_revisions(user, document_client_data) - .await - .map_err(internal_error)?; - } - ClientRevisionWSDataTypePB::ClientPing => { - let _ = self - .doc_manager - .handle_client_ping(user, document_client_data) - .await - .map_err(internal_error)?; - } - } - - Ok(()) - } -} - -#[allow(dead_code)] -fn verify_md5(revision: &RevisionPB) -> Result<()> { - if md5(&revision.delta_data) != revision.md5 { - return Err(ServerError::internal().context("RevisionPB md5 not match")); - } - Ok(()) -} - -#[derive(Clone)] -pub struct DocumentRevisionUser { - pub user: Arc, - pub(crate) socket: Socket, -} - -impl std::fmt::Debug for DocumentRevisionUser { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - f.debug_struct("DocumentRevisionUser") - .field("user", &self.user) - .field("socket", &self.socket) - .finish() - } -} - -impl RevisionUser for DocumentRevisionUser { - fn user_id(&self) -> String { - self.user.id().to_string() - } - - fn receive(&self, resp: RevisionSyncResponse) { - let result = match resp { - RevisionSyncResponse::Pull(data) => { - let msg: WebSocketMessage = revision_data_to_ws_message(data, WSChannel::Document); - self.socket.try_send(msg).map_err(internal_error) - } - RevisionSyncResponse::Push(data) => { - let msg: WebSocketMessage = revision_data_to_ws_message(data, WSChannel::Document); - self.socket.try_send(msg).map_err(internal_error) - } - RevisionSyncResponse::Ack(data) => { - let msg: WebSocketMessage = revision_data_to_ws_message(data, WSChannel::Document); - self.socket.try_send(msg).map_err(internal_error) - } - }; - - match result { - Ok(_) => {} - Err(e) => log::error!("[DocumentRevisionUser]: {}", e), - } - } -} diff --git a/backend/src/services/document/ws_receiver.rs b/backend/src/services/document/ws_receiver.rs deleted file mode 100644 index 4faa7c85ee..0000000000 --- a/backend/src/services/document/ws_receiver.rs +++ /dev/null @@ -1,176 +0,0 @@ -use crate::{ - context::{DocumentRevisionKV, FlowyPersistence}, - services::{ - document::{ - persistence::{create_document, read_document}, - ws_actor::{DocumentWSActorMessage, DocumentWebSocketActor}, - }, - kv::revision_kv::revisions_to_key_value_items, - web_socket::{WSClientData, WebSocketReceiver}, - }, -}; -use backend_service::errors::ServerError; -use flowy_collaboration::{ - entities::document_info::DocumentInfo, - errors::CollaborateError, - protobuf::{ - CreateDocParams as CreateDocParamsPB, DocumentId, RepeatedRevision as RepeatedRevisionPB, - Revision as RevisionPB, - }, - server_document::{DocumentCloudPersistence, ServerDocumentManager}, - util::make_document_info_from_revisions_pb, -}; -use lib_infra::future::BoxResultFuture; -use std::{ - convert::TryInto, - fmt::{Debug, Formatter}, - sync::Arc, -}; -use tokio::sync::{mpsc, oneshot}; - -pub fn make_document_ws_receiver( - persistence: Arc, - document_manager: Arc, -) -> Arc { - let (actor_msg_sender, rx) = tokio::sync::mpsc::channel(1000); - let actor = DocumentWebSocketActor::new(rx, document_manager); - tokio::task::spawn(actor.run()); - - Arc::new(DocumentWebSocketReceiver::new(persistence, actor_msg_sender)) -} - -pub struct DocumentWebSocketReceiver { - actor_msg_sender: mpsc::Sender, - persistence: Arc, -} - -impl DocumentWebSocketReceiver { - pub fn new(persistence: Arc, actor_msg_sender: mpsc::Sender) -> Self { - Self { - actor_msg_sender, - persistence, - } - } -} - -impl WebSocketReceiver for DocumentWebSocketReceiver { - fn receive(&self, data: WSClientData) { - let (ret, rx) = oneshot::channel(); - let actor_msg_sender = self.actor_msg_sender.clone(); - let persistence = self.persistence.clone(); - - actix_rt::spawn(async move { - let msg = DocumentWSActorMessage::ClientData { - client_data: data, - persistence, - ret, - }; - - match actor_msg_sender.send(msg).await { - Ok(_) => {} - Err(e) => tracing::error!("[DocumentWebSocketReceiver]: send message to actor failed: {}", e), - } - match rx.await { - Ok(_) => {} - Err(e) => tracing::error!("[DocumentWebSocketReceiver]: message ret failed {:?}", e), - }; - }); - } -} - -pub struct HttpDocumentCloudPersistence(pub Arc); -impl Debug for HttpDocumentCloudPersistence { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str("HttpDocumentCloudPersistence") - } -} - -impl DocumentCloudPersistence for HttpDocumentCloudPersistence { - fn read_document(&self, doc_id: &str) -> BoxResultFuture { - let params = DocumentId { - doc_id: doc_id.to_string(), - ..Default::default() - }; - let document_store = self.0.clone(); - Box::pin(async move { - let mut pb_doc = read_document(&document_store, params) - .await - .map_err(|e| e.to_collaborate_error())?; - let doc = (&mut pb_doc) - .try_into() - .map_err(|e| CollaborateError::internal().context(e))?; - Ok(doc) - }) - } - - fn create_document( - &self, - doc_id: &str, - repeated_revision: RepeatedRevisionPB, - ) -> BoxResultFuture, CollaborateError> { - let document_store = self.0.clone(); - let doc_id = doc_id.to_owned(); - Box::pin(async move { - let document_info = make_document_info_from_revisions_pb(&doc_id, repeated_revision.clone())?; - let doc_id = doc_id.to_owned(); - let mut params = CreateDocParamsPB::new(); - params.set_id(doc_id); - params.set_revisions(repeated_revision); - let _ = create_document(&document_store, params) - .await - .map_err(|e| e.to_collaborate_error())?; - Ok(document_info) - }) - } - - fn read_document_revisions( - &self, - doc_id: &str, - rev_ids: Option>, - ) -> BoxResultFuture, CollaborateError> { - let document_store = self.0.clone(); - let doc_id = doc_id.to_owned(); - let f = || async move { - let mut repeated_revision = document_store.get_revisions(&doc_id, rev_ids).await?; - Ok::, ServerError>(repeated_revision.take_items().into()) - }; - - Box::pin(async move { f().await.map_err(|e| e.to_collaborate_error()) }) - } - - fn save_document_revisions( - &self, - mut repeated_revision: RepeatedRevisionPB, - ) -> BoxResultFuture<(), CollaborateError> { - let document_store = self.0.clone(); - let f = || async move { - let revisions = repeated_revision.take_items().into(); - let _ = document_store.set_revision(revisions).await?; - Ok::<(), ServerError>(()) - }; - - Box::pin(async move { f().await.map_err(|e| e.to_collaborate_error()) }) - } - - fn reset_document( - &self, - doc_id: &str, - mut repeated_revision: RepeatedRevisionPB, - ) -> BoxResultFuture<(), CollaborateError> { - let document_store = self.0.clone(); - let doc_id = doc_id.to_owned(); - let f = || async move { - document_store - .transaction(|mut transaction| { - Box::pin(async move { - let _ = transaction.batch_delete_key_start_with(&doc_id).await?; - let items = revisions_to_key_value_items(repeated_revision.take_items().into())?; - let _ = transaction.batch_set(items).await?; - Ok(()) - }) - }) - .await - }; - Box::pin(async move { f().await.map_err(|e| e.to_collaborate_error()) }) - } -} diff --git a/backend/src/services/folder/app/controller.rs b/backend/src/services/folder/app/controller.rs deleted file mode 100644 index ba997580df..0000000000 --- a/backend/src/services/folder/app/controller.rs +++ /dev/null @@ -1,113 +0,0 @@ -use crate::services::folder::view::read_view_belong_to_id; - -use crate::{ - entities::logged_user::LoggedUser, - services::folder::{app::persistence::*, trash::read_trash_ids}, - util::sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder}, -}; -use backend_service::errors::{invalid_params, ServerError}; -use chrono::Utc; -use flowy_folder_data_model::{ - parser::{ - app::{AppDesc, AppName}, - workspace::WorkspaceIdentify, - }, - protobuf::{App as AppPB, CreateAppParams as CreateAppParamsPB, RepeatedView as RepeatedViewPB}, -}; -use sqlx::{postgres::PgArguments, Postgres}; -use uuid::Uuid; - -pub(crate) async fn create_app( - transaction: &mut DBTransaction<'_>, - mut params: CreateAppParamsPB, - logged_user: LoggedUser, -) -> Result { - let name = AppName::parse(params.take_name()).map_err(invalid_params)?; - let workspace_id = WorkspaceIdentify::parse(params.take_workspace_id()).map_err(invalid_params)?; - let user_id = logged_user.as_uuid()?.to_string(); - let desc = AppDesc::parse(params.take_desc()).map_err(invalid_params)?; - - let (sql, args, app) = NewAppSqlBuilder::new(&user_id, workspace_id.as_ref()) - .name(name.as_ref()) - .desc(desc.as_ref()) - .color_style(params.take_color_style()) - .build()?; - - let _ = sqlx::query_with(&sql, args) - .execute(transaction) - .await - .map_err(map_sqlx_error)?; - Ok(app) -} - -pub(crate) async fn read_app( - transaction: &mut DBTransaction<'_>, - app_id: Uuid, - user: &LoggedUser, -) -> Result { - let table = read_app_table(app_id, transaction).await?; - - let read_trash_ids = read_trash_ids(user, transaction).await?; - if read_trash_ids.contains(&table.id.to_string()) { - return Err(ServerError::record_not_found()); - } - - let mut views = RepeatedViewPB::default(); - views.set_items( - read_view_belong_to_id(&table.id.to_string(), user, transaction as &mut DBTransaction<'_>) - .await? - .into(), - ); - - let mut app: AppPB = table.into(); - app.set_belongings(views); - Ok(app) -} - -pub(crate) async fn read_app_table(app_id: Uuid, transaction: &mut DBTransaction<'_>) -> Result { - let (sql, args) = SqlBuilder::select(APP_TABLE) - .add_field("*") - .and_where_eq("id", app_id) - .build()?; - - let table = sqlx::query_as_with::(&sql, args) - .fetch_one(transaction as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - - Ok(table) -} - -pub(crate) async fn update_app( - transaction: &mut DBTransaction<'_>, - app_id: Uuid, - name: Option, - desc: Option, - color_style: Option>, -) -> Result<(), ServerError> { - let (sql, args) = SqlBuilder::update(APP_TABLE) - .add_some_arg("name", name) - .add_some_arg("color_style", color_style) - .add_some_arg("description", desc) - .add_some_arg("modified_time", Some(Utc::now())) - .and_where_eq("id", app_id) - .build()?; - - sqlx::query_with(&sql, args) - .execute(transaction) - .await - .map_err(map_sqlx_error)?; - - Ok(()) -} - -#[tracing::instrument(skip(transaction), err)] -pub(crate) async fn delete_app(transaction: &mut DBTransaction<'_>, app_id: Uuid) -> Result<(), ServerError> { - let (sql, args) = SqlBuilder::delete(APP_TABLE).and_where_eq("id", app_id).build()?; - let _ = sqlx::query_with(&sql, args) - .execute(transaction) - .await - .map_err(map_sqlx_error)?; - - Ok(()) -} diff --git a/backend/src/services/folder/app/mod.rs b/backend/src/services/folder/app/mod.rs deleted file mode 100644 index 6ff67bd1b4..0000000000 --- a/backend/src/services/folder/app/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -#![allow(clippy::module_inception)] -pub mod controller; -pub mod router; - -pub mod persistence; diff --git a/backend/src/services/folder/app/persistence.rs b/backend/src/services/folder/app/persistence.rs deleted file mode 100644 index 58832eccd0..0000000000 --- a/backend/src/services/folder/app/persistence.rs +++ /dev/null @@ -1,140 +0,0 @@ -use crate::util::sqlx_ext::SqlBuilder; -use backend_service::errors::{invalid_params, ServerError}; -use chrono::{DateTime, NaiveDateTime, Utc}; -use flowy_folder_data_model::{ - parser::app::AppIdentify, - protobuf::{App as AppPB, ColorStyle as ColorStylePB, RepeatedView as RepeatedViewPB}, -}; -use protobuf::Message; -use sqlx::postgres::PgArguments; -use uuid::Uuid; - -pub(crate) const APP_TABLE: &str = "app_table"; - -pub struct NewAppSqlBuilder { - table: AppTable, -} - -impl NewAppSqlBuilder { - pub fn new(user_id: &str, workspace_id: &str) -> Self { - let uuid = uuid::Uuid::new_v4(); - let time = Utc::now(); - - let table = AppTable { - id: uuid, - workspace_id: workspace_id.to_string(), - name: "".to_string(), - description: "".to_string(), - color_style: default_color_style(), - last_view_id: "".to_string(), - modified_time: time, - create_time: time, - user_id: user_id.to_string(), - }; - - Self { table } - } - - pub fn from_app(user_id: &str, app: AppPB) -> Result { - let app_id = check_app_id(app.id)?; - let create_time = DateTime::::from_utc(NaiveDateTime::from_timestamp(app.create_time, 0), Utc); - let modified_time = DateTime::::from_utc(NaiveDateTime::from_timestamp(app.modified_time, 0), Utc); - - let table = AppTable { - id: app_id, - workspace_id: app.workspace_id, - name: app.name, - description: app.desc, - color_style: default_color_style(), - last_view_id: "".to_string(), - modified_time, - create_time, - user_id: user_id.to_string(), - }; - - Ok(Self { table }) - } - - pub fn name(mut self, name: &str) -> Self { - self.table.name = name.to_string(); - self - } - - #[allow(dead_code)] - pub fn last_view_id(mut self, view_id: &str) -> Self { - self.table.last_view_id = view_id.to_string(); - self - } - - pub fn desc(mut self, desc: &str) -> Self { - self.table.description = desc.to_owned(); - self - } - - pub fn color_style(mut self, color_style: ColorStylePB) -> Self { - self.table.color_style = color_style.write_to_bytes().unwrap_or_else(|_| default_color_style()); - self - } - - pub fn build(self) -> Result<(String, PgArguments, AppPB), ServerError> { - let app: AppPB = self.table.clone().into(); - - let (sql, args) = SqlBuilder::create(APP_TABLE) - .add_field_with_arg("id", self.table.id) - .add_field_with_arg("workspace_id", self.table.workspace_id) - .add_field_with_arg("name", self.table.name) - .add_field_with_arg("description", self.table.description) - .add_field_with_arg("color_style", self.table.color_style) - .add_field_with_arg("modified_time", self.table.modified_time) - .add_field_with_arg("create_time", self.table.create_time) - .add_field_with_arg("user_id", self.table.user_id) - .build()?; - - Ok((sql, args, app)) - } -} - -fn default_color_style() -> Vec { - let style = ColorStylePB::default(); - match style.write_to_bytes() { - Ok(bytes) => bytes, - Err(e) => { - log::error!("Serialize color style failed: {:?}", e); - vec![] - } - } -} - -pub(crate) fn check_app_id(id: String) -> Result { - let app_id = AppIdentify::parse(id).map_err(invalid_params)?; - let app_id = Uuid::parse_str(app_id.as_ref())?; - Ok(app_id) -} - -#[derive(Debug, Clone, sqlx::FromRow)] -pub struct AppTable { - pub(crate) id: uuid::Uuid, - pub(crate) workspace_id: String, - pub(crate) name: String, - pub(crate) description: String, - pub(crate) color_style: Vec, - pub(crate) last_view_id: String, - pub(crate) modified_time: chrono::DateTime, - pub(crate) create_time: chrono::DateTime, - #[allow(dead_code)] - pub(crate) user_id: String, -} -impl std::convert::From for AppPB { - fn from(table: AppTable) -> Self { - let mut app = AppPB::default(); - app.set_id(table.id.to_string()); - app.set_workspace_id(table.workspace_id.to_string()); - app.set_name(table.name.clone()); - app.set_desc(table.description.clone()); - app.set_belongings(RepeatedViewPB::default()); - app.set_modified_time(table.modified_time.timestamp()); - app.set_create_time(table.create_time.timestamp()); - - app - } -} diff --git a/backend/src/services/folder/app/router.rs b/backend/src/services/folder/app/router.rs deleted file mode 100644 index 54aa42983c..0000000000 --- a/backend/src/services/folder/app/router.rs +++ /dev/null @@ -1,114 +0,0 @@ -use crate::{ - entities::logged_user::LoggedUser, - services::folder::app::{ - controller::{create_app, delete_app, read_app, update_app}, - persistence::check_app_id, - }, - util::serde_ext::parse_from_payload, -}; -use actix_web::{ - web::{Data, Payload}, - HttpResponse, -}; -use anyhow::Context; -use backend_service::{ - errors::{invalid_params, ServerError}, - response::FlowyResponse, -}; -use flowy_folder_data_model::{ - parser::app::{AppDesc, AppName}, - protobuf::{AppId as AppIdPB, CreateAppParams as CreateAppParamsPB, UpdateAppParams as UpdateAppParamsPB}, -}; -use protobuf::Message; -use sqlx::PgPool; - -pub async fn create_handler( - payload: Payload, - pool: Data, - logged_user: LoggedUser, -) -> Result { - let params: CreateAppParamsPB = parse_from_payload(payload).await?; - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to create app")?; - - let app = create_app(&mut transaction, params, logged_user).await?; - - transaction - .commit() - .await - .context("Failed to commit SQL transaction to create app.")?; - - Ok(FlowyResponse::success().pb(app)?.into()) -} - -pub async fn read_handler(payload: Payload, pool: Data, user: LoggedUser) -> Result { - let params: AppIdPB = parse_from_payload(payload).await?; - let app_id = check_app_id(params.app_id)?; - - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to read app")?; - let app = read_app(&mut transaction, app_id, &user).await?; - transaction - .commit() - .await - .context("Failed to commit SQL transaction to read app.")?; - - Ok(FlowyResponse::success().pb(app)?.into()) -} - -pub async fn update_handler(payload: Payload, pool: Data) -> Result { - let params: UpdateAppParamsPB = parse_from_payload(payload).await?; - let app_id = check_app_id(params.get_app_id().to_string())?; - let name = match params.has_name() { - false => None, - true => Some(AppName::parse(params.get_name().to_owned()).map_err(invalid_params)?.0), - }; - - let color_style = match params.has_color_style() { - false => None, - true => { - let color_bytes = params.get_color_style().write_to_bytes()?; - Some(color_bytes) - } - }; - - let desc = match params.has_desc() { - false => None, - true => Some(AppDesc::parse(params.get_desc().to_owned()).map_err(invalid_params)?.0), - }; - - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to update app")?; - - let _ = update_app(&mut transaction, app_id, name, desc, color_style).await?; - - transaction - .commit() - .await - .context("Failed to commit SQL transaction to update app.")?; - Ok(FlowyResponse::success().into()) -} - -pub async fn delete_handler(payload: Payload, pool: Data) -> Result { - let params: AppIdPB = parse_from_payload(payload).await?; - let app_id = check_app_id(params.app_id.to_owned())?; - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to delete app")?; - - let _ = delete_app(&mut transaction, app_id).await?; - - transaction - .commit() - .await - .context("Failed to commit SQL transaction to delete app.")?; - - Ok(FlowyResponse::success().into()) -} diff --git a/backend/src/services/folder/mod.rs b/backend/src/services/folder/mod.rs deleted file mode 100644 index 6168695a8f..0000000000 --- a/backend/src/services/folder/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod app; -pub mod trash; -pub mod view; -pub mod workspace; -pub(crate) mod ws_actor; -pub(crate) mod ws_receiver; diff --git a/backend/src/services/folder/trash/mod.rs b/backend/src/services/folder/trash/mod.rs deleted file mode 100644 index 90b217882c..0000000000 --- a/backend/src/services/folder/trash/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -#![allow(clippy::module_inception)] -mod persistence; -pub mod router; -mod trash; - -pub(crate) use trash::*; diff --git a/backend/src/services/folder/trash/persistence.rs b/backend/src/services/folder/trash/persistence.rs deleted file mode 100644 index 20faf2e417..0000000000 --- a/backend/src/services/folder/trash/persistence.rs +++ /dev/null @@ -1,40 +0,0 @@ -use crate::services::folder::{app::persistence::AppTable, view::persistence::ViewTable}; -use flowy_folder_data_model::protobuf::{Trash, TrashType}; - -pub(crate) const TRASH_TABLE: &str = "trash_table"; - -#[derive(Debug, Clone, sqlx::FromRow)] -pub struct TrashTable { - pub(crate) id: uuid::Uuid, - #[allow(dead_code)] - pub(crate) user_id: String, - pub(crate) ty: i32, -} - -impl std::convert::From for Trash { - fn from(table: AppTable) -> Self { - Trash { - id: table.id.to_string(), - name: table.name, - modified_time: table.modified_time.timestamp(), - create_time: table.create_time.timestamp(), - ty: TrashType::App, - unknown_fields: Default::default(), - cached_size: Default::default(), - } - } -} - -impl std::convert::From for Trash { - fn from(table: ViewTable) -> Self { - Trash { - id: table.id.to_string(), - name: table.name, - modified_time: table.modified_time.timestamp(), - create_time: table.create_time.timestamp(), - ty: TrashType::View, - unknown_fields: Default::default(), - cached_size: Default::default(), - } - } -} diff --git a/backend/src/services/folder/trash/router.rs b/backend/src/services/folder/trash/router.rs deleted file mode 100644 index d943c4d1a0..0000000000 --- a/backend/src/services/folder/trash/router.rs +++ /dev/null @@ -1,106 +0,0 @@ -use crate::{ - context::FlowyPersistence, - entities::logged_user::LoggedUser, - services::folder::trash::{create_trash, delete_all_trash, delete_trash, read_trash}, - util::serde_ext::parse_from_payload, -}; -use ::protobuf::ProtobufEnum; -use actix_web::{ - web::{Data, Payload}, - HttpResponse, -}; -use anyhow::Context; -use backend_service::{ - errors::{invalid_params, ServerError}, - response::FlowyResponse, -}; -use flowy_folder_data_model::{parser::trash::TrashIdentify, protobuf::RepeatedTrashId}; -use sqlx::PgPool; -use std::sync::Arc; -use uuid::Uuid; - -#[tracing::instrument(skip(payload, pool, logged_user), err)] -pub async fn create_handler( - payload: Payload, - pool: Data, - logged_user: LoggedUser, -) -> Result { - let params: RepeatedTrashId = parse_from_payload(payload).await?; - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to create trash")?; - - let _ = create_trash(&mut transaction, make_records(params)?, logged_user).await?; - - transaction - .commit() - .await - .context("Failed to commit SQL transaction to create trash.")?; - - Ok(FlowyResponse::success().into()) -} - -#[tracing::instrument(skip(payload, persistence, logged_user), fields(delete_trash), err)] -pub async fn delete_handler( - payload: Payload, - persistence: Data>, - logged_user: LoggedUser, -) -> Result { - let pool = persistence.pg_pool(); - let kv_store = persistence.document_kv_store(); - let params: RepeatedTrashId = parse_from_payload(payload).await?; - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to delete trash")?; - - if params.delete_all { - tracing::Span::current().record("delete_trash", &"all"); - let _ = delete_all_trash(&mut transaction, &kv_store, &logged_user).await?; - } else { - let records = make_records(params)?; - let _ = delete_trash(&mut transaction, &kv_store, records).await?; - } - - transaction - .commit() - .await - .context("Failed to commit SQL transaction to delete trash.")?; - - Ok(FlowyResponse::success().into()) -} - -pub async fn read_handler(pool: Data, logged_user: LoggedUser) -> Result { - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to read trash")?; - - let repeated_trash = read_trash(&mut transaction, &logged_user).await?; - - transaction - .commit() - .await - .context("Failed to commit SQL transaction to read view.")?; - - Ok(FlowyResponse::success().pb(repeated_trash)?.into()) -} - -fn check_trash_id(id: String) -> Result { - let trash_id = TrashIdentify::parse(id).map_err(invalid_params)?; - let trash_id = Uuid::parse_str(trash_id.as_ref())?; - Ok(trash_id) -} - -fn make_records(identifiers: RepeatedTrashId) -> Result, ServerError> { - let mut records = vec![]; - for identifier in identifiers.items { - // match TrashType::from_i32(identifier.ty.value()) { - // None => {} - // Some(ty) => {} - // } - records.push((check_trash_id(identifier.id.to_owned())?, identifier.ty.value())); - } - Ok(records) -} diff --git a/backend/src/services/folder/trash/trash.rs b/backend/src/services/folder/trash/trash.rs deleted file mode 100644 index 9df45779ba..0000000000 --- a/backend/src/services/folder/trash/trash.rs +++ /dev/null @@ -1,180 +0,0 @@ -use crate::{ - context::DocumentRevisionKV, - entities::logged_user::LoggedUser, - services::folder::{ - app::controller::{delete_app, read_app_table}, - trash::persistence::{TrashTable, TRASH_TABLE}, - view::{delete_view, read_view_table}, - }, - util::sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder}, -}; -use ::protobuf::ProtobufEnum; -use backend_service::errors::ServerError; -use flowy_folder_data_model::protobuf::{RepeatedTrash, Trash, TrashType}; -use sqlx::{postgres::PgArguments, Postgres, Row}; -use std::sync::Arc; -use uuid::Uuid; - -#[tracing::instrument(skip(transaction, user), err)] -pub(crate) async fn create_trash( - transaction: &mut DBTransaction<'_>, - records: Vec<(Uuid, i32)>, - user: LoggedUser, -) -> Result<(), ServerError> { - for (trash_id, ty) in records { - let (sql, args) = SqlBuilder::create(TRASH_TABLE) - .add_field_with_arg("id", trash_id) - .add_field_with_arg("user_id", &user.user_id) - .add_field_with_arg("ty", ty) - .build()?; - - let _ = sqlx::query_with(&sql, args) - .execute(transaction as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - } - - Ok(()) -} - -#[tracing::instrument(skip(transaction, document_store, user), fields(delete_rows), err)] -pub(crate) async fn delete_all_trash( - transaction: &mut DBTransaction<'_>, - document_store: &Arc, - user: &LoggedUser, -) -> Result<(), ServerError> { - let (sql, args) = SqlBuilder::select(TRASH_TABLE) - .and_where_eq("user_id", &user.user_id) - .build()?; - let rows = sqlx::query_with(&sql, args) - .fetch_all(transaction as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)? - .into_iter() - .map(|row| (row.get("id"), row.get("ty"))) - .collect::>(); - tracing::Span::current().record("delete_rows", &format!("{:?}", rows).as_str()); - let affected_row_count = rows.len(); - let _ = delete_trash_associate_targets(transaction as &mut DBTransaction<'_>, document_store, rows).await?; - - let (sql, args) = SqlBuilder::delete(TRASH_TABLE) - .and_where_eq("user_id", &user.user_id) - .build()?; - let result = sqlx::query_with(&sql, args) - .execute(transaction as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - tracing::Span::current().record("affected_row", &result.rows_affected()); - debug_assert_eq!(affected_row_count as u64, result.rows_affected()); - - Ok(()) -} - -#[tracing::instrument(skip(transaction, document_store), err)] -pub(crate) async fn delete_trash( - transaction: &mut DBTransaction<'_>, - document_store: &Arc, - records: Vec<(Uuid, i32)>, -) -> Result<(), ServerError> { - for (trash_id, _) in records { - // Read the trash_table and delete the original table according to the TrashType - let (sql, args) = SqlBuilder::select(TRASH_TABLE) - .add_field("*") - .and_where_eq("id", trash_id) - .build()?; - - let trash_table = sqlx::query_as_with::(&sql, args) - .fetch_one(transaction as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - - let _ = delete_trash_associate_targets( - transaction as &mut DBTransaction<'_>, - document_store, - vec![(trash_table.id, trash_table.ty)], - ) - .await?; - - // Delete the trash table - let (sql, args) = SqlBuilder::delete(TRASH_TABLE).and_where_eq("id", &trash_id).build()?; - let _ = sqlx::query_with(&sql, args) - .execute(transaction as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - } - Ok(()) -} - -#[tracing::instrument(skip(transaction, document_store, targets), err)] -async fn delete_trash_associate_targets( - transaction: &mut DBTransaction<'_>, - document_store: &Arc, - targets: Vec<(Uuid, i32)>, -) -> Result<(), ServerError> { - for (id, ty) in targets { - match TrashType::from_i32(ty) { - None => log::error!("Parser trash type with value: {} failed", ty), - Some(ty) => match ty { - TrashType::Unknown => {} - TrashType::View => { - let _ = delete_view(transaction as &mut DBTransaction<'_>, document_store, vec![id]).await; - } - TrashType::App => { - let _ = delete_app(transaction as &mut DBTransaction<'_>, id).await; - } - }, - } - } - - Ok(()) -} - -pub(crate) async fn read_trash_ids( - user: &LoggedUser, - transaction: &mut DBTransaction<'_>, -) -> Result, ServerError> { - let repeated_trash = read_trash(transaction, user).await?.take_items().into_vec(); - let ids = repeated_trash - .into_iter() - .map(|trash| trash.id) - .collect::>(); - - Ok(ids) -} - -#[tracing::instrument(skip(transaction, user), err)] -pub(crate) async fn read_trash( - transaction: &mut DBTransaction<'_>, - user: &LoggedUser, -) -> Result { - let (sql, args) = SqlBuilder::select(TRASH_TABLE) - .add_field("*") - .and_where_eq("user_id", &user.user_id) - .build()?; - - let tables = sqlx::query_as_with::(&sql, args) - .fetch_all(transaction as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - - let mut trash: Vec = vec![]; - for table in tables { - match TrashType::from_i32(table.ty) { - None => log::error!("Parser trash type with value: {} failed", table.ty), - Some(ty) => match ty { - TrashType::Unknown => {} - TrashType::View => { - trash.push(read_view_table(table.id, transaction).await?.into()); - } - TrashType::App => { - trash.push(read_app_table(table.id, transaction).await?.into()); - } - }, - } - } - - let mut repeated_trash = RepeatedTrash::default(); - repeated_trash.set_items(trash.into()); - - Ok(repeated_trash) -} diff --git a/backend/src/services/folder/view/controller.rs b/backend/src/services/folder/view/controller.rs deleted file mode 100644 index b9883d1286..0000000000 --- a/backend/src/services/folder/view/controller.rs +++ /dev/null @@ -1,170 +0,0 @@ -use crate::{ - entities::logged_user::LoggedUser, - services::{ - document::persistence::{create_document, delete_document}, - folder::{trash::read_trash_ids, view::persistence::*}, - }, - util::sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder}, -}; -use backend_service::errors::{invalid_params, ServerError}; - -use crate::context::DocumentRevisionKV; -use chrono::Utc; -use flowy_collaboration::{ - client_document::default::initial_delta, - entities::revision::{RepeatedRevision, Revision}, - protobuf::CreateDocParams as CreateDocParamsPB, -}; -use flowy_folder_data_model::{ - parser::{ - app::AppIdentify, - view::{ViewDesc, ViewName, ViewThumbnail}, - }, - protobuf::{CreateViewParams as CreateViewParamsPB, RepeatedView as RepeatedViewPB, View as ViewPB}, -}; -use sqlx::{postgres::PgArguments, Postgres}; -use std::{convert::TryInto, sync::Arc}; -use uuid::Uuid; - -pub(crate) async fn update_view( - transaction: &mut DBTransaction<'_>, - view_id: Uuid, - name: Option, - desc: Option, - thumbnail: Option, -) -> Result<(), ServerError> { - let (sql, args) = SqlBuilder::update(VIEW_TABLE) - .add_some_arg("name", name) - .add_some_arg("description", desc) - .add_some_arg("thumbnail", thumbnail) - .add_some_arg("modified_time", Some(Utc::now())) - .and_where_eq("id", view_id) - .build()?; - - sqlx::query_with(&sql, args) - .execute(transaction) - .await - .map_err(map_sqlx_error)?; - - Ok(()) -} - -#[tracing::instrument(skip(transaction, document_store), err)] -pub(crate) async fn delete_view( - transaction: &mut DBTransaction<'_>, - document_store: &Arc, - view_ids: Vec, -) -> Result<(), ServerError> { - for view_id in view_ids { - let (sql, args) = SqlBuilder::delete(VIEW_TABLE).and_where_eq("id", &view_id).build()?; - let _ = sqlx::query_with(&sql, args) - .execute(transaction as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - - let _ = delete_document(document_store, view_id).await?; - } - Ok(()) -} - -#[tracing::instrument(name = "create_view", level = "debug", skip(transaction, document_store), err)] -pub(crate) async fn create_view( - transaction: &mut DBTransaction<'_>, - document_store: Arc, - params: CreateViewParamsPB, - user_id: &str, -) -> Result { - let view_id = check_view_id(params.view_id.clone())?; - let name = ViewName::parse(params.name).map_err(invalid_params)?; - let belong_to_id = AppIdentify::parse(params.belong_to_id).map_err(invalid_params)?; - let thumbnail = ViewThumbnail::parse(params.thumbnail).map_err(invalid_params)?; - let desc = ViewDesc::parse(params.desc).map_err(invalid_params)?; - - let (sql, args, view) = NewViewSqlBuilder::new(view_id, belong_to_id.as_ref()) - .name(name.as_ref()) - .desc(desc.as_ref()) - .thumbnail(thumbnail.as_ref()) - .view_type(params.view_type) - .build()?; - - let _ = sqlx::query_with(&sql, args) - .execute(transaction as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - - let initial_delta_data = initial_delta().to_bytes(); - let md5 = format!("{:x}", md5::compute(&initial_delta_data)); - let revision = Revision::new(&view.id, 0, 0, initial_delta_data, user_id, md5); - let repeated_revision = RepeatedRevision::new(vec![revision]); - let mut create_doc_params = CreateDocParamsPB::new(); - create_doc_params.set_revisions(repeated_revision.try_into().unwrap()); - create_doc_params.set_id(view.id.clone()); - let _ = create_document(&document_store, create_doc_params).await?; - - Ok(view) -} - -pub(crate) async fn read_view( - user: &LoggedUser, - view_id: Uuid, - transaction: &mut DBTransaction<'_>, -) -> Result { - let table = read_view_table(view_id, transaction as &mut DBTransaction<'_>).await?; - - let read_trash_ids = read_trash_ids(user, transaction).await?; - if read_trash_ids.contains(&table.id.to_string()) { - return Err(ServerError::record_not_found()); - } - - let mut views = RepeatedViewPB::default(); - views.set_items( - read_view_belong_to_id(&table.id.to_string(), user, transaction) - .await? - .into(), - ); - let mut view: ViewPB = table.into(); - view.set_belongings(views); - Ok(view) -} - -pub(crate) async fn read_view_table( - view_id: Uuid, - transaction: &mut DBTransaction<'_>, -) -> Result { - let (sql, args) = SqlBuilder::select(VIEW_TABLE) - .add_field("*") - .and_where_eq("id", view_id) - .build()?; - - let table = sqlx::query_as_with::(&sql, args) - .fetch_one(transaction as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - - Ok(table) -} - -// transaction must be commit from caller -pub(crate) async fn read_view_belong_to_id<'c>( - id: &str, - user: &LoggedUser, - transaction: &mut DBTransaction<'_>, -) -> Result, ServerError> { - // TODO: add index for app_table - let (sql, args) = SqlBuilder::select(VIEW_TABLE) - .add_field("*") - .and_where_eq("belong_to_id", id) - .build()?; - - let mut tables = sqlx::query_as_with::(&sql, args) - .fetch_all(transaction as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - - let read_trash_ids = read_trash_ids(user, transaction).await?; - tables.retain(|table| !read_trash_ids.contains(&table.id.to_string())); - - let views = tables.into_iter().map(|table| table.into()).collect::>(); - - Ok(views) -} diff --git a/backend/src/services/folder/view/mod.rs b/backend/src/services/folder/view/mod.rs deleted file mode 100644 index 775b5d116f..0000000000 --- a/backend/src/services/folder/view/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -#![allow(clippy::module_inception)] -mod controller; -pub mod persistence; -pub mod router; - -pub(crate) use controller::*; diff --git a/backend/src/services/folder/view/persistence.rs b/backend/src/services/folder/view/persistence.rs deleted file mode 100644 index f43e1573bb..0000000000 --- a/backend/src/services/folder/view/persistence.rs +++ /dev/null @@ -1,134 +0,0 @@ -use crate::util::sqlx_ext::SqlBuilder; -use backend_service::errors::{invalid_params, ServerError}; -use chrono::{DateTime, NaiveDateTime, Utc}; -use flowy_folder_data_model::{ - parser::view::ViewIdentify, - protobuf::{RepeatedView as RepeatedViewPB, View as ViewPB, ViewType as ViewTypePB}, -}; -use protobuf::ProtobufEnum; -use sqlx::postgres::PgArguments; -use uuid::Uuid; - -pub(crate) const VIEW_TABLE: &str = "view_table"; - -pub struct NewViewSqlBuilder { - table: ViewTable, -} - -impl NewViewSqlBuilder { - pub fn new(view_id: Uuid, belong_to_id: &str) -> Self { - let time = Utc::now(); - - let table = ViewTable { - id: view_id, - belong_to_id: belong_to_id.to_string(), - name: "".to_string(), - description: "".to_string(), - modified_time: time, - create_time: time, - thumbnail: "".to_string(), - view_type: ViewTypePB::Doc.value(), - }; - - Self { table } - } - - pub fn from_view(view: ViewPB) -> Result { - let view_id = ViewIdentify::parse(view.id).map_err(invalid_params)?; - let view_id = Uuid::parse_str(view_id.as_ref())?; - let create_time = DateTime::::from_utc(NaiveDateTime::from_timestamp(view.create_time, 0), Utc); - let modified_time = DateTime::::from_utc(NaiveDateTime::from_timestamp(view.modified_time, 0), Utc); - - let table = ViewTable { - id: view_id, - belong_to_id: view.belong_to_id, - name: view.name, - description: view.desc, - modified_time, - create_time, - thumbnail: "".to_string(), - view_type: view.view_type.value(), - }; - Ok(Self { table }) - } - - pub fn name(mut self, name: &str) -> Self { - self.table.name = name.to_string(); - self - } - - pub fn desc(mut self, desc: &str) -> Self { - self.table.description = desc.to_owned(); - self - } - - pub fn thumbnail(mut self, thumbnail: &str) -> Self { - self.table.thumbnail = thumbnail.to_owned(); - self - } - - pub fn view_type(mut self, view_type: ViewTypePB) -> Self { - self.table.view_type = view_type.value(); - self - } - - pub fn build(self) -> Result<(String, PgArguments, ViewPB), ServerError> { - let view: ViewPB = self.table.clone().into(); - - let (sql, args) = SqlBuilder::create(VIEW_TABLE) - .add_field_with_arg("id", self.table.id) - .add_field_with_arg("belong_to_id", self.table.belong_to_id) - .add_field_with_arg("name", self.table.name) - .add_field_with_arg("description", self.table.description) - .add_field_with_arg("modified_time", self.table.modified_time) - .add_field_with_arg("create_time", self.table.create_time) - .add_field_with_arg("thumbnail", self.table.thumbnail) - .add_field_with_arg("view_type", self.table.view_type) - .build()?; - - Ok((sql, args, view)) - } -} - -pub(crate) fn check_view_ids(ids: Vec) -> Result, ServerError> { - let mut view_ids = vec![]; - for id in ids { - view_ids.push(check_view_id(id)?); - } - Ok(view_ids) -} - -pub(crate) fn check_view_id(id: String) -> Result { - let view_id = ViewIdentify::parse(id).map_err(invalid_params)?; - let view_id = Uuid::parse_str(view_id.as_ref())?; - Ok(view_id) -} - -#[derive(Debug, Clone, sqlx::FromRow)] -pub struct ViewTable { - pub(crate) id: uuid::Uuid, - pub(crate) belong_to_id: String, - pub(crate) name: String, - pub(crate) description: String, - pub(crate) modified_time: chrono::DateTime, - pub(crate) create_time: chrono::DateTime, - pub(crate) thumbnail: String, - pub(crate) view_type: i32, -} -impl std::convert::From for ViewPB { - fn from(table: ViewTable) -> Self { - let view_type = ViewTypePB::from_i32(table.view_type).unwrap_or(ViewTypePB::Doc); - - let mut view = ViewPB::default(); - view.set_id(table.id.to_string()); - view.set_belong_to_id(table.belong_to_id); - view.set_name(table.name); - view.set_desc(table.description); - view.set_view_type(view_type); - view.set_belongings(RepeatedViewPB::default()); - view.set_create_time(table.create_time.timestamp()); - view.set_modified_time(table.modified_time.timestamp()); - - view - } -} diff --git a/backend/src/services/folder/view/router.rs b/backend/src/services/folder/view/router.rs deleted file mode 100644 index ce66dda39d..0000000000 --- a/backend/src/services/folder/view/router.rs +++ /dev/null @@ -1,128 +0,0 @@ -use crate::{ - context::FlowyPersistence, - entities::logged_user::LoggedUser, - services::folder::view::{ - create_view, delete_view, - persistence::{check_view_id, check_view_ids}, - read_view, update_view, - }, - util::serde_ext::parse_from_payload, -}; -use actix_web::{ - web::{Data, Payload}, - HttpResponse, -}; -use anyhow::Context; -use backend_service::{ - errors::{invalid_params, ServerError}, - response::FlowyResponse, -}; -use flowy_folder_data_model::{ - parser::view::{ViewDesc, ViewName, ViewThumbnail}, - protobuf::{ - CreateViewParams as CreateViewParamsPB, QueryViewRequest as QueryViewRequestPB, - UpdateViewParams as UpdateViewParamsPB, ViewId as ViewIdPB, - }, -}; -use sqlx::PgPool; -use std::sync::Arc; - -pub async fn create_handler( - payload: Payload, - persistence: Data>, - user: LoggedUser, -) -> Result { - let params: CreateViewParamsPB = parse_from_payload(payload).await?; - let kv_store = persistence.document_kv_store(); - let pool = persistence.pg_pool(); - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to create view")?; - - let view = create_view(&mut transaction, kv_store, params, &user.user_id).await?; - transaction - .commit() - .await - .context("Failed to commit SQL transaction to create view.")?; - - let resp = FlowyResponse::success().pb(view)?; - Ok(resp.into()) -} - -pub async fn read_handler(payload: Payload, pool: Data, user: LoggedUser) -> Result { - let params: ViewIdPB = parse_from_payload(payload).await?; - let view_id = check_view_ids(vec![params.view_id])?.pop().unwrap(); - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to read view")?; - let view = read_view(&user, view_id, &mut transaction).await?; - - transaction - .commit() - .await - .context("Failed to commit SQL transaction to read view.")?; - - Ok(FlowyResponse::success().pb(view)?.into()) -} - -pub async fn update_handler(payload: Payload, pool: Data) -> Result { - let params: UpdateViewParamsPB = parse_from_payload(payload).await?; - let view_id = check_view_id(params.view_id.clone())?; - let name = match params.has_name() { - false => None, - true => Some(ViewName::parse(params.get_name().to_owned()).map_err(invalid_params)?.0), - }; - - let desc = match params.has_desc() { - false => None, - true => Some(ViewDesc::parse(params.get_desc().to_owned()).map_err(invalid_params)?.0), - }; - - let thumbnail = match params.has_thumbnail() { - false => None, - true => Some( - ViewThumbnail::parse(params.get_thumbnail().to_owned()) - .map_err(invalid_params)? - .0, - ), - }; - - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to update app")?; - - let _ = update_view(&mut transaction, view_id, name, desc, thumbnail).await?; - - transaction - .commit() - .await - .context("Failed to commit SQL transaction to update view.")?; - - Ok(FlowyResponse::success().into()) -} - -pub async fn delete_handler( - payload: Payload, - persistence: Data>, -) -> Result { - let params: QueryViewRequestPB = parse_from_payload(payload).await?; - let pool = persistence.pg_pool(); - let kv_store = persistence.document_kv_store(); - let view_ids = check_view_ids(params.view_ids.to_vec())?; - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to delete view")?; - - let _ = delete_view(&mut transaction, &kv_store, view_ids).await?; - - transaction - .commit() - .await - .context("Failed to commit SQL transaction to delete view.")?; - - Ok(FlowyResponse::success().into()) -} diff --git a/backend/src/services/folder/workspace/controller.rs b/backend/src/services/folder/workspace/controller.rs deleted file mode 100644 index 6a3a6d54fc..0000000000 --- a/backend/src/services/folder/workspace/controller.rs +++ /dev/null @@ -1,144 +0,0 @@ -use super::persistence::NewWorkspaceBuilder; -use crate::{ - entities::logged_user::LoggedUser, - services::folder::{ - app::{controller::read_app, persistence::AppTable}, - workspace::persistence::*, - }, - util::sqlx_ext::*, -}; -use anyhow::Context; -use backend_service::errors::{invalid_params, ServerError}; -use flowy_folder_data_model::{ - parser::workspace::WorkspaceIdentify, - protobuf::{RepeatedApp as RepeatedAppPB, RepeatedWorkspace as RepeatedWorkspacePB, Workspace as WorkspacePB}, -}; -use sqlx::{postgres::PgArguments, Postgres}; -use uuid::Uuid; - -pub(crate) async fn create_workspace( - transaction: &mut DBTransaction<'_>, - name: &str, - desc: &str, - logged_user: LoggedUser, -) -> Result { - let user_id = logged_user.as_uuid()?.to_string(); - let (sql, args, workspace) = NewWorkspaceBuilder::new(&user_id).name(name).desc(desc).build()?; - - let _ = sqlx::query_with(&sql, args) - .execute(transaction) - .await - .map_err(map_sqlx_error)?; - - Ok(workspace) -} - -pub(crate) async fn update_workspace( - transaction: &mut DBTransaction<'_>, - workspace_id: Uuid, - name: Option, - desc: Option, -) -> Result<(), ServerError> { - let (sql, args) = SqlBuilder::update(WORKSPACE_TABLE) - .add_some_arg("name", name) - .add_some_arg("description", desc) - .and_where_eq("id", workspace_id) - .build()?; - - sqlx::query_with(&sql, args) - .execute(transaction) - .await - .map_err(map_sqlx_error)?; - - Ok(()) -} - -pub(crate) async fn delete_workspace( - transaction: &mut DBTransaction<'_>, - workspace_id: Uuid, -) -> Result<(), ServerError> { - let (sql, args) = SqlBuilder::delete(WORKSPACE_TABLE) - .and_where_eq("id", workspace_id) - .build()?; - - let _ = sqlx::query_with(&sql, args) - .execute(transaction) - .await - .map_err(map_sqlx_error)?; - - Ok(()) -} - -#[tracing::instrument(skip(transaction, logged_user), err)] -pub async fn read_workspaces( - transaction: &mut DBTransaction<'_>, - workspace_id: Option, - logged_user: LoggedUser, -) -> Result { - let user_id = logged_user.as_uuid()?.to_string(); - - let mut builder = SqlBuilder::select(WORKSPACE_TABLE) - .add_field("*") - .and_where_eq("user_id", &user_id); - - if let Some(workspace_id) = workspace_id { - let workspace_id = check_workspace_id(workspace_id)?; - builder = builder.and_where_eq("id", workspace_id); - } - - let (sql, args) = builder.build()?; - let tables = sqlx::query_as_with::(&sql, args) - .fetch_all(transaction as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - - let mut repeated_workspace = RepeatedWorkspacePB::default(); - let mut workspaces = vec![]; - // Opti: combine the query - for table in tables { - let apps = read_workspace_apps( - &logged_user, - transaction as &mut DBTransaction<'_>, - &table.id.to_string(), - ) - .await - .context("Get workspace app") - .unwrap_or_default(); - - let mut workspace: WorkspacePB = table.into(); - workspace.set_apps(apps); - workspaces.push(workspace); - } - - repeated_workspace.set_items(workspaces.into()); - Ok(repeated_workspace) -} - -#[tracing::instrument(skip(transaction, user), fields(app_count), err)] -async fn read_workspace_apps<'c>( - user: &LoggedUser, - transaction: &mut DBTransaction<'_>, - workspace_id: &str, -) -> Result { - let workspace_id = WorkspaceIdentify::parse(workspace_id.to_owned()).map_err(invalid_params)?; - let (sql, args) = SqlBuilder::select("app_table") - .add_field("*") - .and_where_eq("workspace_id", workspace_id.0) - .build()?; - - let app_tables = sqlx::query_as_with::(&sql, args) - .fetch_all(transaction as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - - tracing::Span::current().record("app_count", &app_tables.len()); - let mut apps = vec![]; - for table in app_tables { - let app = read_app(transaction, table.id, user).await?; - apps.push(app); - } - - let mut repeated_app = RepeatedAppPB::default(); - repeated_app.set_items(apps.into()); - Ok(repeated_app) -} diff --git a/backend/src/services/folder/workspace/mod.rs b/backend/src/services/folder/workspace/mod.rs deleted file mode 100644 index dce53b50a4..0000000000 --- a/backend/src/services/folder/workspace/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -#![allow(clippy::module_inception)] -mod controller; -pub mod persistence; -pub mod router; - -pub use controller::*; diff --git a/backend/src/services/folder/workspace/persistence.rs b/backend/src/services/folder/workspace/persistence.rs deleted file mode 100644 index 48957c98af..0000000000 --- a/backend/src/services/folder/workspace/persistence.rs +++ /dev/null @@ -1,98 +0,0 @@ -use crate::util::sqlx_ext::SqlBuilder; -use backend_service::errors::{invalid_params, ServerError}; -use chrono::{DateTime, NaiveDateTime, Utc}; -use flowy_folder_data_model::{parser::workspace::WorkspaceIdentify, protobuf::Workspace as WorkspacePB}; -use sqlx::postgres::PgArguments; -use uuid::Uuid; - -pub struct NewWorkspaceBuilder { - table: WorkspaceTable, -} - -impl NewWorkspaceBuilder { - pub fn new(user_id: &str) -> Self { - let uuid = uuid::Uuid::new_v4(); - let time = Utc::now(); - - let table = WorkspaceTable { - id: uuid, - name: "".to_string(), - description: "".to_string(), - modified_time: time, - create_time: time, - user_id: user_id.to_string(), - }; - Self { table } - } - - pub fn from_workspace(user_id: &str, workspace: WorkspacePB) -> Result { - let workspace_id = check_workspace_id(workspace.id)?; - let create_time = DateTime::::from_utc(NaiveDateTime::from_timestamp(workspace.create_time, 0), Utc); - let modified_time = DateTime::::from_utc(NaiveDateTime::from_timestamp(workspace.modified_time, 0), Utc); - - let table = WorkspaceTable { - id: workspace_id, - name: workspace.name, - description: workspace.desc, - modified_time, - create_time, - user_id: user_id.to_string(), - }; - - Ok(Self { table }) - } - - pub fn name(mut self, name: &str) -> Self { - self.table.name = name.to_string(); - self - } - - pub fn desc(mut self, desc: &str) -> Self { - self.table.description = desc.to_owned(); - self - } - - pub fn build(self) -> Result<(String, PgArguments, WorkspacePB), ServerError> { - let workspace: WorkspacePB = self.table.clone().into(); - // TODO: use macro to fetch each field from struct - let (sql, args) = SqlBuilder::create(WORKSPACE_TABLE) - .add_field_with_arg("id", self.table.id) - .add_field_with_arg("name", self.table.name) - .add_field_with_arg("description", self.table.description) - .add_field_with_arg("modified_time", self.table.modified_time) - .add_field_with_arg("create_time", self.table.create_time) - .add_field_with_arg("user_id", self.table.user_id) - .build()?; - - Ok((sql, args, workspace)) - } -} - -pub(crate) fn check_workspace_id(id: String) -> Result { - let workspace_id = WorkspaceIdentify::parse(id).map_err(invalid_params)?; - let workspace_id = Uuid::parse_str(workspace_id.as_ref())?; - Ok(workspace_id) -} - -pub(crate) const WORKSPACE_TABLE: &str = "workspace_table"; - -#[derive(Debug, Clone, sqlx::FromRow)] -pub struct WorkspaceTable { - pub(crate) id: uuid::Uuid, - pub(crate) name: String, - pub(crate) description: String, - pub(crate) modified_time: chrono::DateTime, - pub(crate) create_time: chrono::DateTime, - pub(crate) user_id: String, -} -impl std::convert::From for WorkspacePB { - fn from(table: WorkspaceTable) -> Self { - let mut workspace = WorkspacePB::default(); - workspace.set_id(table.id.to_string()); - workspace.set_name(table.name.clone()); - workspace.set_desc(table.description.clone()); - workspace.set_modified_time(table.modified_time.timestamp()); - workspace.set_create_time(table.create_time.timestamp()); - workspace - } -} diff --git a/backend/src/services/folder/workspace/router.rs b/backend/src/services/folder/workspace/router.rs deleted file mode 100644 index 764a1ba78c..0000000000 --- a/backend/src/services/folder/workspace/router.rs +++ /dev/null @@ -1,149 +0,0 @@ -use crate::{ - entities::logged_user::LoggedUser, - services::folder::workspace::{ - create_workspace, delete_workspace, persistence::check_workspace_id, read_workspaces, update_workspace, - }, - util::serde_ext::parse_from_payload, -}; -use actix_web::{ - web::{Data, Payload}, - HttpResponse, -}; -use anyhow::Context; -use backend_service::{ - errors::{invalid_params, ServerError}, - response::FlowyResponse, -}; -use flowy_folder_data_model::{ - parser::workspace::{WorkspaceDesc, WorkspaceName}, - protobuf::{ - CreateWorkspaceParams as CreateWorkspaceParamsPB, UpdateWorkspaceParams as UpdateWorkspaceParamsPB, - WorkspaceId as WorkspaceIdPB, - }, -}; -use sqlx::PgPool; - -pub async fn create_handler( - payload: Payload, - pool: Data, - logged_user: LoggedUser, -) -> Result { - let params: CreateWorkspaceParamsPB = parse_from_payload(payload).await?; - let name = WorkspaceName::parse(params.get_name().to_owned()).map_err(invalid_params)?; - let desc = WorkspaceDesc::parse(params.get_desc().to_owned()).map_err(invalid_params)?; - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to create workspace")?; - let workspace = create_workspace(&mut transaction, name.as_ref(), desc.as_ref(), logged_user).await?; - transaction - .commit() - .await - .context("Failed to commit SQL transaction to create workspace.")?; - - Ok(FlowyResponse::success().pb(workspace)?.into()) -} - -pub async fn read_handler( - payload: Payload, - pool: Data, - logged_user: LoggedUser, -) -> Result { - let params: WorkspaceIdPB = parse_from_payload(payload).await?; - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to read workspace")?; - - let workspace_id = if params.has_workspace_id() { - Some(params.get_workspace_id().to_owned()) - } else { - None - }; - let repeated_workspace = read_workspaces(&mut transaction, workspace_id, logged_user).await?; - - transaction - .commit() - .await - .context("Failed to commit SQL transaction to read workspace.")?; - - Ok(FlowyResponse::success().pb(repeated_workspace)?.into()) -} - -pub async fn delete_handler( - payload: Payload, - pool: Data, - _logged_user: LoggedUser, -) -> Result { - let params: WorkspaceIdPB = parse_from_payload(payload).await?; - let workspace_id = check_workspace_id(params.get_workspace_id().to_owned())?; - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to delete workspace")?; - - let _ = delete_workspace(&mut transaction, workspace_id).await?; - transaction - .commit() - .await - .context("Failed to commit SQL transaction to delete workspace.")?; - - Ok(FlowyResponse::success().into()) -} - -pub async fn update_handler( - payload: Payload, - pool: Data, - _logged_user: LoggedUser, -) -> Result { - let params: UpdateWorkspaceParamsPB = parse_from_payload(payload).await?; - let workspace_id = check_workspace_id(params.get_id().to_owned())?; - let name = match params.has_name() { - false => None, - true => { - let name = WorkspaceName::parse(params.get_name().to_owned()) - .map_err(invalid_params)? - .0; - Some(name) - } - }; - - let desc = match params.has_desc() { - false => None, - true => { - let desc = WorkspaceDesc::parse(params.get_desc().to_owned()) - .map_err(invalid_params)? - .0; - Some(desc) - } - }; - - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to update workspace")?; - - let _ = update_workspace(&mut transaction, workspace_id, name, desc).await?; - - transaction - .commit() - .await - .context("Failed to commit SQL transaction to update workspace.")?; - - Ok(FlowyResponse::success().into()) -} - -pub async fn workspace_list(pool: Data, logged_user: LoggedUser) -> Result { - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to read workspaces")?; - - let repeated_workspace = read_workspaces(&mut transaction, None, logged_user).await?; - transaction - .commit() - .await - .context("Failed to commit SQL transaction to read workspace.")?; - - Ok(FlowyResponse::success().pb(repeated_workspace)?.into()) -} diff --git a/backend/src/services/folder/ws_actor.rs b/backend/src/services/folder/ws_actor.rs deleted file mode 100644 index 3e655243c0..0000000000 --- a/backend/src/services/folder/ws_actor.rs +++ /dev/null @@ -1,148 +0,0 @@ -use crate::{ - context::FlowyPersistence, - services::web_socket::{entities::Socket, revision_data_to_ws_message, WSClientData, WSUser, WebSocketMessage}, - util::serde_ext::parse_from_bytes, -}; -use actix_rt::task::spawn_blocking; -use async_stream::stream; -use backend_service::errors::{internal_error, Result}; - -use flowy_collaboration::{ - protobuf::{ - ClientRevisionWSData as ClientRevisionWSDataPB, ClientRevisionWSDataType as ClientRevisionWSDataTypePB, - }, - server_folder::ServerFolderManager, - synchronizer::{RevisionSyncResponse, RevisionUser}, -}; -use futures::stream::StreamExt; -use lib_ws::WSChannel; -use std::sync::Arc; -use tokio::sync::{mpsc, oneshot}; - -pub enum FolderWSActorMessage { - ClientData { - client_data: WSClientData, - persistence: Arc, - ret: oneshot::Sender>, - }, -} - -pub struct FolderWebSocketActor { - actor_msg_receiver: Option>, - folder_manager: Arc, -} - -impl FolderWebSocketActor { - pub fn new(receiver: mpsc::Receiver, folder_manager: Arc) -> Self { - Self { - actor_msg_receiver: Some(receiver), - folder_manager, - } - } - - pub async fn run(mut self) { - let mut actor_msg_receiver = self - .actor_msg_receiver - .take() - .expect("FolderWebSocketActor's receiver should only take one time"); - let stream = stream! { - loop { - match actor_msg_receiver.recv().await { - Some(msg) => yield msg, - None => { - break - }, - } - } - }; - stream.for_each(|msg| self.handle_message(msg)).await; - } - - async fn handle_message(&self, msg: FolderWSActorMessage) { - match msg { - FolderWSActorMessage::ClientData { - client_data, - persistence: _, - ret, - } => { - let _ = ret.send(self.handle_folder_data(client_data).await); - } - } - } - - async fn handle_folder_data(&self, client_data: WSClientData) -> Result<()> { - let WSClientData { user, socket, data } = client_data; - let folder_client_data = spawn_blocking(move || parse_from_bytes::(&data)) - .await - .map_err(internal_error)??; - - tracing::debug!( - "[FolderWebSocketActor]: receive: {}:{}, {:?}", - folder_client_data.object_id, - folder_client_data.data_id, - folder_client_data.ty - ); - - let user = Arc::new(FolderRevisionUser { user, socket }); - match &folder_client_data.ty { - ClientRevisionWSDataTypePB::ClientPushRev => { - let _ = self - .folder_manager - .handle_client_revisions(user, folder_client_data) - .await - .map_err(internal_error)?; - } - ClientRevisionWSDataTypePB::ClientPing => { - let _ = self - .folder_manager - .handle_client_ping(user, folder_client_data) - .await - .map_err(internal_error)?; - } - } - Ok(()) - } -} - -#[derive(Clone)] -pub struct FolderRevisionUser { - pub user: Arc, - pub(crate) socket: Socket, -} - -impl std::fmt::Debug for FolderRevisionUser { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - f.debug_struct("FolderRevisionUser") - .field("user", &self.user) - .field("socket", &self.socket) - .finish() - } -} - -impl RevisionUser for FolderRevisionUser { - fn user_id(&self) -> String { - self.user.id().to_string() - } - - fn receive(&self, resp: RevisionSyncResponse) { - let result = match resp { - RevisionSyncResponse::Pull(data) => { - let msg: WebSocketMessage = revision_data_to_ws_message(data, WSChannel::Folder); - self.socket.try_send(msg).map_err(internal_error) - } - RevisionSyncResponse::Push(data) => { - let msg: WebSocketMessage = revision_data_to_ws_message(data, WSChannel::Folder); - self.socket.try_send(msg).map_err(internal_error) - } - RevisionSyncResponse::Ack(data) => { - let msg: WebSocketMessage = revision_data_to_ws_message(data, WSChannel::Folder); - self.socket.try_send(msg).map_err(internal_error) - } - }; - - match result { - Ok(_) => {} - Err(e) => log::error!("[FolderRevisionUser]: {}", e), - } - } -} diff --git a/backend/src/services/folder/ws_receiver.rs b/backend/src/services/folder/ws_receiver.rs deleted file mode 100644 index 4d77ea7ce7..0000000000 --- a/backend/src/services/folder/ws_receiver.rs +++ /dev/null @@ -1,169 +0,0 @@ -use crate::{ - context::FlowyPersistence, - services::{ - folder::ws_actor::{FolderWSActorMessage, FolderWebSocketActor}, - web_socket::{WSClientData, WebSocketReceiver}, - }, -}; -use std::fmt::{Debug, Formatter}; - -use crate::{context::FolderRevisionKV, services::kv::revision_kv::revisions_to_key_value_items}; -use flowy_collaboration::{ - entities::folder_info::FolderInfo, - errors::CollaborateError, - protobuf::{RepeatedRevision as RepeatedRevisionPB, Revision as RevisionPB}, - server_folder::{FolderCloudPersistence, ServerFolderManager}, - util::make_folder_from_revisions_pb, -}; -use lib_infra::future::BoxResultFuture; -use std::sync::Arc; -use tokio::sync::{mpsc, oneshot}; - -pub fn make_folder_ws_receiver( - persistence: Arc, - folder_manager: Arc, -) -> Arc { - let (actor_msg_sender, rx) = tokio::sync::mpsc::channel(1000); - let actor = FolderWebSocketActor::new(rx, folder_manager); - tokio::task::spawn(actor.run()); - Arc::new(FolderWebSocketReceiver::new(persistence, actor_msg_sender)) -} - -pub struct FolderWebSocketReceiver { - actor_msg_sender: mpsc::Sender, - persistence: Arc, -} - -impl FolderWebSocketReceiver { - pub fn new(persistence: Arc, actor_msg_sender: mpsc::Sender) -> Self { - Self { - actor_msg_sender, - persistence, - } - } -} - -impl WebSocketReceiver for FolderWebSocketReceiver { - fn receive(&self, data: WSClientData) { - let (ret, rx) = oneshot::channel(); - let actor_msg_sender = self.actor_msg_sender.clone(); - let persistence = self.persistence.clone(); - - actix_rt::spawn(async move { - let msg = FolderWSActorMessage::ClientData { - client_data: data, - persistence, - ret, - }; - - match actor_msg_sender.send(msg).await { - Ok(_) => {} - Err(e) => { - log::error!("[FolderWebSocketReceiver]: send message to actor failed: {}", e); - } - } - match rx.await { - Ok(_) => {} - Err(e) => log::error!("[FolderWebSocketReceiver]: message ret failed {:?}", e), - }; - }); - } -} - -pub struct HttpFolderCloudPersistence(pub Arc); -impl Debug for HttpFolderCloudPersistence { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str("HttpFolderCloudPersistence") - } -} - -impl FolderCloudPersistence for HttpFolderCloudPersistence { - fn read_folder(&self, _user_id: &str, folder_id: &str) -> BoxResultFuture { - let folder_store = self.0.clone(); - let folder_id = folder_id.to_owned(); - Box::pin(async move { - let revisions = folder_store - .get_revisions(&folder_id, None) - .await - .map_err(|e| e.to_collaborate_error())?; - match make_folder_from_revisions_pb(&folder_id, revisions)? { - Some(folder_info) => Ok(folder_info), - None => Err(CollaborateError::record_not_found().context(format!("{} not exist", folder_id))), - } - }) - } - - fn create_folder( - &self, - _user_id: &str, - folder_id: &str, - mut repeated_revision: RepeatedRevisionPB, - ) -> BoxResultFuture, CollaborateError> { - let folder_store = self.0.clone(); - let folder_id = folder_id.to_owned(); - Box::pin(async move { - let folder_info = make_folder_from_revisions_pb(&folder_id, repeated_revision.clone())?; - let revisions: Vec = repeated_revision.take_items().into(); - let _ = folder_store - .set_revision(revisions) - .await - .map_err(|e| e.to_collaborate_error())?; - Ok(folder_info) - }) - } - - fn save_folder_revisions( - &self, - mut repeated_revision: RepeatedRevisionPB, - ) -> BoxResultFuture<(), CollaborateError> { - let folder_store = self.0.clone(); - Box::pin(async move { - let revisions: Vec = repeated_revision.take_items().into(); - let _ = folder_store - .set_revision(revisions) - .await - .map_err(|e| e.to_collaborate_error())?; - Ok(()) - }) - } - - fn read_folder_revisions( - &self, - folder_id: &str, - rev_ids: Option>, - ) -> BoxResultFuture, CollaborateError> { - let folder_store = self.0.clone(); - let folder_id = folder_id.to_owned(); - Box::pin(async move { - let mut repeated_revision = folder_store - .get_revisions(&folder_id, rev_ids) - .await - .map_err(|e| e.to_collaborate_error())?; - let revisions: Vec = repeated_revision.take_items().into(); - Ok(revisions) - }) - } - - fn reset_folder( - &self, - folder_id: &str, - mut repeated_revision: RepeatedRevisionPB, - ) -> BoxResultFuture<(), CollaborateError> { - let folder_store = self.0.clone(); - let folder_id = folder_id.to_owned(); - Box::pin(async move { - let _ = folder_store - .transaction(|mut transaction| { - Box::pin(async move { - let _ = transaction.batch_delete_key_start_with(&folder_id).await?; - let items = revisions_to_key_value_items(repeated_revision.take_items().into())?; - let _ = transaction.batch_set(items).await?; - Ok(()) - }) - }) - .await - .map_err(|e| e.to_collaborate_error())?; - Ok(()) - }) - } -} diff --git a/backend/src/services/kv/kv.rs b/backend/src/services/kv/kv.rs deleted file mode 100644 index 30189edf33..0000000000 --- a/backend/src/services/kv/kv.rs +++ /dev/null @@ -1,210 +0,0 @@ -use crate::{ - services::kv::{KVTransaction, KeyValue}, - util::sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder}, -}; -use anyhow::Context; -use async_trait::async_trait; -use backend_service::errors::ServerError; -use bytes::Bytes; - -use lib_infra::future::BoxResultFuture; -use sql_builder::SqlBuilder as RawSqlBuilder; -use sqlx::{ - postgres::{PgArguments, PgRow}, - Arguments, Error, PgPool, Postgres, Row, -}; - -const KV_TABLE: &str = "kv_table"; - -pub struct PostgresKV { - pub(crate) pg_pool: PgPool, -} - -impl PostgresKV { - pub async fn get(&self, key: &str) -> Result, ServerError> { - let key = key.to_owned(); - self.transaction(|mut transaction| Box::pin(async move { transaction.get(&key).await })) - .await - } - pub async fn set(&self, key: &str, value: Bytes) -> Result<(), ServerError> { - let key = key.to_owned(); - self.transaction(|mut transaction| Box::pin(async move { transaction.set(&key, value).await })) - .await - } - - pub async fn remove(&self, key: &str) -> Result<(), ServerError> { - let key = key.to_owned(); - self.transaction(|mut transaction| Box::pin(async move { transaction.remove(&key).await })) - .await - } - - pub async fn batch_set(&self, kvs: Vec) -> Result<(), ServerError> { - self.transaction(|mut transaction| Box::pin(async move { transaction.batch_set(kvs).await })) - .await - } - - pub async fn batch_get(&self, keys: Vec) -> Result, ServerError> { - self.transaction(|mut transaction| Box::pin(async move { transaction.batch_get(keys).await })) - .await - } - - pub async fn transaction(&self, f: F) -> Result - where - F: for<'a> FnOnce(Box) -> BoxResultFuture, - { - let mut transaction = self - .pg_pool - .begin() - .await - .context("[KV]:Failed to acquire a Postgres connection")?; - let postgres_transaction = PostgresTransaction(&mut transaction); - let result = f(Box::new(postgres_transaction)).await; - transaction - .commit() - .await - .context("[KV]:Failed to commit SQL transaction.")?; - - result - } -} - -pub(crate) struct PostgresTransaction<'a, 'b>(&'a mut DBTransaction<'b>); - -#[async_trait] -impl<'a, 'b> KVTransaction for PostgresTransaction<'a, 'b> { - async fn get(&mut self, key: &str) -> Result, ServerError> { - let id = key.to_string(); - let (sql, args) = SqlBuilder::select(KV_TABLE) - .add_field("*") - .and_where_eq("id", &id) - .build()?; - - let result = sqlx::query_as_with::(&sql, args) - .fetch_one(self.0 as &mut DBTransaction<'b>) - .await; - - match result { - Ok(val) => Ok(Some(Bytes::from(val.blob))), - Err(error) => match error { - Error::RowNotFound => Ok(None), - _ => Err(map_sqlx_error(error)), - }, - } - } - - async fn set(&mut self, key: &str, bytes: Bytes) -> Result<(), ServerError> { - self.batch_set(vec![KeyValue { - key: key.to_string(), - value: bytes, - }]) - .await - } - - async fn remove(&mut self, key: &str) -> Result<(), ServerError> { - let id = key.to_string(); - let (sql, args) = SqlBuilder::delete(KV_TABLE).and_where_eq("id", &id).build()?; - let _ = sqlx::query_with(&sql, args) - .execute(self.0 as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - Ok(()) - } - - async fn batch_set(&mut self, kvs: Vec) -> Result<(), ServerError> { - let mut builder = RawSqlBuilder::insert_into(KV_TABLE); - let m_builder = builder.field("id").field("blob"); - - let mut args = PgArguments::default(); - kvs.iter().enumerate().for_each(|(index, _)| { - let index = index * 2 + 1; - m_builder.values(&[format!("${}", index), format!("${}", index + 1)]); - }); - - for kv in kvs { - args.add(kv.key); - args.add(kv.value.to_vec()); - } - - let sql = m_builder.sql()?; - let _ = sqlx::query_with(&sql, args) - .execute(self.0 as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - - Ok::<(), ServerError>(()) - } - - async fn batch_get(&mut self, keys: Vec) -> Result, ServerError> { - let sql = RawSqlBuilder::select_from(KV_TABLE) - .field("id") - .field("blob") - .and_where_in_quoted("id", &keys) - .sql()?; - - let rows = sqlx::query(&sql) - .fetch_all(self.0 as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - let kvs = rows_to_key_values(rows); - Ok::, ServerError>(kvs) - } - - async fn batch_delete(&mut self, keys: Vec) -> Result<(), ServerError> { - let sql = RawSqlBuilder::delete_from(KV_TABLE).and_where_in("id", &keys).sql()?; - let _ = sqlx::query(&sql) - .execute(self.0 as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - - Ok::<(), ServerError>(()) - } - - async fn batch_get_start_with(&mut self, key: &str) -> Result, ServerError> { - let prefix = key.to_owned(); - let sql = RawSqlBuilder::select_from(KV_TABLE) - .field("id") - .field("blob") - .and_where_like_left("id", &prefix) - .sql()?; - - let rows = sqlx::query(&sql) - .fetch_all(self.0 as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - - let kvs = rows_to_key_values(rows); - - Ok::, ServerError>(kvs) - } - - async fn batch_delete_key_start_with(&mut self, keyword: &str) -> Result<(), ServerError> { - let keyword = keyword.to_owned(); - let sql = RawSqlBuilder::delete_from(KV_TABLE) - .and_where_like_left("id", &keyword) - .sql()?; - - let _ = sqlx::query(&sql) - .execute(self.0 as &mut DBTransaction<'_>) - .await - .map_err(map_sqlx_error)?; - Ok::<(), ServerError>(()) - } -} -fn rows_to_key_values(rows: Vec) -> Vec { - rows.into_iter() - .map(|row| { - let bytes: Vec = row.get("blob"); - KeyValue { - key: row.get("id"), - value: Bytes::from(bytes), - } - }) - .collect::>() -} - -#[derive(Debug, Clone, sqlx::FromRow)] -struct KVTable { - #[allow(dead_code)] - pub(crate) id: String, - pub(crate) blob: Vec, -} diff --git a/backend/src/services/kv/mod.rs b/backend/src/services/kv/mod.rs deleted file mode 100644 index 4a14383b61..0000000000 --- a/backend/src/services/kv/mod.rs +++ /dev/null @@ -1,41 +0,0 @@ -#![allow(clippy::module_inception)] -mod kv; -pub mod revision_kv; - -use async_trait::async_trait; -use bytes::Bytes; - -pub(crate) use kv::*; - -use backend_service::errors::ServerError; - -// TODO: Generic the KVStore that enable switching KVStore to another -// implementation -pub type KVStore = PostgresKV; - -#[rustfmt::skip] -// https://rust-lang.github.io/async-book/07_workarounds/05_async_in_traits.html -// Note that using these trait methods will result in a heap allocation -// per-function-call. This is not a significant cost for the vast majority of -// applications, but should be considered when deciding whether to use this -// functionality in the public API of a low-level function that is expected to -// be called millions of times a second. -#[async_trait] -pub trait KVTransaction: Send + Sync { - async fn get(&mut self, key: &str) -> Result, ServerError>; - async fn set(&mut self, key: &str, value: Bytes) -> Result<(), ServerError>; - async fn remove(&mut self, key: &str) -> Result<(), ServerError>; - - async fn batch_set(&mut self, kvs: Vec) -> Result<(), ServerError>; - async fn batch_get(&mut self, keys: Vec) -> Result, ServerError>; - async fn batch_delete(&mut self, keys: Vec) -> Result<(), ServerError>; - - async fn batch_get_start_with(&mut self, key: &str) -> Result, ServerError>; - async fn batch_delete_key_start_with(&mut self, keyword: &str) -> Result<(), ServerError>; -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct KeyValue { - pub key: String, - pub value: Bytes, -} diff --git a/backend/src/services/kv/revision_kv.rs b/backend/src/services/kv/revision_kv.rs deleted file mode 100644 index c263d84e97..0000000000 --- a/backend/src/services/kv/revision_kv.rs +++ /dev/null @@ -1,128 +0,0 @@ -use crate::{ - services::kv::{KVStore, KeyValue}, - util::serde_ext::parse_from_bytes, -}; -use backend_service::errors::ServerError; -use bytes::Bytes; -use flowy_collaboration::protobuf::{RepeatedRevision as RepeatedRevisionPB, Revision as RevisionPB}; - -use protobuf::Message; -use std::sync::Arc; - -pub struct RevisionKVPersistence { - inner: Arc, -} - -impl std::ops::Deref for RevisionKVPersistence { - type Target = Arc; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -impl std::ops::DerefMut for RevisionKVPersistence { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.inner - } -} - -impl RevisionKVPersistence { - pub(crate) fn new(kv_store: Arc) -> Self { - RevisionKVPersistence { inner: kv_store } - } - - pub(crate) async fn set_revision(&self, revisions: Vec) -> Result<(), ServerError> { - let items = revisions_to_key_value_items(revisions)?; - self.inner - .transaction(|mut t| Box::pin(async move { t.batch_set(items).await })) - .await - } - - pub(crate) async fn get_revisions>>>( - &self, - object_id: &str, - rev_ids: T, - ) -> Result { - let rev_ids = rev_ids.into(); - let items = match rev_ids { - None => { - let object_id = object_id.to_owned(); - self.inner - .transaction(|mut t| Box::pin(async move { t.batch_get_start_with(&object_id).await })) - .await? - } - Some(rev_ids) => { - let keys = rev_ids - .into_iter() - .map(|rev_id| make_revision_key(object_id, rev_id)) - .collect::>(); - - self.inner - .transaction(|mut t| Box::pin(async move { t.batch_get(keys).await })) - .await? - } - }; - - Ok(key_value_items_to_revisions(items)) - } - - pub(crate) async fn delete_revisions>>>( - &self, - object_id: &str, - rev_ids: T, - ) -> Result<(), ServerError> { - match rev_ids.into() { - None => { - let object_id = object_id.to_owned(); - self.inner - .transaction(|mut t| Box::pin(async move { t.batch_delete_key_start_with(&object_id).await })) - .await - } - Some(rev_ids) => { - let keys = rev_ids - .into_iter() - .map(|rev_id| make_revision_key(object_id, rev_id)) - .collect::>(); - - self.inner - .transaction(|mut t| Box::pin(async move { t.batch_delete(keys).await })) - .await - } - } - } -} - -#[inline] -pub fn revisions_to_key_value_items(revisions: Vec) -> Result, ServerError> { - let mut items = vec![]; - for revision in revisions { - let key = make_revision_key(&revision.object_id, revision.rev_id); - - if revision.delta_data.is_empty() { - return Err(ServerError::internal().context("The delta_data of RevisionPB should not be empty")); - } - - let value = Bytes::from(revision.write_to_bytes().unwrap()); - items.push(KeyValue { key, value }); - } - Ok(items) -} - -#[inline] -fn key_value_items_to_revisions(items: Vec) -> RepeatedRevisionPB { - let mut revisions = items - .into_iter() - .filter_map(|kv| parse_from_bytes::(&kv.value).ok()) - .collect::>(); - - revisions.sort_by(|a, b| a.rev_id.cmp(&b.rev_id)); - let mut repeated_revision = RepeatedRevisionPB::new(); - repeated_revision.set_items(revisions.into()); - repeated_revision -} - -#[inline] -fn make_revision_key(object_id: &str, rev_id: i64) -> String { - format!("{}:{}", object_id, rev_id) -} diff --git a/backend/src/services/log.rs b/backend/src/services/log.rs deleted file mode 100644 index 3036234604..0000000000 --- a/backend/src/services/log.rs +++ /dev/null @@ -1,50 +0,0 @@ -use log::LevelFilter; - -use tracing::subscriber::set_global_default; - -use tracing_bunyan_formatter::{BunyanFormattingLayer, JsonStorageLayer}; -use tracing_log::LogTracer; -use tracing_subscriber::{layer::SubscriberExt, EnvFilter}; - -pub struct Builder { - name: String, - env_filter: String, -} - -impl Builder { - pub fn new(name: &str) -> Self { - Builder { - name: name.to_owned(), - env_filter: "Info".to_owned(), - } - } - - pub fn env_filter(mut self, env_filter: &str) -> Self { - self.env_filter = env_filter.to_owned(); - self - } - - pub fn build(self) -> std::result::Result<(), String> { - let env_filter = EnvFilter::new(self.env_filter); - let subscriber = tracing_subscriber::fmt() - .with_target(true) - .with_max_level(tracing::Level::TRACE) - .with_writer(std::io::stderr) - .with_thread_ids(true) - .compact() - .finish() - .with(env_filter); - - let formatting_layer = BunyanFormattingLayer::new(self.name, std::io::stdout); - let _ = set_global_default(subscriber.with(JsonStorageLayer).with(formatting_layer)) - .map_err(|e| format!("{:?}", e))?; - - let _ = LogTracer::builder() - .with_max_level(LevelFilter::Debug) - .init() - .map_err(|e| format!("{:?}", e)) - .unwrap(); - - Ok(()) - } -} diff --git a/backend/src/services/mod.rs b/backend/src/services/mod.rs deleted file mode 100644 index 28905e9684..0000000000 --- a/backend/src/services/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod document; -pub mod folder; -pub mod kv; -pub(crate) mod log; -pub mod user; -pub mod web_socket; diff --git a/backend/src/services/user/controller.rs b/backend/src/services/user/controller.rs deleted file mode 100644 index 7930f06ed1..0000000000 --- a/backend/src/services/user/controller.rs +++ /dev/null @@ -1,236 +0,0 @@ -use crate::{ - entities::{ - logged_user::{LoggedUser, AUTHORIZED_USERS}, - token::Token, - user::UserTable, - }, - util::{ - sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder}, - user_ext::{hash_password, verify_password}, - }, -}; -use anyhow::Context; -use backend_service::{ - errors::{invalid_params, ErrorCode, ServerError}, - response::FlowyResponse, -}; -use chrono::Utc; -use flowy_user_data_model::{ - parser::{UserEmail, UserName, UserPassword}, - protobuf::{ - SignInParams as SignInParamsPB, SignInResponse as SignInResponsePB, SignUpParams as SignUpParamsPB, - SignUpResponse as SignUpResponsePB, UpdateUserParams as UpdateUserParamsPB, UserProfile as UserProfilePB, - }, -}; -use sqlx::{PgPool, Postgres}; - -pub async fn sign_in(pool: &PgPool, params: SignInParamsPB) -> Result { - let email = UserEmail::parse(params.email).map_err(|e| ServerError::params_invalid().context(e))?; - let password = UserPassword::parse(params.password).map_err(|e| ServerError::params_invalid().context(e))?; - - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to sign in")?; - - let user = check_user_password(&mut transaction, email.as_ref(), password.as_ref()).await?; - transaction - .commit() - .await - .context("Failed to commit SQL transaction to sign in.")?; - - let token = Token::create_token(&user.id.to_string())?; - let logged_user = LoggedUser::new(&user.id.to_string()); - - AUTHORIZED_USERS.store_auth(logged_user, true); - let mut response_data = SignInResponsePB::default(); - response_data.set_user_id(user.id.to_string()); - response_data.set_name(user.name); - response_data.set_email(user.email); - response_data.set_token(token.into()); - - Ok(response_data) -} - -pub async fn sign_out(logged_user: LoggedUser) -> Result { - AUTHORIZED_USERS.store_auth(logged_user, false); - Ok(FlowyResponse::success()) -} - -pub async fn register_user(pool: &PgPool, params: SignUpParamsPB) -> Result { - let name = UserName::parse(params.name).map_err(|e| ServerError::params_invalid().context(e))?; - let email = UserEmail::parse(params.email).map_err(|e| ServerError::params_invalid().context(e))?; - let password = UserPassword::parse(params.password).map_err(|e| ServerError::params_invalid().context(e))?; - - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to register user")?; - - let _ = is_email_exist(&mut transaction, email.as_ref()).await?; - let response_data = insert_new_user(&mut transaction, name.as_ref(), email.as_ref(), password.as_ref()) - .await - .context("Failed to insert user")?; - - let logged_user = LoggedUser::new(&response_data.user_id); - AUTHORIZED_USERS.store_auth(logged_user, true); - - transaction - .commit() - .await - .context("Failed to commit SQL transaction to register user.")?; - - FlowyResponse::success().pb(response_data) -} - -pub(crate) async fn get_user_profile( - pool: &PgPool, - token: Token, - logged_user: LoggedUser, -) -> Result { - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to get user detail")?; - - let id = logged_user.as_uuid()?; - let user_table = sqlx::query_as::("SELECT * FROM user_table WHERE id = $1") - .bind(id) - .fetch_one(&mut transaction) - .await - .map_err(|err| ServerError::internal().context(err))?; - - transaction - .commit() - .await - .context("Failed to commit SQL transaction to get user detail.")?; - - // update the user active time - AUTHORIZED_USERS.store_auth(logged_user, true); - - let mut user_profile = UserProfilePB::default(); - user_profile.set_id(user_table.id.to_string()); - user_profile.set_email(user_table.email); - user_profile.set_name(user_table.name); - user_profile.set_token(token.0); - FlowyResponse::success().pb(user_profile) -} - -pub(crate) async fn set_user_profile( - pool: &PgPool, - logged_user: LoggedUser, - params: UpdateUserParamsPB, -) -> Result { - let mut transaction = pool - .begin() - .await - .context("Failed to acquire a Postgres connection to update user profile")?; - - let name = match params.has_name() { - false => None, - true => Some(UserName::parse(params.get_name().to_owned()).map_err(invalid_params)?.0), - }; - - let email = match params.has_email() { - false => None, - true => Some( - UserEmail::parse(params.get_email().to_owned()) - .map_err(invalid_params)? - .0, - ), - }; - - let password = match params.has_password() { - false => None, - true => { - let password = UserPassword::parse(params.get_password().to_owned()).map_err(invalid_params)?; - let password = hash_password(password.as_ref())?; - Some(password) - } - }; - - let (sql, args) = SqlBuilder::update("user_table") - .add_some_arg("name", name) - .add_some_arg("email", email) - .add_some_arg("password", password) - .and_where_eq("id", &logged_user.as_uuid()?) - .build()?; - - sqlx::query_with(&sql, args) - .execute(&mut transaction) - .await - .map_err(map_sqlx_error)?; - - transaction - .commit() - .await - .context("Failed to commit SQL transaction to update user profile.")?; - - Ok(FlowyResponse::success()) -} - -async fn is_email_exist(transaction: &mut DBTransaction<'_>, email: &str) -> Result<(), ServerError> { - let result = sqlx::query(r#"SELECT email FROM user_table WHERE email = $1"#) - .bind(email) - .fetch_optional(transaction) - .await - .map_err(|err| ServerError::internal().context(err))?; - - match result { - Some(_) => Err(ServerError { - code: ErrorCode::EmailAlreadyExists, - msg: format!("{} already exists", email), - }), - None => Ok(()), - } -} - -async fn check_user_password( - transaction: &mut DBTransaction<'_>, - email: &str, - password: &str, -) -> Result { - let user = sqlx::query_as::("SELECT * FROM user_table WHERE email = $1") - .bind(email) - .fetch_one(transaction) - .await - .map_err(|err| ServerError::internal().context(err))?; - - match verify_password(password, &user.password) { - Ok(true) => Ok(user), - _ => Err(ServerError::password_not_match()), - } -} - -async fn insert_new_user( - transaction: &mut DBTransaction<'_>, - name: &str, - email: &str, - password: &str, -) -> Result { - let uuid = uuid::Uuid::new_v4(); - let token = Token::create_token(&uuid.to_string())?; - let password = hash_password(password)?; - let _ = sqlx::query!( - r#" - INSERT INTO user_table (id, email, name, create_time, password) - VALUES ($1, $2, $3, $4, $5) - "#, - uuid, - email, - name, - Utc::now(), - password, - ) - .execute(transaction) - .await - .map_err(|e| ServerError::internal().context(e))?; - - let mut response = SignUpResponsePB::default(); - response.set_user_id(uuid.to_string()); - response.set_name(name.to_string()); - response.set_email(email.to_string()); - response.set_token(token.into()); - - Ok(response) -} diff --git a/backend/src/services/user/mod.rs b/backend/src/services/user/mod.rs deleted file mode 100644 index d3c1ce98a2..0000000000 --- a/backend/src/services/user/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub use controller::*; - -mod controller; -pub mod router; diff --git a/backend/src/services/user/router.rs b/backend/src/services/user/router.rs deleted file mode 100644 index 52efbe4dfa..0000000000 --- a/backend/src/services/user/router.rs +++ /dev/null @@ -1,64 +0,0 @@ -use crate::{ - entities::{logged_user::LoggedUser, token::Token}, - services::user::{get_user_profile, register_user, set_user_profile, sign_in, sign_out}, - util::serde_ext::parse_from_payload, -}; -use actix_identity::Identity; -use actix_web::{ - web::{Data, Payload}, - HttpRequest, HttpResponse, -}; -use backend_service::{errors::ServerError, response::FlowyResponse}; -use flowy_user_data_model::protobuf::{ - SignInParams as SignInParamsPB, SignUpParams as SignUpParamsPB, UpdateUserParams as UpdateUserParamsPB, -}; -use sqlx::PgPool; - -pub async fn sign_in_handler(payload: Payload, id: Identity, pool: Data) -> Result { - let params: SignInParamsPB = parse_from_payload(payload).await?; - let data = sign_in(pool.get_ref(), params).await?; - id.remember(data.token.clone()); - let response = FlowyResponse::success().pb(data)?; - Ok(response.into()) -} - -pub async fn sign_out_handler(logged_user: LoggedUser, id: Identity) -> Result { - id.forget(); - - let response = sign_out(logged_user).await?; - Ok(response.into()) -} - -pub async fn get_user_profile_handler( - token: Token, - logged_user: LoggedUser, - pool: Data, -) -> Result { - let response = get_user_profile(pool.get_ref(), token, logged_user).await?; - Ok(response.into()) -} - -pub async fn set_user_profile_handler( - logged_user: LoggedUser, - pool: Data, - payload: Payload, -) -> Result { - let params: UpdateUserParamsPB = parse_from_payload(payload).await?; - let response = set_user_profile(pool.get_ref(), logged_user, params).await?; - Ok(response.into()) -} - -pub async fn register_handler(payload: Payload, pool: Data) -> Result { - let params: SignUpParamsPB = parse_from_payload(payload).await?; - let resp = register_user(pool.get_ref(), params).await?; - - Ok(resp.into()) -} - -pub async fn change_password( - _request: HttpRequest, - _payload: Payload, - _pool: Data, -) -> Result { - unimplemented!() -} diff --git a/backend/src/services/web_socket/entities/connect.rs b/backend/src/services/web_socket/entities/connect.rs deleted file mode 100644 index c083950e45..0000000000 --- a/backend/src/services/web_socket/entities/connect.rs +++ /dev/null @@ -1,50 +0,0 @@ -use crate::services::web_socket::WebSocketMessage; -use actix::{Message, Recipient}; -use backend_service::errors::ServerError; -use serde::{Deserialize, Serialize}; -use std::fmt::Formatter; - -pub type Socket = Recipient; - -#[derive(Serialize, Deserialize, Debug, Clone, Hash, PartialEq, Eq)] -pub struct SessionId(pub String); - -impl> std::convert::From for SessionId { - fn from(s: T) -> Self { - SessionId(s.as_ref().to_owned()) - } -} - -impl std::fmt::Display for SessionId { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let desc = &self.0.to_string(); - f.write_str(desc) - } -} - -pub struct Session { - pub id: SessionId, - pub socket: Socket, -} - -impl std::convert::From for Session { - fn from(c: Connect) -> Self { - Self { - id: c.sid, - socket: c.socket, - } - } -} - -#[derive(Debug, Message, Clone)] -#[rtype(result = "Result<(), ServerError>")] -pub struct Connect { - pub socket: Socket, - pub sid: SessionId, -} - -#[derive(Debug, Message, Clone)] -#[rtype(result = "Result<(), ServerError>")] -pub struct Disconnect { - pub sid: SessionId, -} diff --git a/backend/src/services/web_socket/entities/message.rs b/backend/src/services/web_socket/entities/message.rs deleted file mode 100644 index 82f9cc1155..0000000000 --- a/backend/src/services/web_socket/entities/message.rs +++ /dev/null @@ -1,27 +0,0 @@ -use actix::Message; -use bytes::Bytes; -use flowy_collaboration::entities::ws_data::ServerRevisionWSData; -use lib_ws::{WSChannel, WebSocketRawMessage}; -use std::convert::TryInto; - -#[derive(Debug, Message, Clone)] -#[rtype(result = "()")] -pub struct WebSocketMessage(pub Bytes); - -impl std::ops::Deref for WebSocketMessage { - type Target = Bytes; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -pub fn revision_data_to_ws_message(data: ServerRevisionWSData, channel: WSChannel) -> WebSocketMessage { - let bytes: Bytes = data.try_into().unwrap(); - let msg = WebSocketRawMessage { - channel, - data: bytes.to_vec(), - }; - let bytes: Bytes = msg.try_into().unwrap(); - WebSocketMessage(bytes) -} diff --git a/backend/src/services/web_socket/entities/mod.rs b/backend/src/services/web_socket/entities/mod.rs deleted file mode 100644 index 5b05781f6b..0000000000 --- a/backend/src/services/web_socket/entities/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub use connect::*; -pub use message::*; - -mod connect; -pub mod message; diff --git a/backend/src/services/web_socket/mod.rs b/backend/src/services/web_socket/mod.rs deleted file mode 100644 index da27353f58..0000000000 --- a/backend/src/services/web_socket/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -pub use entities::message::*; -pub use ws_client::*; -pub use ws_server::*; - -pub(crate) mod entities; -pub mod router; -mod ws_client; -mod ws_server; diff --git a/backend/src/services/web_socket/router.rs b/backend/src/services/web_socket/router.rs deleted file mode 100644 index ee9c61cf45..0000000000 --- a/backend/src/services/web_socket/router.rs +++ /dev/null @@ -1,61 +0,0 @@ -use crate::{ - entities::logged_user::LoggedUser, - services::web_socket::{WSClient, WSServer, WSUser, WebSocketReceivers}, -}; -use actix::Addr; -use actix_web::{ - get, - web::{Data, Path, Payload}, - Error, HttpRequest, HttpResponse, -}; -use actix_web_actors::ws; - -#[rustfmt::skip] -// WsClient -// ┌─────────────┐ -// │ ┌────────┐ │ -// wss://xxx ─────▶│ │ WsUser │ │───┐ -// │ └────────┘ │ │ -// └─────────────┘ │ -// │ -// │ ┌──────────────────┐ 1 n ┌──────────────────┐ -// ├───▶│WebSocketReceivers│◆────│WebSocketReceiver │ -// │ └──────────────────┘ └──────────────────┘ -// WsClient │ △ -// ┌─────────────┐ │ │ -// │ ┌────────┐ │ │ │ -// wss://xxx ─────▶│ │ WsUser │ │───┘ │ -// │ └────────┘ │ ┌───────────────┐ -// └─────────────┘ │DocumentManager│ -// └───────────────┘ -#[get("/{token}")] -pub async fn establish_ws_connection( - request: HttpRequest, - payload: Payload, - token: Path, - server: Data>, - ws_receivers: Data, -) -> Result { - tracing::info!("establish_ws_connection"); - match LoggedUser::from_token(token.clone()) { - Ok(user) => { - let ws_user = WSUser::new(user); - let client = WSClient::new(ws_user, server.get_ref().clone(), ws_receivers); - let result = ws::start(client, &request, payload); - match result { - Ok(response) => Ok(response), - Err(e) => { - log::error!("ws connection error: {:?}", e); - Err(e) - }, - } - }, - Err(e) => { - if e.is_unauthorized() { - Ok(HttpResponse::Unauthorized().json(e)) - } else { - Ok(HttpResponse::BadRequest().json(e)) - } - }, - } -} diff --git a/backend/src/services/web_socket/ws_client.rs b/backend/src/services/web_socket/ws_client.rs deleted file mode 100644 index 46eb105b13..0000000000 --- a/backend/src/services/web_socket/ws_client.rs +++ /dev/null @@ -1,181 +0,0 @@ -use crate::{ - config::{HEARTBEAT_INTERVAL, PING_TIMEOUT}, - entities::logged_user::LoggedUser, - services::web_socket::{ - entities::{Connect, Disconnect, Socket}, - WSServer, WebSocketMessage, - }, -}; -use actix::*; -use actix_web::web::Data; -use actix_web_actors::{ws, ws::Message::Text}; -use bytes::Bytes; -use lib_ws::{WSChannel, WebSocketRawMessage}; -use std::{collections::HashMap, convert::TryFrom, sync::Arc, time::Instant}; - -pub trait WebSocketReceiver: Send + Sync { - fn receive(&self, data: WSClientData); -} - -#[derive(Default)] -pub struct WebSocketReceivers { - inner: HashMap>, -} - -impl WebSocketReceivers { - pub fn new() -> Self { - WebSocketReceivers::default() - } - - pub fn set(&mut self, channel: WSChannel, receiver: Arc) { - tracing::trace!("Add {:?} receiver", channel); - self.inner.insert(channel, receiver); - } - - pub fn get(&self, source: &WSChannel) -> Option> { - self.inner.get(source).cloned() - } -} - -#[derive(Debug)] -pub struct WSUser { - inner: LoggedUser, -} - -impl WSUser { - pub fn new(inner: LoggedUser) -> Self { - Self { inner } - } - - pub fn id(&self) -> &str { - &self.inner.user_id - } -} - -pub struct WSClientData { - pub(crate) user: Arc, - pub(crate) socket: Socket, - pub(crate) data: Bytes, -} - -pub struct WSClient { - user: Arc, - server: Addr, - ws_receivers: Data, - hb: Instant, -} - -impl WSClient { - pub fn new(user: WSUser, server: Addr, ws_receivers: Data) -> Self { - Self { - user: Arc::new(user), - server, - ws_receivers, - hb: Instant::now(), - } - } - - fn hb(&self, ctx: &mut ws::WebsocketContext) { - ctx.run_interval(HEARTBEAT_INTERVAL, |client, ctx| { - if Instant::now().duration_since(client.hb) > PING_TIMEOUT { - client.server.do_send(Disconnect { - sid: client.user.id().into(), - }); - ctx.stop(); - } else { - ctx.ping(b""); - } - }); - } - - fn handle_binary_message(&self, bytes: Bytes, socket: Socket) { - // TODO: ok to unwrap? - let message: WebSocketRawMessage = WebSocketRawMessage::try_from(bytes).unwrap(); - match self.ws_receivers.get(&message.channel) { - None => { - log::error!("Can't find the receiver for {:?}", message.channel); - } - Some(handler) => { - let client_data = WSClientData { - user: self.user.clone(), - socket, - data: Bytes::from(message.data), - }; - handler.receive(client_data); - } - } - } -} - -impl StreamHandler> for WSClient { - fn handle(&mut self, msg: Result, ctx: &mut Self::Context) { - match msg { - Ok(ws::Message::Ping(msg)) => { - self.hb = Instant::now(); - ctx.pong(&msg); - } - Ok(ws::Message::Pong(_msg)) => { - // tracing::debug!("Receive {} pong {:?}", &self.session_id, &msg); - self.hb = Instant::now(); - } - Ok(ws::Message::Binary(bytes)) => { - let socket = ctx.address().recipient(); - self.handle_binary_message(bytes, socket); - } - Ok(Text(_)) => { - log::warn!("Receive unexpected text message"); - } - Ok(ws::Message::Close(reason)) => { - ctx.close(reason); - ctx.stop(); - } - Ok(ws::Message::Continuation(_)) => {} - Ok(ws::Message::Nop) => {} - Err(e) => { - log::error!("[{}]: WebSocketStream protocol error {:?}", self.user.id(), e); - ctx.stop(); - } - } - } -} - -impl Handler for WSClient { - type Result = (); - - fn handle(&mut self, msg: WebSocketMessage, ctx: &mut Self::Context) { - ctx.binary(msg.0); - } -} - -impl Actor for WSClient { - type Context = ws::WebsocketContext; - - fn started(&mut self, ctx: &mut Self::Context) { - self.hb(ctx); - let socket = ctx.address().recipient(); - let connect = Connect { - socket, - sid: self.user.id().into(), - }; - self.server - .send(connect) - .into_actor(self) - .then(|res, _client, _ctx| { - match res { - Ok(Ok(_)) => tracing::trace!("Send connect message to server success"), - Ok(Err(e)) => log::error!("Send connect message to server failed: {:?}", e), - Err(e) => log::error!("Send connect message to server failed: {:?}", e), - } - fut::ready(()) - }) - .wait(ctx); - } - - fn stopping(&mut self, _: &mut Self::Context) -> Running { - self.server.do_send(Disconnect { - sid: self.user.id().into(), - }); - - Running::Stop - } -} diff --git a/backend/src/services/web_socket/ws_server.rs b/backend/src/services/web_socket/ws_server.rs deleted file mode 100644 index af69c0f4d8..0000000000 --- a/backend/src/services/web_socket/ws_server.rs +++ /dev/null @@ -1,65 +0,0 @@ -use crate::services::web_socket::{ - entities::{Connect, Disconnect, Session, SessionId}, - WebSocketMessage, -}; -use actix::{Actor, Context, Handler}; -use backend_service::errors::ServerError; -use dashmap::DashMap; - -pub struct WSServer { - sessions: DashMap, -} - -impl std::default::Default for WSServer { - fn default() -> Self { - Self { - sessions: DashMap::new(), - } - } -} -impl WSServer { - pub fn new() -> Self { - WSServer::default() - } - - pub fn send(&self, _msg: WebSocketMessage) { - unimplemented!() - } -} - -impl Actor for WSServer { - type Context = Context; - fn started(&mut self, _ctx: &mut Self::Context) {} -} - -impl Handler for WSServer { - type Result = Result<(), ServerError>; - fn handle(&mut self, msg: Connect, _ctx: &mut Context) -> Self::Result { - let session: Session = msg.into(); - self.sessions.insert(session.id.clone(), session); - - Ok(()) - } -} - -impl Handler for WSServer { - type Result = Result<(), ServerError>; - fn handle(&mut self, msg: Disconnect, _: &mut Context) -> Self::Result { - self.sessions.remove(&msg.sid); - Ok(()) - } -} - -impl Handler for WSServer { - type Result = (); - - fn handle(&mut self, _msg: WebSocketMessage, _ctx: &mut Context) -> Self::Result { - unimplemented!() - } -} - -impl actix::Supervised for WSServer { - fn restarting(&mut self, _ctx: &mut Context) { - log::warn!("restarting"); - } -} diff --git a/backend/src/util/mod.rs b/backend/src/util/mod.rs deleted file mode 100644 index c4236c6ebf..0000000000 --- a/backend/src/util/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod serde_ext; -pub mod sqlx_ext; -pub mod user_ext; diff --git a/backend/src/util/serde_ext.rs b/backend/src/util/serde_ext.rs deleted file mode 100644 index 04b791ca11..0000000000 --- a/backend/src/util/serde_ext.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::config::MAX_PAYLOAD_SIZE; -use actix_web::web; -use backend_service::errors::{ErrorCode, ServerError}; -use futures::StreamExt; -use protobuf::{Message, ProtobufResult}; - -pub async fn parse_from_payload(payload: web::Payload) -> Result { - let bytes = poll_payload(&mut payload.into_inner()).await?; - parse_from_bytes(&bytes) -} - -#[allow(dead_code)] -pub async fn parse_from_dev_payload(payload: &mut actix_web::dev::Payload) -> Result { - let bytes = poll_payload(payload).await?; - parse_from_bytes(&bytes) -} - -#[inline] -pub fn md5>(data: T) -> String { - let md5 = format!("{:x}", md5::compute(data)); - md5 -} - -pub fn parse_from_bytes(bytes: &[u8]) -> Result { - let result: ProtobufResult = Message::parse_from_bytes(bytes); - match result { - Ok(data) => Ok(data), - Err(e) => Err(e.into()), - } -} - -pub async fn poll_payload(payload: &mut actix_web::dev::Payload) -> Result { - let mut body = web::BytesMut::new(); - while let Some(chunk) = payload.next().await { - let chunk = chunk.map_err(|err| ServerError::internal().context(err))?; - - if (body.len() + chunk.len()) > MAX_PAYLOAD_SIZE { - return Err(ServerError::new( - "Payload overflow".to_string(), - ErrorCode::PayloadOverflow, - )); - } - body.extend_from_slice(&chunk); - } - Ok(body) -} diff --git a/backend/src/util/sqlx_ext/mod.rs b/backend/src/util/sqlx_ext/mod.rs deleted file mode 100644 index efb620b253..0000000000 --- a/backend/src/util/sqlx_ext/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -mod query; -mod utils; - -pub use utils::*; - -pub use query::*; diff --git a/backend/src/util/sqlx_ext/query.rs b/backend/src/util/sqlx_ext/query.rs deleted file mode 100644 index 99414b71b9..0000000000 --- a/backend/src/util/sqlx_ext/query.rs +++ /dev/null @@ -1,159 +0,0 @@ -use backend_service::errors::ServerError; -use sql_builder::SqlBuilder as InnerBuilder; -use sqlx::{postgres::PgArguments, Arguments, Encode, Postgres, Type}; - -enum BuilderType { - Create, - Select, - Update, - Delete, -} - -pub struct SqlBuilder { - table: String, - fields: Vec, - filters: Vec, - fields_args: PgArguments, - ty: BuilderType, -} - -impl SqlBuilder { - fn new(table: &str) -> Self { - Self { - table: table.to_owned(), - fields: vec![], - filters: vec![], - fields_args: PgArguments::default(), - ty: BuilderType::Select, - } - } - - pub fn create(table: &str) -> Self { - let mut builder = Self::new(table); - builder.ty = BuilderType::Create; - builder - } - - pub fn select(table: &str) -> Self { - let mut builder = Self::new(table); - builder.ty = BuilderType::Select; - builder - } - - pub fn update(table: &str) -> Self { - let mut builder = Self::new(table); - builder.ty = BuilderType::Update; - builder - } - - pub fn delete(table: &str) -> Self { - let mut builder = Self::new(table); - builder.ty = BuilderType::Delete; - builder - } - - pub fn add_field_with_arg<'a, T>(mut self, field: &str, arg: T) -> Self - where - T: 'a + Send + Encode<'a, Postgres> + Type, - { - self.fields.push(field.to_owned()); - self.fields_args.add(arg); - self - } - - #[allow(dead_code)] - pub fn add_arg_if<'a, T>(self, add: bool, field: &str, arg: T) -> Self - where - T: 'a + Send + Encode<'a, Postgres> + Type, - { - if add { - self.add_field_with_arg(field, arg) - } else { - self - } - } - - pub fn add_some_arg<'a, T>(self, field: &str, arg: Option) -> Self - where - T: 'a + Send + Encode<'a, Postgres> + Type, - { - if let Some(arg) = arg { - self.add_field_with_arg(field, arg) - } else { - self - } - } - - pub fn add_field(mut self, field: &str) -> Self { - self.fields.push(field.to_owned()); - self - } - - pub fn and_where_eq<'a, T>(mut self, field: &str, arg: T) -> Self - where - T: 'a + Send + Encode<'a, Postgres> + Type, - { - self.filters.push(field.to_owned()); - self.fields_args.add(arg); - self - } - - pub fn build(self) -> Result<(String, PgArguments), ServerError> { - match self.ty { - BuilderType::Create => { - let mut inner = InnerBuilder::insert_into(&self.table); - self.fields.iter().for_each(|field| { - inner.field(field); - }); - - let values = self - .fields - .iter() - .enumerate() - .map(|(index, _)| format!("${}", index + 1)) - .collect::>(); - - inner.values(&values); - - let sql = inner.sql()?; - Ok((sql, self.fields_args)) - } - BuilderType::Select => { - let mut inner = InnerBuilder::select_from(&self.table); - self.fields.into_iter().for_each(|field| { - inner.field(field); - }); - - self.filters.into_iter().enumerate().for_each(|(index, filter)| { - inner.and_where_eq(filter, format!("${}", index + 1)); - }); - - let sql = inner.sql()?; - Ok((sql, self.fields_args)) - } - BuilderType::Update => { - let mut inner = InnerBuilder::update_table(&self.table); - let field_len = self.fields.len(); - self.fields.into_iter().enumerate().for_each(|(index, field)| { - inner.set(&field, format!("${}", index + 1)); - }); - - self.filters.into_iter().enumerate().for_each(|(index, filter)| { - let index = index + field_len; - inner.and_where_eq(filter, format!("${}", index + 1)); - }); - - let sql = inner.sql()?; - Ok((sql, self.fields_args)) - } - BuilderType::Delete => { - let mut inner = InnerBuilder::delete_from(&self.table); - self.filters.into_iter().enumerate().for_each(|(index, filter)| { - inner.and_where_eq(filter, format!("${}", index + 1)); - }); - let sql = inner.sql()?; - Ok((sql, self.fields_args)) - } - } - } -} diff --git a/backend/src/util/sqlx_ext/utils.rs b/backend/src/util/sqlx_ext/utils.rs deleted file mode 100644 index 0b0d69b6e4..0000000000 --- a/backend/src/util/sqlx_ext/utils.rs +++ /dev/null @@ -1,11 +0,0 @@ -use backend_service::errors::{ErrorCode, ServerError}; -use sqlx::{Error, Postgres, Transaction}; - -pub type DBTransaction<'a> = Transaction<'a, Postgres>; - -pub fn map_sqlx_error(error: sqlx::Error) -> ServerError { - match error { - Error::RowNotFound => ServerError::new("".to_string(), ErrorCode::RecordNotFound), - _ => ServerError::internal().context(error), - } -} diff --git a/backend/src/util/user_ext.rs b/backend/src/util/user_ext.rs deleted file mode 100644 index 5d6e8c8867..0000000000 --- a/backend/src/util/user_ext.rs +++ /dev/null @@ -1,31 +0,0 @@ -use backend_service::errors::{ErrorCode, ServerError}; -use bcrypt::{hash, verify, DEFAULT_COST}; - -#[allow(dead_code)] -pub fn uuid() -> String { - uuid::Uuid::new_v4().to_string() -} - -pub fn hash_password(plain: &str) -> Result { - let hashing_cost = std::env::var("HASH_COST") - .ok() - .and_then(|c| c.parse().ok()) - .unwrap_or(DEFAULT_COST); - - hash(plain, hashing_cost).map_err(|e| ServerError::internal().context(e)) -} - -// The Source is the password user enter. The hash is the source after hashing. -// let source = "123"; -// let hash = hash_password(source).unwrap(); -// -// verify_password(source, hash) -pub fn verify_password(source: &str, hash: &str) -> Result { - match verify(source, hash) { - Ok(true) => Ok(true), - _ => Err(ServerError::new( - "Username and password don't match".to_string(), - ErrorCode::PasswordNotMatch, - )), - } -} diff --git a/backend/tests/api_test/auth_test.rs b/backend/tests/api_test/auth_test.rs deleted file mode 100644 index bcab6d0f4b..0000000000 --- a/backend/tests/api_test/auth_test.rs +++ /dev/null @@ -1,126 +0,0 @@ -use crate::util::helper::{spawn_user_server, TestUserServer}; -use backend_service::errors::ErrorCode; -use flowy_user_data_model::entities::{SignInParams, SignUpParams, SignUpResponse, UpdateUserParams}; - -#[actix_rt::test] -async fn user_register() { - let app = spawn_user_server().await; - let response = register_user(&app, "annie@appflowy.io", "HelloWorld123!").await; - tracing::info!("{:?}", response); -} - -#[actix_rt::test] -#[should_panic] -async fn user_sign_in_with_invalid_password() { - let app = spawn_user_server().await; - let email = "annie@appflowy.io"; - let password = "123"; - let _ = register_user(&app, email, password).await; -} - -#[actix_rt::test] -#[should_panic] -async fn user_sign_in_with_invalid_email() { - let app = spawn_user_server().await; - let email = "annie@gmail@"; - let password = "HelloWorld123!"; - let _ = register_user(&app, email, password).await; -} - -#[actix_rt::test] -async fn user_sign_in() { - let app = spawn_user_server().await; - let email = "annie@appflowy.io"; - let password = "HelloWorld123!"; - let _ = register_user(&app, email, password).await; - let params = SignInParams { - email: email.to_string(), - password: password.to_string(), - name: "rust".to_string(), - }; - let _ = app.sign_in(params).await.unwrap(); -} - -#[actix_rt::test] -#[should_panic] -async fn user_sign_out() { - let server = TestUserServer::new().await; - server.sign_out().await; - - // user_detail will be empty because use was sign out. - server.get_user_profile().await; -} - -#[actix_rt::test] -async fn user_get_detail() { - let server = TestUserServer::new().await; - tracing::info!("{:?}", server.get_user_profile().await); -} - -#[actix_rt::test] -async fn user_update_password() { - let mut server = spawn_user_server().await; - let email = "annie@appflowy.io"; - let password = "HelloWorld123!"; - let sign_up_resp = register_user(&server, email, password).await; - - let params = UpdateUserParams::new(&sign_up_resp.user_id).password("Hello123!"); - server.user_token = Some(sign_up_resp.token); - - server.update_user_profile(params).await.unwrap(); - - let sign_in_params = SignInParams { - email: email.to_string(), - password: password.to_string(), - name: "rust".to_string(), - }; - - match server.sign_in(sign_in_params).await { - Ok(_) => {} - Err(e) => { - assert_eq!(e.code, ErrorCode::PasswordNotMatch); - } - } -} - -#[actix_rt::test] -async fn user_update_name() { - let server = TestUserServer::new().await; - - let name = "tom".to_string(); - let params = UpdateUserParams::new(server.user_id()).name(&name); - server.update_user_profile(params).await.unwrap(); - - let user = server.get_user_profile().await; - assert_eq!(user.name, name); -} - -#[actix_rt::test] -async fn user_update_email() { - let server = TestUserServer::new().await; - let email = "123@gmail.com".to_string(); - let params = UpdateUserParams::new(server.user_id()).email(&email); - server.update_user_profile(params).await.unwrap(); - - let user = server.get_user_profile().await; - assert_eq!(user.email, email); -} - -#[allow(dead_code)] -async fn sign_up_user(server: &TestUserServer) -> SignUpResponse { - let email = "annie@appflowy.io"; - let password = "HelloWorld123!"; - let response = register_user(server, email, password).await; - response -} - -async fn register_user(server: &TestUserServer, email: &str, password: &str) -> SignUpResponse { - let params = SignUpParams { - email: email.to_string(), - name: "annie".to_string(), - password: password.to_string(), - }; - - let response = server.register(params).await; - response -} diff --git a/backend/tests/api_test/kv_test.rs b/backend/tests/api_test/kv_test.rs deleted file mode 100644 index 3765270ef3..0000000000 --- a/backend/tests/api_test/kv_test.rs +++ /dev/null @@ -1,79 +0,0 @@ -use crate::util::helper::spawn_server; -use backend::services::kv::KeyValue; -use std::str; - -#[actix_rt::test] -async fn kv_set_test() { - let server = spawn_server().await; - let kv = server.app_ctx.persistence.document_kv_store(); - let s1 = "123".to_string(); - let key = "1"; - - let _ = kv.set(key, s1.clone().into()).await.unwrap(); - let bytes = kv.get(key).await.unwrap().unwrap(); - let s2 = str::from_utf8(&bytes).unwrap(); - assert_eq!(s1, s2); -} - -#[actix_rt::test] -async fn kv_delete_test() { - let server = spawn_server().await; - let kv = server.app_ctx.persistence.document_kv_store(); - let s1 = "123".to_string(); - let key = "1"; - - let _ = kv.set(key, s1.clone().into()).await.unwrap(); - let _ = kv.remove(key).await.unwrap(); - assert_eq!(kv.get(key).await.unwrap(), None); -} - -#[actix_rt::test] -async fn kv_batch_set_test() { - let server = spawn_server().await; - let kv = server.app_ctx.persistence.document_kv_store(); - let kvs = vec![ - KeyValue { - key: "1".to_string(), - value: "a".to_string().into(), - }, - KeyValue { - key: "2".to_string(), - value: "b".to_string().into(), - }, - ]; - - kv.batch_set(kvs.clone()).await.unwrap(); - let kvs_from_db = kv - .batch_get(kvs.clone().into_iter().map(|value| value.key).collect::>()) - .await - .unwrap(); - - assert_eq!(kvs, kvs_from_db); -} - -#[actix_rt::test] -async fn kv_batch_get_start_with_test() { - let server = spawn_server().await; - let kv = server.app_ctx.persistence.document_kv_store(); - let kvs = vec![ - KeyValue { - key: "abc:1".to_string(), - value: "a".to_string().into(), - }, - KeyValue { - key: "abc:2".to_string(), - value: "b".to_string().into(), - }, - ]; - - kv.batch_set(kvs.clone()).await.unwrap(); - kv.transaction(|mut transaction| { - Box::pin(async move { - let kvs_from_db = transaction.batch_get_start_with("abc").await.unwrap(); - assert_eq!(kvs, kvs_from_db); - Ok(()) - }) - }) - .await - .unwrap(); -} diff --git a/backend/tests/api_test/mod.rs b/backend/tests/api_test/mod.rs deleted file mode 100644 index 6053586a32..0000000000 --- a/backend/tests/api_test/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod auth_test; -mod kv_test; -mod workspace_test; diff --git a/backend/tests/api_test/workspace_test.rs b/backend/tests/api_test/workspace_test.rs deleted file mode 100644 index 33a7157e84..0000000000 --- a/backend/tests/api_test/workspace_test.rs +++ /dev/null @@ -1,282 +0,0 @@ -#![allow(clippy::all)] - -use crate::util::helper::{BackendViewTest, *}; -use flowy_collaboration::{ - client_document::{ClientDocument, PlainDoc}, - entities::{ - document_info::{CreateDocParams, DocumentId}, - revision::{md5, RepeatedRevision, Revision}, - }, -}; -use flowy_folder_data_model::entities::{ - app::{AppId, UpdateAppParams}, - trash::{RepeatedTrashId, TrashId, TrashType}, - view::{RepeatedViewId, UpdateViewParams, ViewId}, - workspace::{CreateWorkspaceParams, UpdateWorkspaceParams, WorkspaceId}, -}; - -#[actix_rt::test] -async fn workspace_create() { - let test = BackendWorkspaceTest::new().await; - tracing::info!("{:?}", test.workspace); -} - -#[actix_rt::test] -async fn workspace_read() { - let test = BackendWorkspaceTest::new().await; - let read_params = WorkspaceId::new(Some(test.workspace.id.clone())); - let repeated_workspace = test.server.read_workspaces(read_params).await; - tracing::info!("{:?}", repeated_workspace); -} - -#[actix_rt::test] -async fn workspace_read_with_belongs() { - let test = BackendWorkspaceTest::new().await; - - let _ = test.create_app().await; - let _ = test.create_app().await; - let _ = test.create_app().await; - - let read_params = WorkspaceId::new(Some(test.workspace.id.clone())); - let workspaces = test.server.read_workspaces(read_params).await; - let workspace = workspaces.items.first().unwrap(); - assert_eq!(workspace.apps.len(), 3); -} - -#[actix_rt::test] -async fn workspace_update() { - let test = BackendWorkspaceTest::new().await; - let new_name = "rename workspace name"; - let new_desc = "rename workspace description"; - - let update_params = UpdateWorkspaceParams { - id: test.workspace.id.clone(), - name: Some(new_name.to_string()), - desc: Some(new_desc.to_string()), - }; - test.server.update_workspace(update_params).await; - let read_params = WorkspaceId::new(Some(test.workspace.id.clone())); - let repeated_workspace = test.server.read_workspaces(read_params).await; - - let workspace = repeated_workspace.first().unwrap(); - assert_eq!(workspace.name, new_name); - assert_eq!(workspace.desc, new_desc); -} - -#[actix_rt::test] -async fn workspace_delete() { - let test = BackendWorkspaceTest::new().await; - let delete_params = WorkspaceId { - workspace_id: Some(test.workspace.id.clone()), - }; - - let _ = test.server.delete_workspace(delete_params).await; - let read_params = WorkspaceId::new(Some(test.workspace.id.clone())); - let repeated_workspace = test.server.read_workspaces(read_params).await; - assert_eq!(repeated_workspace.len(), 0); -} - -#[actix_rt::test] -async fn app_create() { - let test = BackendAppTest::new().await; - tracing::info!("{:?}", test.app); -} - -#[actix_rt::test] -async fn app_read() { - let test = BackendAppTest::new().await; - let read_params = AppId::new(&test.app.id); - assert_eq!(test.server.read_app(read_params).await.is_some(), true); -} - -#[actix_rt::test] -async fn app_read_with_belongs() { - let test = BackendAppTest::new().await; - - let _ = create_test_view(&test.server, &test.app.id).await; - let _ = create_test_view(&test.server, &test.app.id).await; - - let read_params = AppId::new(&test.app.id); - let app = test.server.read_app(read_params).await.unwrap(); - assert_eq!(app.belongings.len(), 2); -} - -#[actix_rt::test] -async fn app_read_with_belongs_in_trash() { - let test = BackendAppTest::new().await; - - let _ = create_test_view(&test.server, &test.app.id).await; - let view = create_test_view(&test.server, &test.app.id).await; - - test.server.create_view_trash(&view.id).await; - - let read_params = AppId::new(&test.app.id); - let app = test.server.read_app(read_params).await.unwrap(); - assert_eq!(app.belongings.len(), 1); -} - -#[actix_rt::test] -async fn app_update() { - let test = BackendAppTest::new().await; - - let new_name = "flowy"; - - let update_params = UpdateAppParams::new(&test.app.id).name(new_name); - test.server.update_app(update_params).await; - - let read_params = AppId::new(&test.app.id); - let app = test.server.read_app(read_params).await.unwrap(); - assert_eq!(&app.name, new_name); -} - -#[actix_rt::test] -async fn app_delete() { - let test = BackendAppTest::new().await; - - let delete_params = AppId { - app_id: test.app.id.clone(), - }; - test.server.delete_app(delete_params).await; - let read_params = AppId::new(&test.app.id); - assert_eq!(test.server.read_app(read_params).await.is_none(), true); -} - -#[actix_rt::test] -async fn view_create() { - let test = BackendViewTest::new().await; - tracing::info!("{:?}", test.view); -} - -#[actix_rt::test] -async fn view_update() { - let test = BackendViewTest::new().await; - let new_name = "name view name"; - - // update - let update_params = UpdateViewParams::new(&test.view.id).name(new_name); - test.server.update_view(update_params).await; - - // read - let read_params: ViewId = test.view.id.clone().into(); - let view = test.server.read_view(read_params).await.unwrap(); - assert_eq!(&view.name, new_name); -} - -#[actix_rt::test] -async fn view_delete() { - let test = BackendViewTest::new().await; - test.server.create_view_trash(&test.view.id).await; - - let trash_ids = test - .server - .read_trash() - .await - .items - .into_iter() - .map(|item| item.id) - .collect::>(); - // read - let read_params: ViewId = test.view.id.clone().into(); - - // the view can't read from the server. it should be in the trash - assert_eq!(test.server.read_view(read_params).await.is_none(), true); - assert_eq!(trash_ids.contains(&test.view.id), true); -} - -#[actix_rt::test] -async fn trash_delete() { - let test = BackendViewTest::new().await; - test.server.create_view_trash(&test.view.id).await; - - let identifier = TrashId { - id: test.view.id.clone(), - ty: TrashType::View, - }; - test.server.delete_view_trash(vec![identifier].into()).await; - - assert_eq!(test.server.read_trash().await.is_empty(), true); -} - -#[actix_rt::test] -async fn trash_delete_all() { - let test = BackendViewTest::new().await; - test.server.create_view_trash(&test.view.id).await; - - test.server.delete_view_trash(RepeatedTrashId::all()).await; - assert_eq!(test.server.read_trash().await.is_empty(), true); -} - -#[actix_rt::test] -async fn workspace_list_read() { - let mut server = spawn_user_server().await; - let token = server.register_user().await.token; - server.user_token = Some(token); - for i in 0..3 { - let params = CreateWorkspaceParams { - name: format!("{} workspace", i), - desc: format!("This is my {} workspace", i), - }; - let _ = server.create_workspace(params).await; - } - - let read_params = WorkspaceId::new(None); - let workspaces = server.read_workspaces(read_params).await; - assert_eq!(workspaces.len(), 3); -} - -#[actix_rt::test] -async fn doc_read() { - let test = BackendViewTest::new().await; - let params = DocumentId { - doc_id: test.view.id.clone(), - }; - let doc = test.server.read_doc(params).await; - assert_eq!(doc.is_some(), true); -} - -#[actix_rt::test] -async fn doc_create() { - let mut revisions: Vec = vec![]; - let server = TestUserServer::new().await; - let doc_id = uuid::Uuid::new_v4().to_string(); - let user_id = "a".to_owned(); - let mut document = ClientDocument::new::(); - let mut offset = 0; - for i in 0..5 { - let content = i.to_string(); - let delta = document.insert(offset, content.clone()).unwrap(); - offset += content.len(); - let bytes = delta.to_bytes(); - let md5 = md5(&bytes); - let revision = if i == 0 { - Revision::new(&doc_id, i, i, bytes, &user_id, md5) - } else { - Revision::new(&doc_id, i - 1, i, bytes, &user_id, md5) - }; - revisions.push(revision); - } - - let params = CreateDocParams { - id: doc_id.clone(), - revisions: RepeatedRevision::new(revisions), - }; - server.create_doc(params).await; - - let doc = server.read_doc(DocumentId { doc_id }).await; - assert_eq!(doc.unwrap().text, document.to_json()); -} - -#[actix_rt::test] -async fn doc_delete() { - let test = BackendViewTest::new().await; - let delete_params = RepeatedViewId { - items: vec![test.view.id.clone()], - }; - test.server.delete_view(delete_params).await; - - let params = DocumentId { - doc_id: test.view.id.clone(), - }; - let doc = test.server.read_doc(params).await; - assert_eq!(doc.is_none(), true); -} diff --git a/backend/tests/document_test/edit_script.rs b/backend/tests/document_test/edit_script.rs deleted file mode 100644 index 7bf77a0eea..0000000000 --- a/backend/tests/document_test/edit_script.rs +++ /dev/null @@ -1,179 +0,0 @@ -#![allow(clippy::all)] -#![cfg_attr(rustfmt, rustfmt::skip)] -use std::convert::TryInto; -use actix_web::web::Data; -use flowy_document::core::ClientDocumentEditor; -use flowy_test::{helper::ViewTest, FlowySDKTest}; -use flowy_user::services::UserSession; -use futures_util::{stream, stream::StreamExt}; -use std::sync::Arc; -use bytes::Bytes; -use tokio::time::{sleep, Duration}; -use crate::util::helper::{spawn_server, TestServer}; -use flowy_collaboration::{entities::document_info::DocumentId, protobuf::ResetDocumentParams as ResetDocumentParamsPB}; -use lib_ot::rich_text::{RichTextAttribute, RichTextDelta}; -use parking_lot::RwLock; -use backend::services::document::persistence::{read_document, reset_document}; -use flowy_collaboration::entities::revision::{RepeatedRevision, Revision}; -use flowy_collaboration::protobuf::{RepeatedRevision as RepeatedRevisionPB, DocumentId as DocumentIdPB}; -use flowy_collaboration::server_document::ServerDocumentManager; -use flowy_net::ws::connection::FlowyWebSocketConnect; -use lib_ot::core::Interval; - -pub struct DocumentTest { - server: TestServer, - flowy_test: FlowySDKTest, -} -#[derive(Clone)] -pub enum DocScript { - ClientInsertText(usize, &'static str), - ClientFormatText(Interval, RichTextAttribute), - ClientOpenDoc, - AssertClient(&'static str), - AssertServer(&'static str, i64), - ServerResetDocument(String, i64), // delta_json, rev_id -} - -impl DocumentTest { - pub async fn new() -> Self { - let server = spawn_server().await; - let flowy_test = FlowySDKTest::new(server.client_server_config.clone()); - Self { server, flowy_test } - } - - pub async fn run_scripts(self, scripts: Vec) { - let _ = self.flowy_test.sign_up().await; - let DocumentTest { server, flowy_test } = self; - let script_context = Arc::new(RwLock::new(ScriptContext::new(flowy_test, server).await)); - run_scripts(script_context, scripts).await; - sleep(Duration::from_secs(5)).await; - } -} - -#[derive(Clone)] -struct ScriptContext { - client_editor: Option>, - client_sdk: FlowySDKTest, - client_user_session: Arc, - #[allow(dead_code)] - ws_conn: Arc, - server: TestServer, - doc_id: String, -} - -impl ScriptContext { - async fn new(client_sdk: FlowySDKTest, server: TestServer) -> Self { - let user_session = client_sdk.user_session.clone(); - let ws_conn = client_sdk.ws_conn.clone(); - let doc_id = create_doc(&client_sdk).await; - - Self { - client_editor: None, - client_sdk, - client_user_session: user_session, - ws_conn, - server, - doc_id, - } - } - - async fn open_doc(&mut self) { - let doc_id = self.doc_id.clone(); - let edit_context = self.client_sdk.document_manager.open_document(doc_id).await.unwrap(); - self.client_editor = Some(edit_context); - } - - fn client_editor(&self) -> Arc { self.client_editor.as_ref().unwrap().clone() } -} - -async fn run_scripts(context: Arc>, scripts: Vec) { - let mut fut_scripts = vec![]; - for script in scripts { - let context = context.clone(); - let fut = async move { - let doc_id = context.read().doc_id.clone(); - match script { - DocScript::ClientOpenDoc => { - context.write().open_doc().await; - }, - DocScript::ClientInsertText(index, s) => { - context.read().client_editor().insert(index, s).await.unwrap(); - }, - DocScript::ClientFormatText(interval, attribute) => { - context - .read() - .client_editor() - .format(interval, attribute) - .await - .unwrap(); - }, - DocScript::AssertClient(s) => { - sleep(Duration::from_millis(2000)).await; - let json = context.read().client_editor().doc_json().await.unwrap(); - assert_eq(s, &json); - }, - DocScript::AssertServer(s, rev_id) => { - sleep(Duration::from_millis(2000)).await; - let persistence = Data::new(context.read().server.app_ctx.persistence.document_kv_store()); - let doc_identifier: DocumentIdPB = DocumentId { - doc_id - }.try_into().unwrap(); - - let document_info = read_document(persistence.get_ref(), doc_identifier).await.unwrap(); - assert_eq(s, &document_info.text); - assert_eq!(document_info.rev_id, rev_id); - }, - DocScript::ServerResetDocument(document_json, rev_id) => { - let delta_data = Bytes::from(document_json); - let user_id = context.read().client_user_session.user_id().unwrap(); - let md5 = format!("{:x}", md5::compute(&delta_data)); - let base_rev_id = if rev_id == 0 { rev_id } else { rev_id - 1 }; - let revision = Revision::new( - &doc_id, - base_rev_id, - rev_id, - delta_data, - &user_id, - md5, - ); - - let document_manager = context.read().server.app_ctx.document_manager.clone(); - reset_doc(&doc_id, RepeatedRevision::new(vec![revision]), document_manager.get_ref()).await; - sleep(Duration::from_millis(2000)).await; - }, - } - }; - fut_scripts.push(fut); - } - - let mut stream = stream::iter(fut_scripts); - while let Some(script) = stream.next().await { - let _ = script.await; - } - - std::mem::forget(context); -} - -fn assert_eq(expect: &str, receive: &str) { - let expected_delta: RichTextDelta = serde_json::from_str(expect).unwrap(); - let target_delta: RichTextDelta = serde_json::from_str(receive).unwrap(); - - if expected_delta != target_delta { - log::error!("✅ expect: {}", expect,); - log::error!("❌ receive: {}", receive); - } - assert_eq!(target_delta, expected_delta); -} - -async fn create_doc(flowy_test: &FlowySDKTest) -> String { - let view_test = ViewTest::new(flowy_test).await; - view_test.view.id -} - -async fn reset_doc(doc_id: &str, repeated_revision: RepeatedRevision, document_manager: &Arc) { - let pb: RepeatedRevisionPB = repeated_revision.try_into().unwrap(); - let mut params = ResetDocumentParamsPB::new(); - params.set_doc_id(doc_id.to_owned()); - params.set_revisions(pb); - let _ = reset_document(document_manager, params).await.unwrap(); -} diff --git a/backend/tests/document_test/edit_test.rs b/backend/tests/document_test/edit_test.rs deleted file mode 100644 index 0aff223881..0000000000 --- a/backend/tests/document_test/edit_test.rs +++ /dev/null @@ -1,207 +0,0 @@ -use crate::document_test::edit_script::{DocScript, DocumentTest}; -use flowy_collaboration::client_document::{ClientDocument, NewlineDoc}; -use lib_ot::{core::Interval, rich_text::RichTextAttribute}; - -#[rustfmt::skip] -// ┌─────────┐ ┌─────────┐ -// │ Server │ │ Client │ -// └─────────┘ └─────────┘ -// ┌────────────────┐ │ │ ┌────────────────┐ -// │ops: [] rev: 0 │◀┼──── Ping ─────┼─┤ops: [] rev: 0 │ -// └────────────────┘ │ │ └────────────────┘ -// ┌────────────────────┐ │ │ ┌────────────────────┐ -// │ops: ["abc"] rev: 1 │◀┼───ClientPush ───┼─│ops: ["abc"] rev: 1 │ -// └────────────────────┘ │ │ └────────────────────┘ -// ┌──────────────────────────┐ │ │ ┌──────────────────────┐ -// │ops: ["abc", "123"] rev: 2│◀┼── ClientPush ───┼─│ops: ["123"] rev: 2 │ -// └──────────────────────────┘ │ │ └──────────────────────┘ -// │ │ -#[actix_rt::test] -async fn delta_sync_while_editing() { - let test = DocumentTest::new().await; - test.run_scripts(vec![ - DocScript::ClientOpenDoc, - DocScript::ClientInsertText(0, "abc"), - DocScript::ClientInsertText(3, "123"), - DocScript::AssertClient(r#"[{"insert":"abc123\n"}]"#), - DocScript::AssertServer(r#"[{"insert":"abc123\n"}]"#, 1), - ]) - .await; -} - -#[actix_rt::test] -async fn delta_sync_multi_revs() { - let test = DocumentTest::new().await; - test.run_scripts(vec![ - DocScript::ClientOpenDoc, - DocScript::ClientInsertText(0, "abc"), - DocScript::ClientInsertText(3, "123"), - DocScript::ClientInsertText(6, "efg"), - DocScript::ClientInsertText(9, "456"), - ]) - .await; -} - -#[actix_rt::test] -async fn delta_sync_while_editing_with_attribute() { - let test = DocumentTest::new().await; - test.run_scripts(vec![ - DocScript::ClientOpenDoc, - DocScript::ClientInsertText(0, "abc"), - DocScript::ClientFormatText(Interval::new(0, 3), RichTextAttribute::Bold(true)), - DocScript::AssertClient(r#"[{"insert":"abc","attributes":{"bold":true}},{"insert":"\n"}]"#), - DocScript::AssertServer(r#"[{"insert":"abc","attributes":{"bold":true}},{"insert":"\n"}]"#, 1), - DocScript::ClientInsertText(3, "efg"), - DocScript::ClientFormatText(Interval::new(3, 5), RichTextAttribute::Italic(true)), - DocScript::AssertClient(r#"[{"insert":"abc","attributes":{"bold":true}},{"insert":"ef","attributes":{"bold":true,"italic":true}},{"insert":"g","attributes":{"bold":true}},{"insert":"\n"}]"#), - DocScript::AssertServer(r#"[{"insert":"abc","attributes":{"bold":true}},{"insert":"ef","attributes":{"bold":true,"italic":true}},{"insert":"g","attributes":{"bold":true}},{"insert":"\n"}]"#, 3), - ]) - .await; -} - -#[rustfmt::skip] -// ┌─────────┐ ┌─────────┐ -// │ Server │ │ Client │ -// └─────────┘ └─────────┘ -// ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ │ -// ops: ["123", "456"] rev: 3│ │ │ -// └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ │ -// │ │ -// ◀───── Ping ───┤ Open doc -// │ │ -// │ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ -// ├───ServerPush────┼─▶ ops: ["123", "456"] rev: 3│ -// │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ -#[actix_rt::test] -async fn delta_sync_with_server_push() { - let test = DocumentTest::new().await; - let mut document = ClientDocument::new::(); - document.insert(0, "123").unwrap(); - document.insert(3, "456").unwrap(); - let json = document.to_json(); - - test.run_scripts(vec![ - DocScript::ServerResetDocument(json, 3), - DocScript::ClientOpenDoc, - DocScript::AssertClient(r#"[{"insert":"123456\n"}]"#), - DocScript::AssertServer(r#"[{"insert":"123456\n"}]"#, 3), - ]) - .await; -} - -#[rustfmt::skip] -// ┌─────────┐ ┌─────────┐ -// │ Server │ │ Client │ -// └─────────┘ └─────────┘ -// ┌ ─ ─ ─ ─ ┐ │ │ -// ops: [] │ │ -// └ ─ ─ ─ ─ ┘ │ │ -// │ │ -// ◀───── Ping ───┤ Open doc -// ◀───── Ping ───┤ -// ◀───── Ping ───┤ -// ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ │ -// ops: ["123"], rev: 3 │ │ -// └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ -// ├────ServerPush───▶ ops: ["123"] rev: 3 -// │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ -// │ │ -#[actix_rt::test] -async fn delta_sync_with_server_push_after_reset_document() { - let test = DocumentTest::new().await; - let mut document = ClientDocument::new::(); - document.insert(0, "123").unwrap(); - let json = document.to_json(); - - test.run_scripts(vec![ - DocScript::ClientOpenDoc, - DocScript::ServerResetDocument(json, 3), - DocScript::AssertClient(r#"[{"insert":"123\n"}]"#), - DocScript::AssertServer(r#"[{"insert":"123\n"}]"#, 3), - ]) - .await; -} - -#[rustfmt::skip] -// ┌─────────┐ ┌─────────┐ -// │ Server │ │ Client │ -// └─────────┘ └─────────┘ -// │ │ -// │ │ -// ◀────── Ping ─────┤ Open doc -// ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ │ -// ops: ["123"] rev: 3 │ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ -// └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ │ ops: ["abc"] rev: 1 │ -// │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ -// │ │ ┌────────────────────┐ -// ◀───ClientPush ───┤ │ops: ["abc"] rev: 1 │ -// ┌───────────────────┐ │ │ └────────────────────┘ -// │ops: ["123"] rev: 3│ ├────ServerPush───▶ transform -// └───────────────────┘ │ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ -// │ │ ops: ["abc", "123"] rev: 4│ -// │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ -// │ │ ┌────────────────────────────────┐ -// ◀────ClientPush───┤ │ops: ["retain 3","abc"] rev: 4 │ -// ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ │ └────────────────────────────────┘ -// ops: ["abc", "123"] rev: 4│ ├────ServerAck────▶ -// └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ │ -#[actix_rt::test] -async fn delta_sync_while_local_rev_less_than_server_rev() { - let test = DocumentTest::new().await; - let mut document = ClientDocument::new::(); - document.insert(0, "123").unwrap(); - let json = document.to_json(); - - test.run_scripts(vec![ - DocScript::ClientOpenDoc, - DocScript::ServerResetDocument(json, 3), - DocScript::ClientInsertText(0, "abc"), - DocScript::AssertClient(r#"[{"insert":"abc123\n"}]"#), - DocScript::AssertServer(r#"[{"insert":"abc123\n"}]"#, 4), - ]) - .await; -} - -#[rustfmt::skip] -// ┌─────────┐ ┌─────────┐ -// │ Server │ │ Client │ -// └─────────┘ └─────────┘ -// ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ │ -// ops: ["123"] rev: 1 │ │ -// └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ │ -// ◀──── Ping ────┤ Open doc -// │ │ -// │ │ ┌──────────────────┐ -// ├───ServerPush────▶ │ops: [123] rev: 1 │ -// │ │ └──────────────────┘ -// │ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ -// │ │ ops: ["123","abc", "efg"] rev: 3 │ -// │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ -// │ │ ┌──────────────────────────────┐ -// ◀────ClientPush───┤ │ops: [retain 3, "abc"] rev: 2 │ -// ┌──────────────────────────┐ │ │ └──────────────────────────────┘ -// │ops: ["123","abc"] rev: 2 │ ├────ServerAck────▶ -// └──────────────────────────┘ │ │ -// │ │ ┌──────────────────────────────┐ -// ◀────ClientPush───┤ │ops: [retain 6, "efg"] rev: 3 │ -// ┌──────────────────────────────────┐ │ │ └──────────────────────────────┘ -// │ops: ["123","abc", "efg"] rev: 3 │ ├────ServerAck────▶ -// └──────────────────────────────────┘ │ │ -#[actix_rt::test] -async fn delta_sync_while_local_rev_greater_than_server_rev() { - let test = DocumentTest::new().await; - let mut document = ClientDocument::new::(); - document.insert(0, "123").unwrap(); - let json = document.to_json(); - - test.run_scripts(vec![ - DocScript::ServerResetDocument(json, 1), - DocScript::ClientOpenDoc, - DocScript::AssertClient(r#"[{"insert":"123\n"}]"#), - DocScript::ClientInsertText(3, "abc"), - DocScript::ClientInsertText(6, "efg"), - DocScript::AssertClient(r#"[{"insert":"123abcefg\n"}]"#), - DocScript::AssertServer(r#"[{"insert":"123abcefg\n"}]"#, 3), - ]) - .await; -} diff --git a/backend/tests/document_test/mod.rs b/backend/tests/document_test/mod.rs deleted file mode 100644 index 7ffb40d9b8..0000000000 --- a/backend/tests/document_test/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod edit_script; -mod edit_test; diff --git a/backend/tests/main.rs b/backend/tests/main.rs deleted file mode 100644 index eb6c8a9e40..0000000000 --- a/backend/tests/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod api_test; -mod document_test; -pub mod util; diff --git a/backend/tests/util/helper.rs b/backend/tests/util/helper.rs deleted file mode 100644 index cce94b54eb..0000000000 --- a/backend/tests/util/helper.rs +++ /dev/null @@ -1,388 +0,0 @@ -use backend::{ - application::{init_app_context, Application}, - config::{get_configuration, DatabaseSettings}, - context::AppContext, -}; -use backend_service::{ - configuration::{get_client_server_configuration, ClientServerConfiguration}, - errors::ServerError, -}; -use flowy_collaboration::{ - client_document::default::initial_delta_string, - entities::document_info::{CreateDocParams, DocumentId, DocumentInfo}, -}; -use flowy_folder_data_model::entities::{app::*, trash::*, view::*, workspace::*}; -use flowy_net::http_server::{ - core::*, - document::{create_document_request, read_document_request}, - user::*, -}; -use flowy_user_data_model::entities::*; -use lib_infra::uuid_string; -use sqlx::{Connection, Executor, PgConnection, PgPool}; -use uuid::Uuid; - -pub struct TestUserServer { - pub inner: TestServer, - pub user_token: Option, - pub user_id: Option, -} - -impl TestUserServer { - pub async fn new() -> Self { - let mut server: TestUserServer = spawn_server().await.into(); - let response = server.register_user().await; - server.user_token = Some(response.token); - server.user_id = Some(response.user_id); - server - } - - pub async fn sign_in(&self, params: SignInParams) -> Result { - let url = format!("{}/api/auth", self.http_addr()); - let resp = user_sign_in_request(params, &url).await?; - Ok(resp) - } - - pub async fn sign_out(&self) { - let url = format!("{}/api/auth", self.http_addr()); - let _ = user_sign_out_request(self.user_token(), &url).await.unwrap(); - } - - pub fn user_token(&self) -> &str { - self.user_token.as_ref().expect("must call register_user first ") - } - - pub fn user_id(&self) -> &str { - self.user_id.as_ref().expect("must call register_user first ") - } - - pub async fn get_user_profile(&self) -> UserProfile { - let url = format!("{}/api/user", self.http_addr()); - let user_profile = get_user_profile_request(self.user_token(), &url).await.unwrap(); - user_profile - } - - pub async fn update_user_profile(&self, params: UpdateUserParams) -> Result<(), ServerError> { - let url = format!("{}/api/user", self.http_addr()); - let _ = update_user_profile_request(self.user_token(), params, &url).await?; - Ok(()) - } - - pub async fn create_workspace(&self, params: CreateWorkspaceParams) -> Workspace { - let url = format!("{}/api/workspace", self.http_addr()); - let workspace = create_workspace_request(self.user_token(), params, &url).await.unwrap(); - workspace - } - - pub async fn read_workspaces(&self, params: WorkspaceId) -> RepeatedWorkspace { - let url = format!("{}/api/workspace", self.http_addr()); - let workspaces = read_workspaces_request(self.user_token(), params, &url).await.unwrap(); - workspaces - } - - pub async fn update_workspace(&self, params: UpdateWorkspaceParams) { - let url = format!("{}/api/workspace", self.http_addr()); - update_workspace_request(self.user_token(), params, &url).await.unwrap(); - } - - pub async fn delete_workspace(&self, params: WorkspaceId) { - let url = format!("{}/api/workspace", self.http_addr()); - delete_workspace_request(self.user_token(), params, &url).await.unwrap(); - } - - pub async fn create_app(&self, params: CreateAppParams) -> App { - let url = format!("{}/api/app", self.http_addr()); - let app = create_app_request(self.user_token(), params, &url).await.unwrap(); - app - } - - pub async fn read_app(&self, params: AppId) -> Option { - let url = format!("{}/api/app", self.http_addr()); - let app = read_app_request(self.user_token(), params, &url).await.unwrap(); - app - } - - pub async fn update_app(&self, params: UpdateAppParams) { - let url = format!("{}/api/app", self.http_addr()); - update_app_request(self.user_token(), params, &url).await.unwrap(); - } - - pub async fn delete_app(&self, params: AppId) { - let url = format!("{}/api/app", self.http_addr()); - delete_app_request(self.user_token(), params, &url).await.unwrap(); - } - - pub async fn create_view(&self, params: CreateViewParams) -> View { - let url = format!("{}/api/view", self.http_addr()); - let view = create_view_request(self.user_token(), params, &url).await.unwrap(); - view - } - - pub async fn read_view(&self, params: ViewId) -> Option { - let url = format!("{}/api/view", self.http_addr()); - let view = read_view_request(self.user_token(), params, &url).await.unwrap(); - view - } - - pub async fn update_view(&self, params: UpdateViewParams) { - let url = format!("{}/api/view", self.http_addr()); - update_view_request(self.user_token(), params, &url).await.unwrap(); - } - - pub async fn delete_view(&self, params: RepeatedViewId) { - let url = format!("{}/api/view", self.http_addr()); - delete_view_request(self.user_token(), params, &url).await.unwrap(); - } - - pub async fn create_view_trash(&self, view_id: &str) { - let identifier = TrashId { - id: view_id.to_string(), - ty: TrashType::View, - }; - let url = format!("{}/api/trash", self.http_addr()); - create_trash_request(self.user_token(), vec![identifier].into(), &url) - .await - .unwrap(); - } - - pub async fn delete_view_trash(&self, trash_identifiers: RepeatedTrashId) { - let url = format!("{}/api/trash", self.http_addr()); - - delete_trash_request(self.user_token(), trash_identifiers, &url) - .await - .unwrap(); - } - - pub async fn read_trash(&self) -> RepeatedTrash { - let url = format!("{}/api/trash", self.http_addr()); - read_trash_request(self.user_token(), &url).await.unwrap() - } - - pub async fn read_doc(&self, params: DocumentId) -> Option { - let url = format!("{}/api/doc", self.http_addr()); - let doc = read_document_request(self.user_token(), params, &url).await.unwrap(); - doc - } - - pub async fn create_doc(&self, params: CreateDocParams) { - let url = format!("{}/api/doc", self.http_addr()); - let _ = create_document_request(self.user_token(), params, &url).await.unwrap(); - } - - pub async fn register_user(&self) -> SignUpResponse { - let params = SignUpParams { - email: "annie@appflowy.io".to_string(), - name: "annie".to_string(), - password: "HelloAppFlowy123!".to_string(), - }; - - self.register(params).await - } - - pub async fn register(&self, params: SignUpParams) -> SignUpResponse { - let url = format!("{}/api/register", self.http_addr()); - let response = user_sign_up_request(params, &url).await.unwrap(); - response - } - - pub fn http_addr(&self) -> String { - self.inner.client_server_config.base_url() - } - - pub fn ws_addr(&self) -> String { - format!( - "{}/{}", - self.inner.client_server_config.ws_addr(), - self.user_token.as_ref().unwrap() - ) - } -} - -impl std::convert::From for TestUserServer { - fn from(server: TestServer) -> Self { - TestUserServer { - inner: server, - user_token: None, - user_id: None, - } - } -} - -pub async fn spawn_user_server() -> TestUserServer { - let server: TestUserServer = spawn_server().await.into(); - server -} - -#[derive(Clone)] -pub struct TestServer { - pub app_ctx: AppContext, - pub client_server_config: ClientServerConfiguration, -} - -pub async fn spawn_server() -> TestServer { - let database_name = Uuid::new_v4().to_string(); - let configuration = { - let mut c = get_configuration().expect("Failed to read configuration."); - c.database.database_name = database_name.clone(); - // Use a random OS port - c.application.port = 0; - c - }; - - let _ = configure_database(&configuration.database).await; - let app_ctx = init_app_context(&configuration).await; - let application = Application::build(configuration.clone(), app_ctx.clone()) - .await - .expect("Failed to build application."); - let application_port = application.port(); - - let _ = tokio::spawn(async { - let _ = application.run_until_stopped(); - // drop_test_database(database_name).await; - }); - - let mut client_server_config = get_client_server_configuration().expect("Failed to read configuration."); - client_server_config.reset_host_with_port("localhost", application_port); - - TestServer { - app_ctx, - client_server_config, - } -} - -async fn configure_database(config: &DatabaseSettings) -> PgPool { - // Create database - let mut connection = PgConnection::connect_with(&config.without_db()) - .await - .expect("Failed to connect to Postgres"); - connection - .execute(&*format!(r#"CREATE DATABASE "{}";"#, config.database_name)) - .await - .expect("Failed to create database."); - - // Migrate database - let connection_pool = PgPool::connect_with(config.with_db()) - .await - .expect("Failed to connect to Postgres."); - - sqlx::migrate!("./migrations") - .run(&connection_pool) - .await - .expect("Failed to migrate the database"); - - connection_pool -} - -#[allow(dead_code)] -async fn drop_test_database(database_name: String) { - // https://stackoverflow.com/questions/36502401/postgres-drop-database-error-pq-cannot-drop-the-currently-open-database?rq=1 - let configuration = { - let mut c = get_configuration().expect("Failed to read configuration."); - c.database.database_name = "flowy".to_owned(); - c.application.port = 0; - c - }; - - let mut connection = PgConnection::connect_with(&configuration.database.without_db()) - .await - .expect("Failed to connect to Postgres"); - - connection - .execute(&*format!(r#"Drop DATABASE "{}";"#, database_name)) - .await - .expect("Failed to drop database."); -} - -pub async fn create_test_workspace(server: &TestUserServer) -> Workspace { - let params = CreateWorkspaceParams { - name: "My first workspace".to_string(), - desc: "This is my first workspace".to_string(), - }; - - let workspace = server.create_workspace(params).await; - workspace -} - -pub async fn create_test_app(server: &TestUserServer, workspace_id: &str) -> App { - let params = CreateAppParams { - workspace_id: workspace_id.to_owned(), - name: "My first app".to_string(), - desc: "This is my first app".to_string(), - color_style: ColorStyle::default(), - }; - - let app = server.create_app(params).await; - app -} - -pub async fn create_test_view(application: &TestUserServer, app_id: &str) -> View { - let name = "My first view".to_string(); - let desc = "This is my first view".to_string(); - let thumbnail = "http://1.png".to_string(); - - let params = CreateViewParams::new( - app_id.to_owned(), - name, - desc, - ViewType::Doc, - thumbnail, - initial_delta_string(), - uuid_string(), - ); - let app = application.create_view(params).await; - app -} - -pub struct BackendWorkspaceTest { - pub server: TestUserServer, - pub workspace: Workspace, -} - -impl BackendWorkspaceTest { - pub async fn new() -> Self { - let server = TestUserServer::new().await; - let workspace = create_test_workspace(&server).await; - Self { server, workspace } - } - - pub async fn create_app(&self) -> App { - create_test_app(&self.server, &self.workspace.id).await - } -} - -pub struct BackendAppTest { - pub server: TestUserServer, - pub workspace: Workspace, - pub app: App, -} - -impl BackendAppTest { - pub async fn new() -> Self { - let server = TestUserServer::new().await; - let workspace = create_test_workspace(&server).await; - let app = create_test_app(&server, &workspace.id).await; - Self { server, workspace, app } - } -} - -pub struct BackendViewTest { - pub server: TestUserServer, - pub workspace: Workspace, - pub app: App, - pub view: View, -} - -impl BackendViewTest { - pub async fn new() -> Self { - let server = TestUserServer::new().await; - let workspace = create_test_workspace(&server).await; - let app = create_test_app(&server, &workspace.id).await; - let view = create_test_view(&server, &app.id).await; - Self { - server, - workspace, - app, - view, - } - } -} diff --git a/backend/tests/util/mod.rs b/backend/tests/util/mod.rs deleted file mode 100644 index 485bb81a91..0000000000 --- a/backend/tests/util/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod helper;