├── .dockerignore ├── .github ├── deploy-grpc-telegram-bot.sh ├── deploy-mongodb-redis.sh └── workflows │ ├── grpc-telegram-bot.yml │ └── mongodb-redis.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── grpc-telegram-bot ├── .env ├── bot │ ├── .env │ ├── Cargo.toml │ ├── Dockerfile │ └── src │ │ ├── conversion.rs │ │ ├── error.rs │ │ └── main.rs ├── docker-compose.override.yml ├── docker-compose.yml ├── nginx │ ├── Dockerfile │ └── nginx.conf ├── readme.adoc ├── rpc │ ├── Cargo.toml │ ├── build.rs │ ├── proto │ │ └── solar-system-info │ │ │ └── solar-system-info.proto │ └── src │ │ └── lib.rs └── server │ ├── .env │ ├── Cargo.toml │ ├── Dockerfile │ ├── diesel.toml │ ├── images │ ├── earth.jpg │ ├── jupiter.jpg │ ├── mars.jpg │ ├── mercury.jpg │ ├── neptune.jpg │ ├── saturn.jpg │ ├── uranus.jpg │ └── venus.jpg │ ├── migrations │ ├── 00000000000000_diesel_initial_setup │ │ ├── down.sql │ │ └── up.sql │ ├── 2021-02-20-104857_init_structure │ │ ├── down.sql │ │ └── up.sql │ └── 2021-02-20-104902_init_data │ │ ├── down.sql │ │ └── up.sql │ └── src │ ├── conversion.rs │ ├── main.rs │ └── persistence │ ├── connection.rs │ ├── mod.rs │ ├── model.rs │ ├── repository.rs │ └── schema.rs ├── mongodb-redis ├── .env ├── .env.local ├── Cargo.toml ├── Dockerfile ├── docker-compose.override.yml ├── docker-compose.yml ├── images │ ├── earth.jpg │ ├── jupiter.jpg │ ├── mars.jpg │ ├── mercury.jpg │ ├── neptune.jpg │ ├── saturn.jpg │ ├── uranus.jpg │ └── venus.jpg ├── mongodb-init │ └── init.json ├── monitoring │ ├── alertmanager │ │ └── alertmanager.yml │ ├── grafana │ │ └── provisioning │ │ │ ├── dashboards │ │ │ ├── providers.yml │ │ │ └── webapp_metrics.json │ │ │ └── datasources │ │ │ └── datasources.yml │ └── prometheus │ │ ├── prometheus.yml │ │ └── rules.yml ├── readme.adoc └── src │ ├── broadcaster.rs │ ├── db.rs │ ├── dto.rs │ ├── errors.rs │ ├── handlers.rs │ ├── index.html │ ├── main.rs │ ├── metrics.rs │ ├── model.rs │ ├── redis.rs │ └── services.rs └── readme.adoc /.dockerignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | 3 | /solar-system-info/*/Dockerfile 4 | /solar-system-info/*/.env 5 | 6 | /mongodb-redis/Dockerfile 7 | /mongodb-redis/.env 8 | -------------------------------------------------------------------------------- /.github/deploy-grpc-telegram-bot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | shopt -s expand_aliases 3 | alias docker-compose='docker run --rm \ 4 | -v /var/run/docker.sock:/var/run/docker.sock \ 5 | -v "$PWD:$PWD" \ 6 | -w="$PWD" \ 7 | docker/compose:1.29.2' 8 | 9 | cd ~/solar-system-info 10 | 11 | docker system prune 12 | docker-compose pull 13 | docker-compose down 14 | docker-compose up -d 15 | -------------------------------------------------------------------------------- /.github/deploy-mongodb-redis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | shopt -s expand_aliases 3 | alias docker-compose='docker run --rm \ 4 | -v /var/run/docker.sock:/var/run/docker.sock \ 5 | -v "$PWD:$PWD" \ 6 | -w="$PWD" \ 7 | docker/compose:1.29.2' 8 | 9 | cd ~/mongodb-redis 10 | 11 | docker system prune 12 | docker-compose pull 13 | docker-compose down 14 | docker-compose up -d 15 | -------------------------------------------------------------------------------- /.github/workflows/grpc-telegram-bot.yml: -------------------------------------------------------------------------------- 1 | name: gRPC/Telegram bot CI/CD 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | solar-system-info-server-image: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | - uses: docker/build-push-action@v1.1.2 18 | with: 19 | username: ${{ secrets.DOCKER_USERNAME }} 20 | password: ${{ secrets.DOCKER_PASSWORD }} 21 | repository: kudryashovroman/solar-system-info 22 | path: . 23 | dockerfile: ./grpc-telegram-bot/server/Dockerfile 24 | tags: server 25 | push: ${{ github.event_name == 'push' }} 26 | 27 | solar-system-info-bot-image: 28 | runs-on: ubuntu-latest 29 | steps: 30 | - uses: actions/checkout@v2 31 | - uses: docker/build-push-action@v1.1.2 32 | with: 33 | username: ${{ secrets.DOCKER_USERNAME }} 34 | password: ${{ secrets.DOCKER_PASSWORD }} 35 | repository: kudryashovroman/solar-system-info 36 | path: . 37 | dockerfile: ./grpc-telegram-bot/bot/Dockerfile 38 | tags: bot 39 | push: ${{ github.event_name == 'push' }} 40 | 41 | nginx-image: 42 | runs-on: ubuntu-latest 43 | steps: 44 | - uses: actions/checkout@v2 45 | - uses: docker/build-push-action@v1.1.2 46 | with: 47 | username: ${{ secrets.DOCKER_USERNAME }} 48 | password: ${{ secrets.DOCKER_PASSWORD }} 49 | repository: kudryashovroman/solar-system-info 50 | path: ./grpc-telegram-bot/nginx 51 | dockerfile: ./grpc-telegram-bot/nginx/Dockerfile 52 | tags: nginx 53 | push: ${{ github.event_name == 'push' }} 54 | 55 | deploy: 56 | if: ${{ github.event_name == 'push' }} 57 | needs: [ solar-system-info-server-image, solar-system-info-bot-image, nginx-image ] 58 | runs-on: ubuntu-latest 59 | steps: 60 | - name: Checkout 61 | uses: actions/checkout@v2 62 | 63 | - name: Setup gcloud CLI 64 | uses: google-github-actions/setup-gcloud@master 65 | with: 66 | service_account_key: ${{ secrets.GCE_SA_KEY }} 67 | project_id: ${{ secrets.GCE_PROJECT }} 68 | 69 | - run: | 70 | sed -i 's//${{ secrets.TELOXIDE_TOKEN }}/g' ./grpc-telegram-bot/.env 71 | 72 | - run: |- 73 | gcloud config set compute/zone ${{ secrets.GCE_INSTANCE_ZONE }} 74 | 75 | - run: |- 76 | gcloud compute ssh ${{ secrets.GCE_INSTANCE }} --command 'mkdir -p ~/solar-system-info' 77 | 78 | - run: |- 79 | gcloud compute scp --quiet ./grpc-telegram-bot/docker-compose.yml ./grpc-telegram-bot/.env ./.github/deploy-grpc-telegram-bot.sh ${{ secrets.GCE_INSTANCE }}:~/solar-system-info/ 80 | 81 | - run: |- 82 | gcloud compute ssh ${{ secrets.GCE_INSTANCE }} --command 'bash ~/solar-system-info/deploy-grpc-telegram-bot.sh' 83 | -------------------------------------------------------------------------------- /.github/workflows/mongodb-redis.yml: -------------------------------------------------------------------------------- 1 | name: MongoDB/Redis CI/CD 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | mongodb-redis-image: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | - uses: docker/build-push-action@v1.1.2 18 | with: 19 | username: ${{ secrets.DOCKER_USERNAME }} 20 | password: ${{ secrets.DOCKER_PASSWORD }} 21 | repository: kudryashovroman/mongodb-redis 22 | path: . 23 | dockerfile: ./mongodb-redis/Dockerfile 24 | tags: latest 25 | push: ${{ github.event_name == 'push' }} 26 | 27 | deploy: 28 | if: ${{ github.event_name == 'push' }} 29 | needs: [ mongodb-redis-image ] 30 | runs-on: ubuntu-latest 31 | steps: 32 | - name: Checkout 33 | uses: actions/checkout@v2 34 | 35 | - name: Setup gcloud CLI 36 | uses: google-github-actions/setup-gcloud@master 37 | with: 38 | service_account_key: ${{ secrets.GCE_SA_KEY }} 39 | project_id: ${{ secrets.GCE_PROJECT }} 40 | 41 | - run: |- 42 | gcloud config set compute/zone ${{ secrets.GCE_INSTANCE_ZONE }} 43 | 44 | - run: |- 45 | gcloud compute ssh ${{ secrets.GCE_INSTANCE }} --command 'mkdir -p ~/mongodb-redis' 46 | 47 | - run: |- 48 | gcloud compute scp --recurse --quiet ./mongodb-redis/docker-compose.yml ./mongodb-redis/.env ./mongodb-redis/mongodb-init ./.github/deploy-mongodb-redis.sh ${{ secrets.GCE_INSTANCE }}:~/mongodb-redis/ 49 | 50 | - run: |- 51 | gcloud compute ssh ${{ secrets.GCE_INSTANCE }} --command 'bash ~/mongodb-redis/deploy-mongodb-redis.sh' 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.idea/ 3 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "actix-codec" 7 | version = "0.4.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "13895df506faee81e423febbae3a33b27fca71831b96bb3d60adf16ebcfea952" 10 | dependencies = [ 11 | "bitflags", 12 | "bytes", 13 | "futures-core", 14 | "futures-sink", 15 | "log", 16 | "memchr", 17 | "pin-project-lite", 18 | "tokio", 19 | "tokio-util", 20 | ] 21 | 22 | [[package]] 23 | name = "actix-http" 24 | version = "3.0.0-beta.16" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "6294c508c1413346857838356f53f45dbfd257ea31dca19470d9ce78750a7d37" 27 | dependencies = [ 28 | "actix-codec", 29 | "actix-rt", 30 | "actix-service", 31 | "actix-utils", 32 | "ahash 0.7.6", 33 | "base64", 34 | "bitflags", 35 | "brotli2", 36 | "bytes", 37 | "bytestring", 38 | "derive_more", 39 | "encoding_rs", 40 | "flate2", 41 | "futures-core", 42 | "futures-task", 43 | "h2", 44 | "http", 45 | "httparse", 46 | "httpdate", 47 | "itoa 0.4.8", 48 | "language-tags", 49 | "local-channel", 50 | "log", 51 | "mime", 52 | "percent-encoding", 53 | "pin-project-lite", 54 | "rand", 55 | "sha-1", 56 | "smallvec", 57 | "zstd", 58 | ] 59 | 60 | [[package]] 61 | name = "actix-macros" 62 | version = "0.2.3" 63 | source = "registry+https://github.com/rust-lang/crates.io-index" 64 | checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" 65 | dependencies = [ 66 | "quote", 67 | "syn", 68 | ] 69 | 70 | [[package]] 71 | name = "actix-router" 72 | version = "0.5.0-beta.3" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "ddd9f117b910fbcce6e9f45092ffd4ff017785a346d09e2d4fd049f4e20384f4" 75 | dependencies = [ 76 | "bytestring", 77 | "firestorm", 78 | "http", 79 | "log", 80 | "regex", 81 | "serde", 82 | ] 83 | 84 | [[package]] 85 | name = "actix-rt" 86 | version = "2.5.0" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "05c2f80ce8d0c990941c7a7a931f69fd0701b76d521f8d36298edf59cd3fbf1f" 89 | dependencies = [ 90 | "actix-macros", 91 | "futures-core", 92 | "tokio", 93 | ] 94 | 95 | [[package]] 96 | name = "actix-server" 97 | version = "2.0.0-rc.1" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "78c9b22794b8af1c2e02434873ef858f2a7db40dbbf861ce77a04cd81ac6b767" 100 | dependencies = [ 101 | "actix-rt", 102 | "actix-service", 103 | "actix-utils", 104 | "futures-core", 105 | "futures-util", 106 | "log", 107 | "mio 0.8.0", 108 | "num_cpus", 109 | "socket2 0.4.2", 110 | "tokio", 111 | ] 112 | 113 | [[package]] 114 | name = "actix-service" 115 | version = "2.0.2" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" 118 | dependencies = [ 119 | "futures-core", 120 | "paste", 121 | "pin-project-lite", 122 | ] 123 | 124 | [[package]] 125 | name = "actix-utils" 126 | version = "3.0.0" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "e491cbaac2e7fc788dfff99ff48ef317e23b3cf63dbaf7aaab6418f40f92aa94" 129 | dependencies = [ 130 | "local-waker", 131 | "pin-project-lite", 132 | ] 133 | 134 | [[package]] 135 | name = "actix-web" 136 | version = "4.0.0-beta.15" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "4609cf57246040316642d4dc4c03d7f3d4a083a892122829dbd9e6ec8db7cd67" 139 | dependencies = [ 140 | "actix-codec", 141 | "actix-http", 142 | "actix-macros", 143 | "actix-router", 144 | "actix-rt", 145 | "actix-server", 146 | "actix-service", 147 | "actix-utils", 148 | "actix-web-codegen", 149 | "ahash 0.7.6", 150 | "bytes", 151 | "cfg-if 1.0.0", 152 | "cookie", 153 | "derive_more", 154 | "encoding_rs", 155 | "futures-core", 156 | "futures-util", 157 | "itoa 0.4.8", 158 | "language-tags", 159 | "log", 160 | "mime", 161 | "once_cell", 162 | "paste", 163 | "pin-project-lite", 164 | "regex", 165 | "serde", 166 | "serde_json", 167 | "serde_urlencoded", 168 | "smallvec", 169 | "socket2 0.4.2", 170 | "time 0.3.5", 171 | "url", 172 | ] 173 | 174 | [[package]] 175 | name = "actix-web-codegen" 176 | version = "0.5.0-beta.6" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "30a90b7f6c2fde9a1fe3df4da758c2c3c9d620dfa3eae4da0b6925dc0a13444a" 179 | dependencies = [ 180 | "actix-router", 181 | "proc-macro2", 182 | "quote", 183 | "syn", 184 | ] 185 | 186 | [[package]] 187 | name = "adler" 188 | version = "1.0.2" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 191 | 192 | [[package]] 193 | name = "ahash" 194 | version = "0.3.8" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" 197 | 198 | [[package]] 199 | name = "ahash" 200 | version = "0.7.6" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" 203 | dependencies = [ 204 | "getrandom", 205 | "once_cell", 206 | "version_check", 207 | ] 208 | 209 | [[package]] 210 | name = "aho-corasick" 211 | version = "0.7.18" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 214 | dependencies = [ 215 | "memchr", 216 | ] 217 | 218 | [[package]] 219 | name = "anyhow" 220 | version = "1.0.51" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "8b26702f315f53b6071259e15dd9d64528213b44d61de1ec926eca7715d62203" 223 | 224 | [[package]] 225 | name = "arc-swap" 226 | version = "1.5.0" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "c5d78ce20460b82d3fa150275ed9d55e21064fc7951177baacf86a145c4a4b1f" 229 | 230 | [[package]] 231 | name = "async-stream" 232 | version = "0.3.2" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "171374e7e3b2504e0e5236e3b59260560f9fe94bfe9ac39ba5e4e929c5590625" 235 | dependencies = [ 236 | "async-stream-impl", 237 | "futures-core", 238 | ] 239 | 240 | [[package]] 241 | name = "async-stream-impl" 242 | version = "0.3.2" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "648ed8c8d2ce5409ccd57453d9d1b214b342a0d69376a6feda1fd6cae3299308" 245 | dependencies = [ 246 | "proc-macro2", 247 | "quote", 248 | "syn", 249 | ] 250 | 251 | [[package]] 252 | name = "async-trait" 253 | version = "0.1.52" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3" 256 | dependencies = [ 257 | "proc-macro2", 258 | "quote", 259 | "syn", 260 | ] 261 | 262 | [[package]] 263 | name = "atty" 264 | version = "0.2.14" 265 | source = "registry+https://github.com/rust-lang/crates.io-index" 266 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 267 | dependencies = [ 268 | "hermit-abi", 269 | "libc", 270 | "winapi", 271 | ] 272 | 273 | [[package]] 274 | name = "autocfg" 275 | version = "1.0.1" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 278 | 279 | [[package]] 280 | name = "base-x" 281 | version = "0.2.8" 282 | source = "registry+https://github.com/rust-lang/crates.io-index" 283 | checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" 284 | 285 | [[package]] 286 | name = "base64" 287 | version = "0.13.0" 288 | source = "registry+https://github.com/rust-lang/crates.io-index" 289 | checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" 290 | 291 | [[package]] 292 | name = "bigdecimal" 293 | version = "0.1.2" 294 | source = "registry+https://github.com/rust-lang/crates.io-index" 295 | checksum = "1374191e2dd25f9ae02e3aa95041ed5d747fc77b3c102b49fe2dd9a8117a6244" 296 | dependencies = [ 297 | "num-bigint", 298 | "num-integer", 299 | "num-traits", 300 | ] 301 | 302 | [[package]] 303 | name = "bitflags" 304 | version = "1.3.2" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 307 | 308 | [[package]] 309 | name = "block-buffer" 310 | version = "0.9.0" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" 313 | dependencies = [ 314 | "generic-array", 315 | ] 316 | 317 | [[package]] 318 | name = "brotli-sys" 319 | version = "0.3.2" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "4445dea95f4c2b41cde57cc9fee236ae4dbae88d8fcbdb4750fc1bb5d86aaecd" 322 | dependencies = [ 323 | "cc", 324 | "libc", 325 | ] 326 | 327 | [[package]] 328 | name = "brotli2" 329 | version = "0.3.2" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "0cb036c3eade309815c15ddbacec5b22c4d1f3983a774ab2eac2e3e9ea85568e" 332 | dependencies = [ 333 | "brotli-sys", 334 | "libc", 335 | ] 336 | 337 | [[package]] 338 | name = "bson" 339 | version = "2.1.0" 340 | source = "registry+https://github.com/rust-lang/crates.io-index" 341 | checksum = "41539b5c502b7c4e7b8af8ef07e5c442fe79ceba62a2aad8e62bd589b9454745" 342 | dependencies = [ 343 | "ahash 0.7.6", 344 | "base64", 345 | "chrono", 346 | "hex", 347 | "indexmap", 348 | "lazy_static", 349 | "rand", 350 | "serde", 351 | "serde_bytes", 352 | "serde_json", 353 | "uuid", 354 | ] 355 | 356 | [[package]] 357 | name = "bumpalo" 358 | version = "3.8.0" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" 361 | 362 | [[package]] 363 | name = "byteorder" 364 | version = "1.4.3" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 367 | 368 | [[package]] 369 | name = "bytes" 370 | version = "1.1.0" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" 373 | 374 | [[package]] 375 | name = "bytestring" 376 | version = "1.0.0" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "90706ba19e97b90786e19dc0d5e2abd80008d99d4c0c5d1ad0b5e72cec7c494d" 379 | dependencies = [ 380 | "bytes", 381 | ] 382 | 383 | [[package]] 384 | name = "cc" 385 | version = "1.0.72" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" 388 | dependencies = [ 389 | "jobserver", 390 | ] 391 | 392 | [[package]] 393 | name = "cfg-if" 394 | version = "0.1.10" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 397 | 398 | [[package]] 399 | name = "cfg-if" 400 | version = "1.0.0" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 403 | 404 | [[package]] 405 | name = "chrono" 406 | version = "0.4.19" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" 409 | dependencies = [ 410 | "libc", 411 | "num-integer", 412 | "num-traits", 413 | "serde", 414 | "time 0.1.44", 415 | "winapi", 416 | ] 417 | 418 | [[package]] 419 | name = "cloudabi" 420 | version = "0.0.3" 421 | source = "registry+https://github.com/rust-lang/crates.io-index" 422 | checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" 423 | dependencies = [ 424 | "bitflags", 425 | ] 426 | 427 | [[package]] 428 | name = "combine" 429 | version = "4.6.2" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | checksum = "b2b2f5d0ee456f3928812dfc8c6d9a1d592b98678f6d56db9b0cd2b7bc6c8db5" 432 | dependencies = [ 433 | "bytes", 434 | "futures-core", 435 | "memchr", 436 | "pin-project-lite", 437 | "tokio", 438 | "tokio-util", 439 | ] 440 | 441 | [[package]] 442 | name = "const_fn" 443 | version = "0.4.8" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "f92cfa0fd5690b3cf8c1ef2cabbd9b7ef22fa53cf5e1f92b05103f6d5d1cf6e7" 446 | 447 | [[package]] 448 | name = "convert_case" 449 | version = "0.4.0" 450 | source = "registry+https://github.com/rust-lang/crates.io-index" 451 | checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" 452 | 453 | [[package]] 454 | name = "cookie" 455 | version = "0.15.1" 456 | source = "registry+https://github.com/rust-lang/crates.io-index" 457 | checksum = "d5f1c7727e460397e56abc4bddc1d49e07a1ad78fc98eb2e1c8f032a58a2f80d" 458 | dependencies = [ 459 | "percent-encoding", 460 | "time 0.2.27", 461 | "version_check", 462 | ] 463 | 464 | [[package]] 465 | name = "core-foundation" 466 | version = "0.9.2" 467 | source = "registry+https://github.com/rust-lang/crates.io-index" 468 | checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" 469 | dependencies = [ 470 | "core-foundation-sys", 471 | "libc", 472 | ] 473 | 474 | [[package]] 475 | name = "core-foundation-sys" 476 | version = "0.8.3" 477 | source = "registry+https://github.com/rust-lang/crates.io-index" 478 | checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" 479 | 480 | [[package]] 481 | name = "cpufeatures" 482 | version = "0.2.1" 483 | source = "registry+https://github.com/rust-lang/crates.io-index" 484 | checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" 485 | dependencies = [ 486 | "libc", 487 | ] 488 | 489 | [[package]] 490 | name = "crc32fast" 491 | version = "1.3.0" 492 | source = "registry+https://github.com/rust-lang/crates.io-index" 493 | checksum = "738c290dfaea84fc1ca15ad9c168d083b05a714e1efddd8edaab678dc28d2836" 494 | dependencies = [ 495 | "cfg-if 1.0.0", 496 | ] 497 | 498 | [[package]] 499 | name = "crossbeam-epoch" 500 | version = "0.8.2" 501 | source = "registry+https://github.com/rust-lang/crates.io-index" 502 | checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" 503 | dependencies = [ 504 | "autocfg", 505 | "cfg-if 0.1.10", 506 | "crossbeam-utils", 507 | "lazy_static", 508 | "maybe-uninit", 509 | "memoffset", 510 | "scopeguard", 511 | ] 512 | 513 | [[package]] 514 | name = "crossbeam-utils" 515 | version = "0.7.2" 516 | source = "registry+https://github.com/rust-lang/crates.io-index" 517 | checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" 518 | dependencies = [ 519 | "autocfg", 520 | "cfg-if 0.1.10", 521 | "lazy_static", 522 | ] 523 | 524 | [[package]] 525 | name = "crypto-mac" 526 | version = "0.11.1" 527 | source = "registry+https://github.com/rust-lang/crates.io-index" 528 | checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" 529 | dependencies = [ 530 | "generic-array", 531 | "subtle", 532 | ] 533 | 534 | [[package]] 535 | name = "darling" 536 | version = "0.13.1" 537 | source = "registry+https://github.com/rust-lang/crates.io-index" 538 | checksum = "d0d720b8683f8dd83c65155f0530560cba68cd2bf395f6513a483caee57ff7f4" 539 | dependencies = [ 540 | "darling_core", 541 | "darling_macro", 542 | ] 543 | 544 | [[package]] 545 | name = "darling_core" 546 | version = "0.13.1" 547 | source = "registry+https://github.com/rust-lang/crates.io-index" 548 | checksum = "7a340f241d2ceed1deb47ae36c4144b2707ec7dd0b649f894cb39bb595986324" 549 | dependencies = [ 550 | "fnv", 551 | "ident_case", 552 | "proc-macro2", 553 | "quote", 554 | "strsim", 555 | "syn", 556 | ] 557 | 558 | [[package]] 559 | name = "darling_macro" 560 | version = "0.13.1" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | checksum = "72c41b3b7352feb3211a0d743dc5700a4e3b60f51bd2b368892d1e0f9a95f44b" 563 | dependencies = [ 564 | "darling_core", 565 | "quote", 566 | "syn", 567 | ] 568 | 569 | [[package]] 570 | name = "data-encoding" 571 | version = "2.3.2" 572 | source = "registry+https://github.com/rust-lang/crates.io-index" 573 | checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" 574 | 575 | [[package]] 576 | name = "derivative" 577 | version = "2.2.0" 578 | source = "registry+https://github.com/rust-lang/crates.io-index" 579 | checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" 580 | dependencies = [ 581 | "proc-macro2", 582 | "quote", 583 | "syn", 584 | ] 585 | 586 | [[package]] 587 | name = "derive_more" 588 | version = "0.99.17" 589 | source = "registry+https://github.com/rust-lang/crates.io-index" 590 | checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" 591 | dependencies = [ 592 | "convert_case", 593 | "proc-macro2", 594 | "quote", 595 | "rustc_version 0.4.0", 596 | "syn", 597 | ] 598 | 599 | [[package]] 600 | name = "diesel" 601 | version = "1.4.8" 602 | source = "registry+https://github.com/rust-lang/crates.io-index" 603 | checksum = "b28135ecf6b7d446b43e27e225622a038cc4e2930a1022f51cdb97ada19b8e4d" 604 | dependencies = [ 605 | "bigdecimal", 606 | "bitflags", 607 | "byteorder", 608 | "chrono", 609 | "diesel_derives", 610 | "num-bigint", 611 | "num-integer", 612 | "num-traits", 613 | "pq-sys", 614 | "r2d2", 615 | ] 616 | 617 | [[package]] 618 | name = "diesel_derives" 619 | version = "1.4.1" 620 | source = "registry+https://github.com/rust-lang/crates.io-index" 621 | checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3" 622 | dependencies = [ 623 | "proc-macro2", 624 | "quote", 625 | "syn", 626 | ] 627 | 628 | [[package]] 629 | name = "diesel_migrations" 630 | version = "1.4.0" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "bf3cde8413353dc7f5d72fa8ce0b99a560a359d2c5ef1e5817ca731cd9008f4c" 633 | dependencies = [ 634 | "migrations_internals", 635 | "migrations_macros", 636 | ] 637 | 638 | [[package]] 639 | name = "digest" 640 | version = "0.9.0" 641 | source = "registry+https://github.com/rust-lang/crates.io-index" 642 | checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" 643 | dependencies = [ 644 | "generic-array", 645 | ] 646 | 647 | [[package]] 648 | name = "discard" 649 | version = "1.0.4" 650 | source = "registry+https://github.com/rust-lang/crates.io-index" 651 | checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" 652 | 653 | [[package]] 654 | name = "dotenv" 655 | version = "0.15.0" 656 | source = "registry+https://github.com/rust-lang/crates.io-index" 657 | checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" 658 | 659 | [[package]] 660 | name = "dtoa" 661 | version = "0.4.8" 662 | source = "registry+https://github.com/rust-lang/crates.io-index" 663 | checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" 664 | 665 | [[package]] 666 | name = "either" 667 | version = "1.6.1" 668 | source = "registry+https://github.com/rust-lang/crates.io-index" 669 | checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" 670 | 671 | [[package]] 672 | name = "encoding_rs" 673 | version = "0.8.30" 674 | source = "registry+https://github.com/rust-lang/crates.io-index" 675 | checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df" 676 | dependencies = [ 677 | "cfg-if 1.0.0", 678 | ] 679 | 680 | [[package]] 681 | name = "enum-as-inner" 682 | version = "0.3.3" 683 | source = "registry+https://github.com/rust-lang/crates.io-index" 684 | checksum = "7c5f0096a91d210159eceb2ff5e1c4da18388a170e1e3ce948aac9c8fdbbf595" 685 | dependencies = [ 686 | "heck", 687 | "proc-macro2", 688 | "quote", 689 | "syn", 690 | ] 691 | 692 | [[package]] 693 | name = "env_logger" 694 | version = "0.7.1" 695 | source = "registry+https://github.com/rust-lang/crates.io-index" 696 | checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" 697 | dependencies = [ 698 | "atty", 699 | "humantime 1.3.0", 700 | "log", 701 | "regex", 702 | "termcolor", 703 | ] 704 | 705 | [[package]] 706 | name = "env_logger" 707 | version = "0.9.0" 708 | source = "registry+https://github.com/rust-lang/crates.io-index" 709 | checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" 710 | dependencies = [ 711 | "atty", 712 | "humantime 2.1.0", 713 | "log", 714 | "regex", 715 | "termcolor", 716 | ] 717 | 718 | [[package]] 719 | name = "firestorm" 720 | version = "0.4.6" 721 | source = "registry+https://github.com/rust-lang/crates.io-index" 722 | checksum = "31586bda1b136406162e381a3185a506cdfc1631708dd40cba2f6628d8634499" 723 | 724 | [[package]] 725 | name = "fixedbitset" 726 | version = "0.4.0" 727 | source = "registry+https://github.com/rust-lang/crates.io-index" 728 | checksum = "398ea4fabe40b9b0d885340a2a991a44c8a645624075ad966d21f88688e2b69e" 729 | 730 | [[package]] 731 | name = "flate2" 732 | version = "1.0.22" 733 | source = "registry+https://github.com/rust-lang/crates.io-index" 734 | checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" 735 | dependencies = [ 736 | "cfg-if 1.0.0", 737 | "crc32fast", 738 | "libc", 739 | "miniz_oxide", 740 | ] 741 | 742 | [[package]] 743 | name = "flurry" 744 | version = "0.3.1" 745 | source = "registry+https://github.com/rust-lang/crates.io-index" 746 | checksum = "8c0a35f7b50e99185a2825541946252f669f3c3ca77801357cd682a1b356bb3e" 747 | dependencies = [ 748 | "ahash 0.3.8", 749 | "crossbeam-epoch", 750 | "num_cpus", 751 | "parking_lot 0.10.2", 752 | ] 753 | 754 | [[package]] 755 | name = "fnv" 756 | version = "1.0.7" 757 | source = "registry+https://github.com/rust-lang/crates.io-index" 758 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 759 | 760 | [[package]] 761 | name = "foreign-types" 762 | version = "0.3.2" 763 | source = "registry+https://github.com/rust-lang/crates.io-index" 764 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 765 | dependencies = [ 766 | "foreign-types-shared", 767 | ] 768 | 769 | [[package]] 770 | name = "foreign-types-shared" 771 | version = "0.1.1" 772 | source = "registry+https://github.com/rust-lang/crates.io-index" 773 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 774 | 775 | [[package]] 776 | name = "form_urlencoded" 777 | version = "1.0.1" 778 | source = "registry+https://github.com/rust-lang/crates.io-index" 779 | checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" 780 | dependencies = [ 781 | "matches", 782 | "percent-encoding", 783 | ] 784 | 785 | [[package]] 786 | name = "futures" 787 | version = "0.3.19" 788 | source = "registry+https://github.com/rust-lang/crates.io-index" 789 | checksum = "28560757fe2bb34e79f907794bb6b22ae8b0e5c669b638a1132f2592b19035b4" 790 | dependencies = [ 791 | "futures-channel", 792 | "futures-core", 793 | "futures-executor", 794 | "futures-io", 795 | "futures-sink", 796 | "futures-task", 797 | "futures-util", 798 | ] 799 | 800 | [[package]] 801 | name = "futures-channel" 802 | version = "0.3.19" 803 | source = "registry+https://github.com/rust-lang/crates.io-index" 804 | checksum = "ba3dda0b6588335f360afc675d0564c17a77a2bda81ca178a4b6081bd86c7f0b" 805 | dependencies = [ 806 | "futures-core", 807 | "futures-sink", 808 | ] 809 | 810 | [[package]] 811 | name = "futures-core" 812 | version = "0.3.19" 813 | source = "registry+https://github.com/rust-lang/crates.io-index" 814 | checksum = "d0c8ff0461b82559810cdccfde3215c3f373807f5e5232b71479bff7bb2583d7" 815 | 816 | [[package]] 817 | name = "futures-executor" 818 | version = "0.3.19" 819 | source = "registry+https://github.com/rust-lang/crates.io-index" 820 | checksum = "29d6d2ff5bb10fb95c85b8ce46538a2e5f5e7fdc755623a7d4529ab8a4ed9d2a" 821 | dependencies = [ 822 | "futures-core", 823 | "futures-task", 824 | "futures-util", 825 | ] 826 | 827 | [[package]] 828 | name = "futures-io" 829 | version = "0.3.19" 830 | source = "registry+https://github.com/rust-lang/crates.io-index" 831 | checksum = "b1f9d34af5a1aac6fb380f735fe510746c38067c5bf16c7fd250280503c971b2" 832 | 833 | [[package]] 834 | name = "futures-macro" 835 | version = "0.3.19" 836 | source = "registry+https://github.com/rust-lang/crates.io-index" 837 | checksum = "6dbd947adfffb0efc70599b3ddcf7b5597bb5fa9e245eb99f62b3a5f7bb8bd3c" 838 | dependencies = [ 839 | "proc-macro2", 840 | "quote", 841 | "syn", 842 | ] 843 | 844 | [[package]] 845 | name = "futures-sink" 846 | version = "0.3.19" 847 | source = "registry+https://github.com/rust-lang/crates.io-index" 848 | checksum = "e3055baccb68d74ff6480350f8d6eb8fcfa3aa11bdc1a1ae3afdd0514617d508" 849 | 850 | [[package]] 851 | name = "futures-task" 852 | version = "0.3.19" 853 | source = "registry+https://github.com/rust-lang/crates.io-index" 854 | checksum = "6ee7c6485c30167ce4dfb83ac568a849fe53274c831081476ee13e0dce1aad72" 855 | 856 | [[package]] 857 | name = "futures-util" 858 | version = "0.3.19" 859 | source = "registry+https://github.com/rust-lang/crates.io-index" 860 | checksum = "d9b5cf40b47a271f77a8b1bec03ca09044d99d2372c0de244e66430761127164" 861 | dependencies = [ 862 | "futures-channel", 863 | "futures-core", 864 | "futures-io", 865 | "futures-macro", 866 | "futures-sink", 867 | "futures-task", 868 | "memchr", 869 | "pin-project-lite", 870 | "pin-utils", 871 | "slab", 872 | ] 873 | 874 | [[package]] 875 | name = "generic-array" 876 | version = "0.14.4" 877 | source = "registry+https://github.com/rust-lang/crates.io-index" 878 | checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" 879 | dependencies = [ 880 | "typenum", 881 | "version_check", 882 | ] 883 | 884 | [[package]] 885 | name = "getrandom" 886 | version = "0.2.3" 887 | source = "registry+https://github.com/rust-lang/crates.io-index" 888 | checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" 889 | dependencies = [ 890 | "cfg-if 1.0.0", 891 | "libc", 892 | "wasi", 893 | ] 894 | 895 | [[package]] 896 | name = "h2" 897 | version = "0.3.9" 898 | source = "registry+https://github.com/rust-lang/crates.io-index" 899 | checksum = "8f072413d126e57991455e0a922b31e4c8ba7c2ffbebf6b78b4f8521397d65cd" 900 | dependencies = [ 901 | "bytes", 902 | "fnv", 903 | "futures-core", 904 | "futures-sink", 905 | "futures-util", 906 | "http", 907 | "indexmap", 908 | "slab", 909 | "tokio", 910 | "tokio-util", 911 | "tracing", 912 | ] 913 | 914 | [[package]] 915 | name = "hashbrown" 916 | version = "0.11.2" 917 | source = "registry+https://github.com/rust-lang/crates.io-index" 918 | checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" 919 | 920 | [[package]] 921 | name = "heck" 922 | version = "0.3.3" 923 | source = "registry+https://github.com/rust-lang/crates.io-index" 924 | checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" 925 | dependencies = [ 926 | "unicode-segmentation", 927 | ] 928 | 929 | [[package]] 930 | name = "hermit-abi" 931 | version = "0.1.19" 932 | source = "registry+https://github.com/rust-lang/crates.io-index" 933 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 934 | dependencies = [ 935 | "libc", 936 | ] 937 | 938 | [[package]] 939 | name = "hex" 940 | version = "0.4.3" 941 | source = "registry+https://github.com/rust-lang/crates.io-index" 942 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 943 | 944 | [[package]] 945 | name = "hmac" 946 | version = "0.11.0" 947 | source = "registry+https://github.com/rust-lang/crates.io-index" 948 | checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" 949 | dependencies = [ 950 | "crypto-mac", 951 | "digest", 952 | ] 953 | 954 | [[package]] 955 | name = "hostname" 956 | version = "0.3.1" 957 | source = "registry+https://github.com/rust-lang/crates.io-index" 958 | checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" 959 | dependencies = [ 960 | "libc", 961 | "match_cfg", 962 | "winapi", 963 | ] 964 | 965 | [[package]] 966 | name = "http" 967 | version = "0.2.5" 968 | source = "registry+https://github.com/rust-lang/crates.io-index" 969 | checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b" 970 | dependencies = [ 971 | "bytes", 972 | "fnv", 973 | "itoa 0.4.8", 974 | ] 975 | 976 | [[package]] 977 | name = "http-body" 978 | version = "0.4.4" 979 | source = "registry+https://github.com/rust-lang/crates.io-index" 980 | checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" 981 | dependencies = [ 982 | "bytes", 983 | "http", 984 | "pin-project-lite", 985 | ] 986 | 987 | [[package]] 988 | name = "httparse" 989 | version = "1.5.1" 990 | source = "registry+https://github.com/rust-lang/crates.io-index" 991 | checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" 992 | 993 | [[package]] 994 | name = "httpdate" 995 | version = "1.0.2" 996 | source = "registry+https://github.com/rust-lang/crates.io-index" 997 | checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" 998 | 999 | [[package]] 1000 | name = "humantime" 1001 | version = "1.3.0" 1002 | source = "registry+https://github.com/rust-lang/crates.io-index" 1003 | checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" 1004 | dependencies = [ 1005 | "quick-error", 1006 | ] 1007 | 1008 | [[package]] 1009 | name = "humantime" 1010 | version = "2.1.0" 1011 | source = "registry+https://github.com/rust-lang/crates.io-index" 1012 | checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" 1013 | 1014 | [[package]] 1015 | name = "hyper" 1016 | version = "0.14.16" 1017 | source = "registry+https://github.com/rust-lang/crates.io-index" 1018 | checksum = "b7ec3e62bdc98a2f0393a5048e4c30ef659440ea6e0e572965103e72bd836f55" 1019 | dependencies = [ 1020 | "bytes", 1021 | "futures-channel", 1022 | "futures-core", 1023 | "futures-util", 1024 | "h2", 1025 | "http", 1026 | "http-body", 1027 | "httparse", 1028 | "httpdate", 1029 | "itoa 0.4.8", 1030 | "pin-project-lite", 1031 | "socket2 0.4.2", 1032 | "tokio", 1033 | "tower-service", 1034 | "tracing", 1035 | "want", 1036 | ] 1037 | 1038 | [[package]] 1039 | name = "hyper-timeout" 1040 | version = "0.4.1" 1041 | source = "registry+https://github.com/rust-lang/crates.io-index" 1042 | checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" 1043 | dependencies = [ 1044 | "hyper", 1045 | "pin-project-lite", 1046 | "tokio", 1047 | "tokio-io-timeout", 1048 | ] 1049 | 1050 | [[package]] 1051 | name = "hyper-tls" 1052 | version = "0.5.0" 1053 | source = "registry+https://github.com/rust-lang/crates.io-index" 1054 | checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" 1055 | dependencies = [ 1056 | "bytes", 1057 | "hyper", 1058 | "native-tls", 1059 | "tokio", 1060 | "tokio-native-tls", 1061 | ] 1062 | 1063 | [[package]] 1064 | name = "ident_case" 1065 | version = "1.0.1" 1066 | source = "registry+https://github.com/rust-lang/crates.io-index" 1067 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 1068 | 1069 | [[package]] 1070 | name = "idna" 1071 | version = "0.2.3" 1072 | source = "registry+https://github.com/rust-lang/crates.io-index" 1073 | checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" 1074 | dependencies = [ 1075 | "matches", 1076 | "unicode-bidi", 1077 | "unicode-normalization", 1078 | ] 1079 | 1080 | [[package]] 1081 | name = "indexmap" 1082 | version = "1.7.0" 1083 | source = "registry+https://github.com/rust-lang/crates.io-index" 1084 | checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" 1085 | dependencies = [ 1086 | "autocfg", 1087 | "hashbrown", 1088 | ] 1089 | 1090 | [[package]] 1091 | name = "indoc" 1092 | version = "1.0.3" 1093 | source = "registry+https://github.com/rust-lang/crates.io-index" 1094 | checksum = "e5a75aeaaef0ce18b58056d306c27b07436fbb34b8816c53094b76dd81803136" 1095 | dependencies = [ 1096 | "unindent", 1097 | ] 1098 | 1099 | [[package]] 1100 | name = "instant" 1101 | version = "0.1.12" 1102 | source = "registry+https://github.com/rust-lang/crates.io-index" 1103 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 1104 | dependencies = [ 1105 | "cfg-if 1.0.0", 1106 | ] 1107 | 1108 | [[package]] 1109 | name = "ipconfig" 1110 | version = "0.2.2" 1111 | source = "registry+https://github.com/rust-lang/crates.io-index" 1112 | checksum = "f7e2f18aece9709094573a9f24f483c4f65caa4298e2f7ae1b71cc65d853fad7" 1113 | dependencies = [ 1114 | "socket2 0.3.19", 1115 | "widestring", 1116 | "winapi", 1117 | "winreg 0.6.2", 1118 | ] 1119 | 1120 | [[package]] 1121 | name = "ipnet" 1122 | version = "2.3.1" 1123 | source = "registry+https://github.com/rust-lang/crates.io-index" 1124 | checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" 1125 | 1126 | [[package]] 1127 | name = "itertools" 1128 | version = "0.10.3" 1129 | source = "registry+https://github.com/rust-lang/crates.io-index" 1130 | checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" 1131 | dependencies = [ 1132 | "either", 1133 | ] 1134 | 1135 | [[package]] 1136 | name = "itoa" 1137 | version = "0.4.8" 1138 | source = "registry+https://github.com/rust-lang/crates.io-index" 1139 | checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" 1140 | 1141 | [[package]] 1142 | name = "itoa" 1143 | version = "1.0.1" 1144 | source = "registry+https://github.com/rust-lang/crates.io-index" 1145 | checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" 1146 | 1147 | [[package]] 1148 | name = "jobserver" 1149 | version = "0.1.24" 1150 | source = "registry+https://github.com/rust-lang/crates.io-index" 1151 | checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" 1152 | dependencies = [ 1153 | "libc", 1154 | ] 1155 | 1156 | [[package]] 1157 | name = "js-sys" 1158 | version = "0.3.55" 1159 | source = "registry+https://github.com/rust-lang/crates.io-index" 1160 | checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" 1161 | dependencies = [ 1162 | "wasm-bindgen", 1163 | ] 1164 | 1165 | [[package]] 1166 | name = "language-tags" 1167 | version = "0.3.2" 1168 | source = "registry+https://github.com/rust-lang/crates.io-index" 1169 | checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" 1170 | 1171 | [[package]] 1172 | name = "lazy_static" 1173 | version = "1.4.0" 1174 | source = "registry+https://github.com/rust-lang/crates.io-index" 1175 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 1176 | 1177 | [[package]] 1178 | name = "libc" 1179 | version = "0.2.112" 1180 | source = "registry+https://github.com/rust-lang/crates.io-index" 1181 | checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" 1182 | 1183 | [[package]] 1184 | name = "linked-hash-map" 1185 | version = "0.5.4" 1186 | source = "registry+https://github.com/rust-lang/crates.io-index" 1187 | checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" 1188 | 1189 | [[package]] 1190 | name = "local-channel" 1191 | version = "0.1.2" 1192 | source = "registry+https://github.com/rust-lang/crates.io-index" 1193 | checksum = "6246c68cf195087205a0512559c97e15eaf95198bf0e206d662092cdcb03fe9f" 1194 | dependencies = [ 1195 | "futures-core", 1196 | "futures-sink", 1197 | "futures-util", 1198 | "local-waker", 1199 | ] 1200 | 1201 | [[package]] 1202 | name = "local-waker" 1203 | version = "0.1.2" 1204 | source = "registry+https://github.com/rust-lang/crates.io-index" 1205 | checksum = "902eb695eb0591864543cbfbf6d742510642a605a61fc5e97fe6ceb5a30ac4fb" 1206 | 1207 | [[package]] 1208 | name = "lock_api" 1209 | version = "0.3.4" 1210 | source = "registry+https://github.com/rust-lang/crates.io-index" 1211 | checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" 1212 | dependencies = [ 1213 | "scopeguard", 1214 | ] 1215 | 1216 | [[package]] 1217 | name = "lock_api" 1218 | version = "0.4.5" 1219 | source = "registry+https://github.com/rust-lang/crates.io-index" 1220 | checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" 1221 | dependencies = [ 1222 | "scopeguard", 1223 | ] 1224 | 1225 | [[package]] 1226 | name = "log" 1227 | version = "0.4.14" 1228 | source = "registry+https://github.com/rust-lang/crates.io-index" 1229 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 1230 | dependencies = [ 1231 | "cfg-if 1.0.0", 1232 | ] 1233 | 1234 | [[package]] 1235 | name = "lru-cache" 1236 | version = "0.1.2" 1237 | source = "registry+https://github.com/rust-lang/crates.io-index" 1238 | checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" 1239 | dependencies = [ 1240 | "linked-hash-map", 1241 | ] 1242 | 1243 | [[package]] 1244 | name = "match_cfg" 1245 | version = "0.1.0" 1246 | source = "registry+https://github.com/rust-lang/crates.io-index" 1247 | checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" 1248 | 1249 | [[package]] 1250 | name = "matches" 1251 | version = "0.1.9" 1252 | source = "registry+https://github.com/rust-lang/crates.io-index" 1253 | checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" 1254 | 1255 | [[package]] 1256 | name = "maybe-uninit" 1257 | version = "2.0.0" 1258 | source = "registry+https://github.com/rust-lang/crates.io-index" 1259 | checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" 1260 | 1261 | [[package]] 1262 | name = "md-5" 1263 | version = "0.9.1" 1264 | source = "registry+https://github.com/rust-lang/crates.io-index" 1265 | checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" 1266 | dependencies = [ 1267 | "block-buffer", 1268 | "digest", 1269 | "opaque-debug", 1270 | ] 1271 | 1272 | [[package]] 1273 | name = "memchr" 1274 | version = "2.4.1" 1275 | source = "registry+https://github.com/rust-lang/crates.io-index" 1276 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 1277 | 1278 | [[package]] 1279 | name = "memoffset" 1280 | version = "0.5.6" 1281 | source = "registry+https://github.com/rust-lang/crates.io-index" 1282 | checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" 1283 | dependencies = [ 1284 | "autocfg", 1285 | ] 1286 | 1287 | [[package]] 1288 | name = "migrations_internals" 1289 | version = "1.4.1" 1290 | source = "registry+https://github.com/rust-lang/crates.io-index" 1291 | checksum = "2b4fc84e4af020b837029e017966f86a1c2d5e83e64b589963d5047525995860" 1292 | dependencies = [ 1293 | "diesel", 1294 | ] 1295 | 1296 | [[package]] 1297 | name = "migrations_macros" 1298 | version = "1.4.2" 1299 | source = "registry+https://github.com/rust-lang/crates.io-index" 1300 | checksum = "9753f12909fd8d923f75ae5c3258cae1ed3c8ec052e1b38c93c21a6d157f789c" 1301 | dependencies = [ 1302 | "migrations_internals", 1303 | "proc-macro2", 1304 | "quote", 1305 | "syn", 1306 | ] 1307 | 1308 | [[package]] 1309 | name = "mime" 1310 | version = "0.3.16" 1311 | source = "registry+https://github.com/rust-lang/crates.io-index" 1312 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 1313 | 1314 | [[package]] 1315 | name = "mime_guess" 1316 | version = "2.0.3" 1317 | source = "registry+https://github.com/rust-lang/crates.io-index" 1318 | checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212" 1319 | dependencies = [ 1320 | "mime", 1321 | "unicase", 1322 | ] 1323 | 1324 | [[package]] 1325 | name = "miniz_oxide" 1326 | version = "0.4.4" 1327 | source = "registry+https://github.com/rust-lang/crates.io-index" 1328 | checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" 1329 | dependencies = [ 1330 | "adler", 1331 | "autocfg", 1332 | ] 1333 | 1334 | [[package]] 1335 | name = "mio" 1336 | version = "0.7.14" 1337 | source = "registry+https://github.com/rust-lang/crates.io-index" 1338 | checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" 1339 | dependencies = [ 1340 | "libc", 1341 | "log", 1342 | "miow", 1343 | "ntapi", 1344 | "winapi", 1345 | ] 1346 | 1347 | [[package]] 1348 | name = "mio" 1349 | version = "0.8.0" 1350 | source = "registry+https://github.com/rust-lang/crates.io-index" 1351 | checksum = "ba272f85fa0b41fc91872be579b3bbe0f56b792aa361a380eb669469f68dafb2" 1352 | dependencies = [ 1353 | "libc", 1354 | "log", 1355 | "miow", 1356 | "ntapi", 1357 | "winapi", 1358 | ] 1359 | 1360 | [[package]] 1361 | name = "miow" 1362 | version = "0.3.7" 1363 | source = "registry+https://github.com/rust-lang/crates.io-index" 1364 | checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" 1365 | dependencies = [ 1366 | "winapi", 1367 | ] 1368 | 1369 | [[package]] 1370 | name = "mongodb" 1371 | version = "2.1.0" 1372 | source = "registry+https://github.com/rust-lang/crates.io-index" 1373 | checksum = "bacb6f8cee6bf010d7bc57550d859f6a4ffe255eb8c9a7014637fe988eaece64" 1374 | dependencies = [ 1375 | "async-trait", 1376 | "base64", 1377 | "bitflags", 1378 | "bson", 1379 | "chrono", 1380 | "derivative", 1381 | "futures-core", 1382 | "futures-executor", 1383 | "futures-io", 1384 | "futures-util", 1385 | "hex", 1386 | "hmac", 1387 | "lazy_static", 1388 | "md-5", 1389 | "os_info", 1390 | "pbkdf2", 1391 | "percent-encoding", 1392 | "rand", 1393 | "rustls", 1394 | "rustls-pemfile", 1395 | "serde", 1396 | "serde_bytes", 1397 | "serde_with", 1398 | "sha-1", 1399 | "sha2", 1400 | "socket2 0.4.2", 1401 | "stringprep", 1402 | "strsim", 1403 | "take_mut", 1404 | "thiserror", 1405 | "tokio", 1406 | "tokio-rustls", 1407 | "tokio-util", 1408 | "trust-dns-proto", 1409 | "trust-dns-resolver", 1410 | "typed-builder", 1411 | "uuid", 1412 | "version_check", 1413 | "webpki", 1414 | "webpki-roots", 1415 | ] 1416 | 1417 | [[package]] 1418 | name = "mongodb-redis" 1419 | version = "0.1.0" 1420 | dependencies = [ 1421 | "actix-web", 1422 | "chrono", 1423 | "derive_more", 1424 | "dotenv", 1425 | "env_logger 0.9.0", 1426 | "lazy_static", 1427 | "log", 1428 | "mime", 1429 | "mongodb", 1430 | "prometheus", 1431 | "redis", 1432 | "rust-embed", 1433 | "serde", 1434 | "serde_json", 1435 | "tokio", 1436 | "tokio-stream", 1437 | ] 1438 | 1439 | [[package]] 1440 | name = "multimap" 1441 | version = "0.8.3" 1442 | source = "registry+https://github.com/rust-lang/crates.io-index" 1443 | checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" 1444 | 1445 | [[package]] 1446 | name = "native-tls" 1447 | version = "0.2.8" 1448 | source = "registry+https://github.com/rust-lang/crates.io-index" 1449 | checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d" 1450 | dependencies = [ 1451 | "lazy_static", 1452 | "libc", 1453 | "log", 1454 | "openssl", 1455 | "openssl-probe", 1456 | "openssl-sys", 1457 | "schannel", 1458 | "security-framework", 1459 | "security-framework-sys", 1460 | "tempfile", 1461 | ] 1462 | 1463 | [[package]] 1464 | name = "never" 1465 | version = "0.1.0" 1466 | source = "registry+https://github.com/rust-lang/crates.io-index" 1467 | checksum = "c96aba5aa877601bb3f6dd6a63a969e1f82e60646e81e71b14496995e9853c91" 1468 | 1469 | [[package]] 1470 | name = "ntapi" 1471 | version = "0.3.6" 1472 | source = "registry+https://github.com/rust-lang/crates.io-index" 1473 | checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" 1474 | dependencies = [ 1475 | "winapi", 1476 | ] 1477 | 1478 | [[package]] 1479 | name = "num-bigint" 1480 | version = "0.2.6" 1481 | source = "registry+https://github.com/rust-lang/crates.io-index" 1482 | checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" 1483 | dependencies = [ 1484 | "autocfg", 1485 | "num-integer", 1486 | "num-traits", 1487 | ] 1488 | 1489 | [[package]] 1490 | name = "num-integer" 1491 | version = "0.1.44" 1492 | source = "registry+https://github.com/rust-lang/crates.io-index" 1493 | checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" 1494 | dependencies = [ 1495 | "autocfg", 1496 | "num-traits", 1497 | ] 1498 | 1499 | [[package]] 1500 | name = "num-traits" 1501 | version = "0.2.14" 1502 | source = "registry+https://github.com/rust-lang/crates.io-index" 1503 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 1504 | dependencies = [ 1505 | "autocfg", 1506 | ] 1507 | 1508 | [[package]] 1509 | name = "num_cpus" 1510 | version = "1.13.0" 1511 | source = "registry+https://github.com/rust-lang/crates.io-index" 1512 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 1513 | dependencies = [ 1514 | "hermit-abi", 1515 | "libc", 1516 | ] 1517 | 1518 | [[package]] 1519 | name = "once_cell" 1520 | version = "1.9.0" 1521 | source = "registry+https://github.com/rust-lang/crates.io-index" 1522 | checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" 1523 | 1524 | [[package]] 1525 | name = "opaque-debug" 1526 | version = "0.3.0" 1527 | source = "registry+https://github.com/rust-lang/crates.io-index" 1528 | checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" 1529 | 1530 | [[package]] 1531 | name = "openssl" 1532 | version = "0.10.38" 1533 | source = "registry+https://github.com/rust-lang/crates.io-index" 1534 | checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95" 1535 | dependencies = [ 1536 | "bitflags", 1537 | "cfg-if 1.0.0", 1538 | "foreign-types", 1539 | "libc", 1540 | "once_cell", 1541 | "openssl-sys", 1542 | ] 1543 | 1544 | [[package]] 1545 | name = "openssl-probe" 1546 | version = "0.1.4" 1547 | source = "registry+https://github.com/rust-lang/crates.io-index" 1548 | checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" 1549 | 1550 | [[package]] 1551 | name = "openssl-sys" 1552 | version = "0.9.72" 1553 | source = "registry+https://github.com/rust-lang/crates.io-index" 1554 | checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb" 1555 | dependencies = [ 1556 | "autocfg", 1557 | "cc", 1558 | "libc", 1559 | "pkg-config", 1560 | "vcpkg", 1561 | ] 1562 | 1563 | [[package]] 1564 | name = "os_info" 1565 | version = "3.0.8" 1566 | source = "registry+https://github.com/rust-lang/crates.io-index" 1567 | checksum = "e5501659840950e918d046ad97ebe9702cbb4ec0097e47dbd27abf7692223181" 1568 | dependencies = [ 1569 | "log", 1570 | "winapi", 1571 | ] 1572 | 1573 | [[package]] 1574 | name = "parking_lot" 1575 | version = "0.10.2" 1576 | source = "registry+https://github.com/rust-lang/crates.io-index" 1577 | checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" 1578 | dependencies = [ 1579 | "lock_api 0.3.4", 1580 | "parking_lot_core 0.7.2", 1581 | ] 1582 | 1583 | [[package]] 1584 | name = "parking_lot" 1585 | version = "0.11.2" 1586 | source = "registry+https://github.com/rust-lang/crates.io-index" 1587 | checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" 1588 | dependencies = [ 1589 | "instant", 1590 | "lock_api 0.4.5", 1591 | "parking_lot_core 0.8.5", 1592 | ] 1593 | 1594 | [[package]] 1595 | name = "parking_lot_core" 1596 | version = "0.7.2" 1597 | source = "registry+https://github.com/rust-lang/crates.io-index" 1598 | checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" 1599 | dependencies = [ 1600 | "cfg-if 0.1.10", 1601 | "cloudabi", 1602 | "libc", 1603 | "redox_syscall 0.1.57", 1604 | "smallvec", 1605 | "winapi", 1606 | ] 1607 | 1608 | [[package]] 1609 | name = "parking_lot_core" 1610 | version = "0.8.5" 1611 | source = "registry+https://github.com/rust-lang/crates.io-index" 1612 | checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" 1613 | dependencies = [ 1614 | "cfg-if 1.0.0", 1615 | "instant", 1616 | "libc", 1617 | "redox_syscall 0.2.10", 1618 | "smallvec", 1619 | "winapi", 1620 | ] 1621 | 1622 | [[package]] 1623 | name = "paste" 1624 | version = "1.0.6" 1625 | source = "registry+https://github.com/rust-lang/crates.io-index" 1626 | checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5" 1627 | 1628 | [[package]] 1629 | name = "pbkdf2" 1630 | version = "0.8.0" 1631 | source = "registry+https://github.com/rust-lang/crates.io-index" 1632 | checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" 1633 | dependencies = [ 1634 | "crypto-mac", 1635 | ] 1636 | 1637 | [[package]] 1638 | name = "percent-encoding" 1639 | version = "2.1.0" 1640 | source = "registry+https://github.com/rust-lang/crates.io-index" 1641 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 1642 | 1643 | [[package]] 1644 | name = "petgraph" 1645 | version = "0.6.0" 1646 | source = "registry+https://github.com/rust-lang/crates.io-index" 1647 | checksum = "4a13a2fa9d0b63e5f22328828741e523766fff0ee9e779316902290dff3f824f" 1648 | dependencies = [ 1649 | "fixedbitset", 1650 | "indexmap", 1651 | ] 1652 | 1653 | [[package]] 1654 | name = "pin-project" 1655 | version = "1.0.8" 1656 | source = "registry+https://github.com/rust-lang/crates.io-index" 1657 | checksum = "576bc800220cc65dac09e99e97b08b358cfab6e17078de8dc5fee223bd2d0c08" 1658 | dependencies = [ 1659 | "pin-project-internal", 1660 | ] 1661 | 1662 | [[package]] 1663 | name = "pin-project-internal" 1664 | version = "1.0.8" 1665 | source = "registry+https://github.com/rust-lang/crates.io-index" 1666 | checksum = "6e8fe8163d14ce7f0cdac2e040116f22eac817edabff0be91e8aff7e9accf389" 1667 | dependencies = [ 1668 | "proc-macro2", 1669 | "quote", 1670 | "syn", 1671 | ] 1672 | 1673 | [[package]] 1674 | name = "pin-project-lite" 1675 | version = "0.2.7" 1676 | source = "registry+https://github.com/rust-lang/crates.io-index" 1677 | checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" 1678 | 1679 | [[package]] 1680 | name = "pin-utils" 1681 | version = "0.1.0" 1682 | source = "registry+https://github.com/rust-lang/crates.io-index" 1683 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1684 | 1685 | [[package]] 1686 | name = "pkg-config" 1687 | version = "0.3.24" 1688 | source = "registry+https://github.com/rust-lang/crates.io-index" 1689 | checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" 1690 | 1691 | [[package]] 1692 | name = "ppv-lite86" 1693 | version = "0.2.15" 1694 | source = "registry+https://github.com/rust-lang/crates.io-index" 1695 | checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" 1696 | 1697 | [[package]] 1698 | name = "pq-sys" 1699 | version = "0.4.6" 1700 | source = "registry+https://github.com/rust-lang/crates.io-index" 1701 | checksum = "6ac25eee5a0582f45a67e837e350d784e7003bd29a5f460796772061ca49ffda" 1702 | dependencies = [ 1703 | "vcpkg", 1704 | ] 1705 | 1706 | [[package]] 1707 | name = "pretty_env_logger" 1708 | version = "0.4.0" 1709 | source = "registry+https://github.com/rust-lang/crates.io-index" 1710 | checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" 1711 | dependencies = [ 1712 | "env_logger 0.7.1", 1713 | "log", 1714 | ] 1715 | 1716 | [[package]] 1717 | name = "proc-macro-hack" 1718 | version = "0.5.19" 1719 | source = "registry+https://github.com/rust-lang/crates.io-index" 1720 | checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" 1721 | 1722 | [[package]] 1723 | name = "proc-macro2" 1724 | version = "1.0.34" 1725 | source = "registry+https://github.com/rust-lang/crates.io-index" 1726 | checksum = "2f84e92c0f7c9d58328b85a78557813e4bd845130db68d7184635344399423b1" 1727 | dependencies = [ 1728 | "unicode-xid", 1729 | ] 1730 | 1731 | [[package]] 1732 | name = "procfs" 1733 | version = "0.10.1" 1734 | source = "registry+https://github.com/rust-lang/crates.io-index" 1735 | checksum = "95e344cafeaeefe487300c361654bcfc85db3ac53619eeccced29f5ea18c4c70" 1736 | dependencies = [ 1737 | "bitflags", 1738 | "byteorder", 1739 | "flate2", 1740 | "hex", 1741 | "lazy_static", 1742 | "libc", 1743 | ] 1744 | 1745 | [[package]] 1746 | name = "prometheus" 1747 | version = "0.13.0" 1748 | source = "registry+https://github.com/rust-lang/crates.io-index" 1749 | checksum = "b7f64969ffd5dd8f39bd57a68ac53c163a095ed9d0fb707146da1b27025a3504" 1750 | dependencies = [ 1751 | "cfg-if 1.0.0", 1752 | "fnv", 1753 | "lazy_static", 1754 | "libc", 1755 | "memchr", 1756 | "parking_lot 0.11.2", 1757 | "procfs", 1758 | "protobuf", 1759 | "thiserror", 1760 | ] 1761 | 1762 | [[package]] 1763 | name = "prost" 1764 | version = "0.9.0" 1765 | source = "registry+https://github.com/rust-lang/crates.io-index" 1766 | checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" 1767 | dependencies = [ 1768 | "bytes", 1769 | "prost-derive", 1770 | ] 1771 | 1772 | [[package]] 1773 | name = "prost-build" 1774 | version = "0.9.0" 1775 | source = "registry+https://github.com/rust-lang/crates.io-index" 1776 | checksum = "62941722fb675d463659e49c4f3fe1fe792ff24fe5bbaa9c08cd3b98a1c354f5" 1777 | dependencies = [ 1778 | "bytes", 1779 | "heck", 1780 | "itertools", 1781 | "lazy_static", 1782 | "log", 1783 | "multimap", 1784 | "petgraph", 1785 | "prost", 1786 | "prost-types", 1787 | "regex", 1788 | "tempfile", 1789 | "which", 1790 | ] 1791 | 1792 | [[package]] 1793 | name = "prost-derive" 1794 | version = "0.9.0" 1795 | source = "registry+https://github.com/rust-lang/crates.io-index" 1796 | checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe" 1797 | dependencies = [ 1798 | "anyhow", 1799 | "itertools", 1800 | "proc-macro2", 1801 | "quote", 1802 | "syn", 1803 | ] 1804 | 1805 | [[package]] 1806 | name = "prost-types" 1807 | version = "0.9.0" 1808 | source = "registry+https://github.com/rust-lang/crates.io-index" 1809 | checksum = "534b7a0e836e3c482d2693070f982e39e7611da9695d4d1f5a4b186b51faef0a" 1810 | dependencies = [ 1811 | "bytes", 1812 | "prost", 1813 | ] 1814 | 1815 | [[package]] 1816 | name = "protobuf" 1817 | version = "2.25.2" 1818 | source = "registry+https://github.com/rust-lang/crates.io-index" 1819 | checksum = "47c327e191621a2158159df97cdbc2e7074bb4e940275e35abf38eb3d2595754" 1820 | 1821 | [[package]] 1822 | name = "quick-error" 1823 | version = "1.2.3" 1824 | source = "registry+https://github.com/rust-lang/crates.io-index" 1825 | checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" 1826 | 1827 | [[package]] 1828 | name = "quote" 1829 | version = "1.0.10" 1830 | source = "registry+https://github.com/rust-lang/crates.io-index" 1831 | checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" 1832 | dependencies = [ 1833 | "proc-macro2", 1834 | ] 1835 | 1836 | [[package]] 1837 | name = "r2d2" 1838 | version = "0.8.9" 1839 | source = "registry+https://github.com/rust-lang/crates.io-index" 1840 | checksum = "545c5bc2b880973c9c10e4067418407a0ccaa3091781d1671d46eb35107cb26f" 1841 | dependencies = [ 1842 | "log", 1843 | "parking_lot 0.11.2", 1844 | "scheduled-thread-pool", 1845 | ] 1846 | 1847 | [[package]] 1848 | name = "rand" 1849 | version = "0.8.4" 1850 | source = "registry+https://github.com/rust-lang/crates.io-index" 1851 | checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" 1852 | dependencies = [ 1853 | "libc", 1854 | "rand_chacha", 1855 | "rand_core", 1856 | "rand_hc", 1857 | ] 1858 | 1859 | [[package]] 1860 | name = "rand_chacha" 1861 | version = "0.3.1" 1862 | source = "registry+https://github.com/rust-lang/crates.io-index" 1863 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1864 | dependencies = [ 1865 | "ppv-lite86", 1866 | "rand_core", 1867 | ] 1868 | 1869 | [[package]] 1870 | name = "rand_core" 1871 | version = "0.6.3" 1872 | source = "registry+https://github.com/rust-lang/crates.io-index" 1873 | checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" 1874 | dependencies = [ 1875 | "getrandom", 1876 | ] 1877 | 1878 | [[package]] 1879 | name = "rand_hc" 1880 | version = "0.3.1" 1881 | source = "registry+https://github.com/rust-lang/crates.io-index" 1882 | checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" 1883 | dependencies = [ 1884 | "rand_core", 1885 | ] 1886 | 1887 | [[package]] 1888 | name = "redis" 1889 | version = "0.21.4" 1890 | source = "registry+https://github.com/rust-lang/crates.io-index" 1891 | checksum = "7f23ceed4c0e76b322657c2c3352ea116f9ec60a1a1aefeb3c84ed062c50865b" 1892 | dependencies = [ 1893 | "arc-swap", 1894 | "async-trait", 1895 | "bytes", 1896 | "combine", 1897 | "dtoa", 1898 | "futures", 1899 | "futures-util", 1900 | "itoa 0.4.8", 1901 | "percent-encoding", 1902 | "pin-project-lite", 1903 | "sha1", 1904 | "tokio", 1905 | "tokio-util", 1906 | "url", 1907 | ] 1908 | 1909 | [[package]] 1910 | name = "redox_syscall" 1911 | version = "0.1.57" 1912 | source = "registry+https://github.com/rust-lang/crates.io-index" 1913 | checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" 1914 | 1915 | [[package]] 1916 | name = "redox_syscall" 1917 | version = "0.2.10" 1918 | source = "registry+https://github.com/rust-lang/crates.io-index" 1919 | checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" 1920 | dependencies = [ 1921 | "bitflags", 1922 | ] 1923 | 1924 | [[package]] 1925 | name = "regex" 1926 | version = "1.5.4" 1927 | source = "registry+https://github.com/rust-lang/crates.io-index" 1928 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" 1929 | dependencies = [ 1930 | "aho-corasick", 1931 | "memchr", 1932 | "regex-syntax", 1933 | ] 1934 | 1935 | [[package]] 1936 | name = "regex-syntax" 1937 | version = "0.6.25" 1938 | source = "registry+https://github.com/rust-lang/crates.io-index" 1939 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 1940 | 1941 | [[package]] 1942 | name = "remove_dir_all" 1943 | version = "0.5.3" 1944 | source = "registry+https://github.com/rust-lang/crates.io-index" 1945 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 1946 | dependencies = [ 1947 | "winapi", 1948 | ] 1949 | 1950 | [[package]] 1951 | name = "reqwest" 1952 | version = "0.11.7" 1953 | source = "registry+https://github.com/rust-lang/crates.io-index" 1954 | checksum = "07bea77bc708afa10e59905c3d4af7c8fd43c9214251673095ff8b14345fcbc5" 1955 | dependencies = [ 1956 | "base64", 1957 | "bytes", 1958 | "encoding_rs", 1959 | "futures-core", 1960 | "futures-util", 1961 | "http", 1962 | "http-body", 1963 | "hyper", 1964 | "hyper-tls", 1965 | "ipnet", 1966 | "js-sys", 1967 | "lazy_static", 1968 | "log", 1969 | "mime", 1970 | "mime_guess", 1971 | "native-tls", 1972 | "percent-encoding", 1973 | "pin-project-lite", 1974 | "serde", 1975 | "serde_json", 1976 | "serde_urlencoded", 1977 | "tokio", 1978 | "tokio-native-tls", 1979 | "tokio-util", 1980 | "url", 1981 | "wasm-bindgen", 1982 | "wasm-bindgen-futures", 1983 | "web-sys", 1984 | "winreg 0.7.0", 1985 | ] 1986 | 1987 | [[package]] 1988 | name = "resolv-conf" 1989 | version = "0.7.0" 1990 | source = "registry+https://github.com/rust-lang/crates.io-index" 1991 | checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" 1992 | dependencies = [ 1993 | "hostname", 1994 | "quick-error", 1995 | ] 1996 | 1997 | [[package]] 1998 | name = "ring" 1999 | version = "0.16.20" 2000 | source = "registry+https://github.com/rust-lang/crates.io-index" 2001 | checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" 2002 | dependencies = [ 2003 | "cc", 2004 | "libc", 2005 | "once_cell", 2006 | "spin", 2007 | "untrusted", 2008 | "web-sys", 2009 | "winapi", 2010 | ] 2011 | 2012 | [[package]] 2013 | name = "rust-embed" 2014 | version = "6.3.0" 2015 | source = "registry+https://github.com/rust-lang/crates.io-index" 2016 | checksum = "d40377bff8cceee81e28ddb73ac97f5c2856ce5522f0b260b763f434cdfae602" 2017 | dependencies = [ 2018 | "rust-embed-impl", 2019 | "rust-embed-utils", 2020 | "walkdir", 2021 | ] 2022 | 2023 | [[package]] 2024 | name = "rust-embed-impl" 2025 | version = "6.2.0" 2026 | source = "registry+https://github.com/rust-lang/crates.io-index" 2027 | checksum = "94e763e24ba2bf0c72bc6be883f967f794a019fafd1b86ba1daff9c91a7edd30" 2028 | dependencies = [ 2029 | "proc-macro2", 2030 | "quote", 2031 | "rust-embed-utils", 2032 | "syn", 2033 | "walkdir", 2034 | ] 2035 | 2036 | [[package]] 2037 | name = "rust-embed-utils" 2038 | version = "7.1.0" 2039 | source = "registry+https://github.com/rust-lang/crates.io-index" 2040 | checksum = "ad22c7226e4829104deab21df575e995bfbc4adfad13a595e387477f238c1aec" 2041 | dependencies = [ 2042 | "sha2", 2043 | "walkdir", 2044 | ] 2045 | 2046 | [[package]] 2047 | name = "rustc_version" 2048 | version = "0.2.3" 2049 | source = "registry+https://github.com/rust-lang/crates.io-index" 2050 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 2051 | dependencies = [ 2052 | "semver 0.9.0", 2053 | ] 2054 | 2055 | [[package]] 2056 | name = "rustc_version" 2057 | version = "0.4.0" 2058 | source = "registry+https://github.com/rust-lang/crates.io-index" 2059 | checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" 2060 | dependencies = [ 2061 | "semver 1.0.4", 2062 | ] 2063 | 2064 | [[package]] 2065 | name = "rustls" 2066 | version = "0.19.1" 2067 | source = "registry+https://github.com/rust-lang/crates.io-index" 2068 | checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" 2069 | dependencies = [ 2070 | "base64", 2071 | "log", 2072 | "ring", 2073 | "sct", 2074 | "webpki", 2075 | ] 2076 | 2077 | [[package]] 2078 | name = "rustls-pemfile" 2079 | version = "0.2.1" 2080 | source = "registry+https://github.com/rust-lang/crates.io-index" 2081 | checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" 2082 | dependencies = [ 2083 | "base64", 2084 | ] 2085 | 2086 | [[package]] 2087 | name = "rustversion" 2088 | version = "1.0.6" 2089 | source = "registry+https://github.com/rust-lang/crates.io-index" 2090 | checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" 2091 | 2092 | [[package]] 2093 | name = "ryu" 2094 | version = "1.0.9" 2095 | source = "registry+https://github.com/rust-lang/crates.io-index" 2096 | checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" 2097 | 2098 | [[package]] 2099 | name = "same-file" 2100 | version = "1.0.6" 2101 | source = "registry+https://github.com/rust-lang/crates.io-index" 2102 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 2103 | dependencies = [ 2104 | "winapi-util", 2105 | ] 2106 | 2107 | [[package]] 2108 | name = "schannel" 2109 | version = "0.1.19" 2110 | source = "registry+https://github.com/rust-lang/crates.io-index" 2111 | checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" 2112 | dependencies = [ 2113 | "lazy_static", 2114 | "winapi", 2115 | ] 2116 | 2117 | [[package]] 2118 | name = "scheduled-thread-pool" 2119 | version = "0.2.5" 2120 | source = "registry+https://github.com/rust-lang/crates.io-index" 2121 | checksum = "dc6f74fd1204073fa02d5d5d68bec8021be4c38690b61264b2fdb48083d0e7d7" 2122 | dependencies = [ 2123 | "parking_lot 0.11.2", 2124 | ] 2125 | 2126 | [[package]] 2127 | name = "scopeguard" 2128 | version = "1.1.0" 2129 | source = "registry+https://github.com/rust-lang/crates.io-index" 2130 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 2131 | 2132 | [[package]] 2133 | name = "sct" 2134 | version = "0.6.1" 2135 | source = "registry+https://github.com/rust-lang/crates.io-index" 2136 | checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" 2137 | dependencies = [ 2138 | "ring", 2139 | "untrusted", 2140 | ] 2141 | 2142 | [[package]] 2143 | name = "security-framework" 2144 | version = "2.4.2" 2145 | source = "registry+https://github.com/rust-lang/crates.io-index" 2146 | checksum = "525bc1abfda2e1998d152c45cf13e696f76d0a4972310b22fac1658b05df7c87" 2147 | dependencies = [ 2148 | "bitflags", 2149 | "core-foundation", 2150 | "core-foundation-sys", 2151 | "libc", 2152 | "security-framework-sys", 2153 | ] 2154 | 2155 | [[package]] 2156 | name = "security-framework-sys" 2157 | version = "2.4.2" 2158 | source = "registry+https://github.com/rust-lang/crates.io-index" 2159 | checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e" 2160 | dependencies = [ 2161 | "core-foundation-sys", 2162 | "libc", 2163 | ] 2164 | 2165 | [[package]] 2166 | name = "semver" 2167 | version = "0.9.0" 2168 | source = "registry+https://github.com/rust-lang/crates.io-index" 2169 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 2170 | dependencies = [ 2171 | "semver-parser", 2172 | ] 2173 | 2174 | [[package]] 2175 | name = "semver" 2176 | version = "1.0.4" 2177 | source = "registry+https://github.com/rust-lang/crates.io-index" 2178 | checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" 2179 | 2180 | [[package]] 2181 | name = "semver-parser" 2182 | version = "0.7.0" 2183 | source = "registry+https://github.com/rust-lang/crates.io-index" 2184 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 2185 | 2186 | [[package]] 2187 | name = "serde" 2188 | version = "1.0.132" 2189 | source = "registry+https://github.com/rust-lang/crates.io-index" 2190 | checksum = "8b9875c23cf305cd1fd7eb77234cbb705f21ea6a72c637a5c6db5fe4b8e7f008" 2191 | dependencies = [ 2192 | "serde_derive", 2193 | ] 2194 | 2195 | [[package]] 2196 | name = "serde_bytes" 2197 | version = "0.11.5" 2198 | source = "registry+https://github.com/rust-lang/crates.io-index" 2199 | checksum = "16ae07dd2f88a366f15bd0632ba725227018c69a1c8550a927324f8eb8368bb9" 2200 | dependencies = [ 2201 | "serde", 2202 | ] 2203 | 2204 | [[package]] 2205 | name = "serde_derive" 2206 | version = "1.0.132" 2207 | source = "registry+https://github.com/rust-lang/crates.io-index" 2208 | checksum = "ecc0db5cb2556c0e558887d9bbdcf6ac4471e83ff66cf696e5419024d1606276" 2209 | dependencies = [ 2210 | "proc-macro2", 2211 | "quote", 2212 | "syn", 2213 | ] 2214 | 2215 | [[package]] 2216 | name = "serde_json" 2217 | version = "1.0.73" 2218 | source = "registry+https://github.com/rust-lang/crates.io-index" 2219 | checksum = "bcbd0344bc6533bc7ec56df11d42fb70f1b912351c0825ccb7211b59d8af7cf5" 2220 | dependencies = [ 2221 | "indexmap", 2222 | "itoa 1.0.1", 2223 | "ryu", 2224 | "serde", 2225 | ] 2226 | 2227 | [[package]] 2228 | name = "serde_urlencoded" 2229 | version = "0.7.0" 2230 | source = "registry+https://github.com/rust-lang/crates.io-index" 2231 | checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" 2232 | dependencies = [ 2233 | "form_urlencoded", 2234 | "itoa 0.4.8", 2235 | "ryu", 2236 | "serde", 2237 | ] 2238 | 2239 | [[package]] 2240 | name = "serde_with" 2241 | version = "1.11.0" 2242 | source = "registry+https://github.com/rust-lang/crates.io-index" 2243 | checksum = "ad6056b4cb69b6e43e3a0f055def223380baecc99da683884f205bf347f7c4b3" 2244 | dependencies = [ 2245 | "rustversion", 2246 | "serde", 2247 | "serde_with_macros", 2248 | ] 2249 | 2250 | [[package]] 2251 | name = "serde_with_macros" 2252 | version = "1.5.1" 2253 | source = "registry+https://github.com/rust-lang/crates.io-index" 2254 | checksum = "12e47be9471c72889ebafb5e14d5ff930d89ae7a67bbdb5f8abb564f845a927e" 2255 | dependencies = [ 2256 | "darling", 2257 | "proc-macro2", 2258 | "quote", 2259 | "syn", 2260 | ] 2261 | 2262 | [[package]] 2263 | name = "sha-1" 2264 | version = "0.9.8" 2265 | source = "registry+https://github.com/rust-lang/crates.io-index" 2266 | checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" 2267 | dependencies = [ 2268 | "block-buffer", 2269 | "cfg-if 1.0.0", 2270 | "cpufeatures", 2271 | "digest", 2272 | "opaque-debug", 2273 | ] 2274 | 2275 | [[package]] 2276 | name = "sha1" 2277 | version = "0.6.0" 2278 | source = "registry+https://github.com/rust-lang/crates.io-index" 2279 | checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" 2280 | 2281 | [[package]] 2282 | name = "sha2" 2283 | version = "0.9.8" 2284 | source = "registry+https://github.com/rust-lang/crates.io-index" 2285 | checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" 2286 | dependencies = [ 2287 | "block-buffer", 2288 | "cfg-if 1.0.0", 2289 | "cpufeatures", 2290 | "digest", 2291 | "opaque-debug", 2292 | ] 2293 | 2294 | [[package]] 2295 | name = "signal-hook-registry" 2296 | version = "1.4.0" 2297 | source = "registry+https://github.com/rust-lang/crates.io-index" 2298 | checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" 2299 | dependencies = [ 2300 | "libc", 2301 | ] 2302 | 2303 | [[package]] 2304 | name = "slab" 2305 | version = "0.4.5" 2306 | source = "registry+https://github.com/rust-lang/crates.io-index" 2307 | checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" 2308 | 2309 | [[package]] 2310 | name = "smallvec" 2311 | version = "1.7.0" 2312 | source = "registry+https://github.com/rust-lang/crates.io-index" 2313 | checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" 2314 | 2315 | [[package]] 2316 | name = "socket2" 2317 | version = "0.3.19" 2318 | source = "registry+https://github.com/rust-lang/crates.io-index" 2319 | checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" 2320 | dependencies = [ 2321 | "cfg-if 1.0.0", 2322 | "libc", 2323 | "winapi", 2324 | ] 2325 | 2326 | [[package]] 2327 | name = "socket2" 2328 | version = "0.4.2" 2329 | source = "registry+https://github.com/rust-lang/crates.io-index" 2330 | checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516" 2331 | dependencies = [ 2332 | "libc", 2333 | "winapi", 2334 | ] 2335 | 2336 | [[package]] 2337 | name = "solar-system-info-bot" 2338 | version = "0.1.0" 2339 | dependencies = [ 2340 | "chrono", 2341 | "dotenv", 2342 | "futures", 2343 | "indoc", 2344 | "lazy_static", 2345 | "log", 2346 | "pretty_env_logger", 2347 | "reqwest", 2348 | "solar-system-info-rpc", 2349 | "teloxide", 2350 | "tokio", 2351 | "tonic", 2352 | ] 2353 | 2354 | [[package]] 2355 | name = "solar-system-info-rpc" 2356 | version = "0.1.0" 2357 | dependencies = [ 2358 | "prost", 2359 | "prost-types", 2360 | "tonic", 2361 | "tonic-build", 2362 | ] 2363 | 2364 | [[package]] 2365 | name = "solar-system-info-server" 2366 | version = "0.1.0" 2367 | dependencies = [ 2368 | "bigdecimal", 2369 | "chrono", 2370 | "diesel", 2371 | "diesel_migrations", 2372 | "dotenv", 2373 | "env_logger 0.9.0", 2374 | "futures", 2375 | "log", 2376 | "prost", 2377 | "prost-types", 2378 | "rust-embed", 2379 | "serde", 2380 | "serde_json", 2381 | "solar-system-info-rpc", 2382 | "tokio", 2383 | "tokio-stream", 2384 | "tonic", 2385 | ] 2386 | 2387 | [[package]] 2388 | name = "spin" 2389 | version = "0.5.2" 2390 | source = "registry+https://github.com/rust-lang/crates.io-index" 2391 | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 2392 | 2393 | [[package]] 2394 | name = "standback" 2395 | version = "0.2.17" 2396 | source = "registry+https://github.com/rust-lang/crates.io-index" 2397 | checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" 2398 | dependencies = [ 2399 | "version_check", 2400 | ] 2401 | 2402 | [[package]] 2403 | name = "stdweb" 2404 | version = "0.4.20" 2405 | source = "registry+https://github.com/rust-lang/crates.io-index" 2406 | checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" 2407 | dependencies = [ 2408 | "discard", 2409 | "rustc_version 0.2.3", 2410 | "stdweb-derive", 2411 | "stdweb-internal-macros", 2412 | "stdweb-internal-runtime", 2413 | "wasm-bindgen", 2414 | ] 2415 | 2416 | [[package]] 2417 | name = "stdweb-derive" 2418 | version = "0.5.3" 2419 | source = "registry+https://github.com/rust-lang/crates.io-index" 2420 | checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" 2421 | dependencies = [ 2422 | "proc-macro2", 2423 | "quote", 2424 | "serde", 2425 | "serde_derive", 2426 | "syn", 2427 | ] 2428 | 2429 | [[package]] 2430 | name = "stdweb-internal-macros" 2431 | version = "0.2.9" 2432 | source = "registry+https://github.com/rust-lang/crates.io-index" 2433 | checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" 2434 | dependencies = [ 2435 | "base-x", 2436 | "proc-macro2", 2437 | "quote", 2438 | "serde", 2439 | "serde_derive", 2440 | "serde_json", 2441 | "sha1", 2442 | "syn", 2443 | ] 2444 | 2445 | [[package]] 2446 | name = "stdweb-internal-runtime" 2447 | version = "0.1.5" 2448 | source = "registry+https://github.com/rust-lang/crates.io-index" 2449 | checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" 2450 | 2451 | [[package]] 2452 | name = "stringprep" 2453 | version = "0.1.2" 2454 | source = "registry+https://github.com/rust-lang/crates.io-index" 2455 | checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" 2456 | dependencies = [ 2457 | "unicode-bidi", 2458 | "unicode-normalization", 2459 | ] 2460 | 2461 | [[package]] 2462 | name = "strsim" 2463 | version = "0.10.0" 2464 | source = "registry+https://github.com/rust-lang/crates.io-index" 2465 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 2466 | 2467 | [[package]] 2468 | name = "subtle" 2469 | version = "2.4.1" 2470 | source = "registry+https://github.com/rust-lang/crates.io-index" 2471 | checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" 2472 | 2473 | [[package]] 2474 | name = "syn" 2475 | version = "1.0.82" 2476 | source = "registry+https://github.com/rust-lang/crates.io-index" 2477 | checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59" 2478 | dependencies = [ 2479 | "proc-macro2", 2480 | "quote", 2481 | "unicode-xid", 2482 | ] 2483 | 2484 | [[package]] 2485 | name = "take_mut" 2486 | version = "0.2.2" 2487 | source = "registry+https://github.com/rust-lang/crates.io-index" 2488 | checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" 2489 | 2490 | [[package]] 2491 | name = "teloxide" 2492 | version = "0.5.3" 2493 | source = "registry+https://github.com/rust-lang/crates.io-index" 2494 | checksum = "9964854e5ec3a5a44a9f50ebb7641327f1084ab4fc37a6c4a23cc011a388dc2e" 2495 | dependencies = [ 2496 | "async-trait", 2497 | "bytes", 2498 | "derive_more", 2499 | "flurry", 2500 | "futures", 2501 | "log", 2502 | "mime", 2503 | "pin-project", 2504 | "serde", 2505 | "serde_json", 2506 | "serde_with_macros", 2507 | "teloxide-core", 2508 | "teloxide-macros", 2509 | "thiserror", 2510 | "tokio", 2511 | "tokio-stream", 2512 | "tokio-util", 2513 | ] 2514 | 2515 | [[package]] 2516 | name = "teloxide-core" 2517 | version = "0.3.4" 2518 | source = "registry+https://github.com/rust-lang/crates.io-index" 2519 | checksum = "114c9057a3a2f74d937ece64029b362f583a69fb4b7405722c6dc03cd5bb4658" 2520 | dependencies = [ 2521 | "bytes", 2522 | "chrono", 2523 | "derive_more", 2524 | "either", 2525 | "futures", 2526 | "log", 2527 | "mime", 2528 | "never", 2529 | "once_cell", 2530 | "pin-project", 2531 | "reqwest", 2532 | "serde", 2533 | "serde_json", 2534 | "serde_with_macros", 2535 | "thiserror", 2536 | "tokio", 2537 | "tokio-util", 2538 | "url", 2539 | "uuid", 2540 | ] 2541 | 2542 | [[package]] 2543 | name = "teloxide-macros" 2544 | version = "0.4.1" 2545 | source = "registry+https://github.com/rust-lang/crates.io-index" 2546 | checksum = "2fb7e97b8bef2231aea6643558147c7f9c112675c4ca49f24d8fac2edff1216d" 2547 | dependencies = [ 2548 | "proc-macro2", 2549 | "quote", 2550 | "syn", 2551 | ] 2552 | 2553 | [[package]] 2554 | name = "tempfile" 2555 | version = "3.2.0" 2556 | source = "registry+https://github.com/rust-lang/crates.io-index" 2557 | checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" 2558 | dependencies = [ 2559 | "cfg-if 1.0.0", 2560 | "libc", 2561 | "rand", 2562 | "redox_syscall 0.2.10", 2563 | "remove_dir_all", 2564 | "winapi", 2565 | ] 2566 | 2567 | [[package]] 2568 | name = "termcolor" 2569 | version = "1.1.2" 2570 | source = "registry+https://github.com/rust-lang/crates.io-index" 2571 | checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" 2572 | dependencies = [ 2573 | "winapi-util", 2574 | ] 2575 | 2576 | [[package]] 2577 | name = "thiserror" 2578 | version = "1.0.30" 2579 | source = "registry+https://github.com/rust-lang/crates.io-index" 2580 | checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" 2581 | dependencies = [ 2582 | "thiserror-impl", 2583 | ] 2584 | 2585 | [[package]] 2586 | name = "thiserror-impl" 2587 | version = "1.0.30" 2588 | source = "registry+https://github.com/rust-lang/crates.io-index" 2589 | checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" 2590 | dependencies = [ 2591 | "proc-macro2", 2592 | "quote", 2593 | "syn", 2594 | ] 2595 | 2596 | [[package]] 2597 | name = "time" 2598 | version = "0.1.44" 2599 | source = "registry+https://github.com/rust-lang/crates.io-index" 2600 | checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" 2601 | dependencies = [ 2602 | "libc", 2603 | "wasi", 2604 | "winapi", 2605 | ] 2606 | 2607 | [[package]] 2608 | name = "time" 2609 | version = "0.2.27" 2610 | source = "registry+https://github.com/rust-lang/crates.io-index" 2611 | checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242" 2612 | dependencies = [ 2613 | "const_fn", 2614 | "libc", 2615 | "standback", 2616 | "stdweb", 2617 | "time-macros", 2618 | "version_check", 2619 | "winapi", 2620 | ] 2621 | 2622 | [[package]] 2623 | name = "time" 2624 | version = "0.3.5" 2625 | source = "registry+https://github.com/rust-lang/crates.io-index" 2626 | checksum = "41effe7cfa8af36f439fac33861b66b049edc6f9a32331e2312660529c1c24ad" 2627 | dependencies = [ 2628 | "itoa 0.4.8", 2629 | "libc", 2630 | ] 2631 | 2632 | [[package]] 2633 | name = "time-macros" 2634 | version = "0.1.1" 2635 | source = "registry+https://github.com/rust-lang/crates.io-index" 2636 | checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" 2637 | dependencies = [ 2638 | "proc-macro-hack", 2639 | "time-macros-impl", 2640 | ] 2641 | 2642 | [[package]] 2643 | name = "time-macros-impl" 2644 | version = "0.1.2" 2645 | source = "registry+https://github.com/rust-lang/crates.io-index" 2646 | checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" 2647 | dependencies = [ 2648 | "proc-macro-hack", 2649 | "proc-macro2", 2650 | "quote", 2651 | "standback", 2652 | "syn", 2653 | ] 2654 | 2655 | [[package]] 2656 | name = "tinyvec" 2657 | version = "1.5.1" 2658 | source = "registry+https://github.com/rust-lang/crates.io-index" 2659 | checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" 2660 | dependencies = [ 2661 | "tinyvec_macros", 2662 | ] 2663 | 2664 | [[package]] 2665 | name = "tinyvec_macros" 2666 | version = "0.1.0" 2667 | source = "registry+https://github.com/rust-lang/crates.io-index" 2668 | checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" 2669 | 2670 | [[package]] 2671 | name = "tokio" 2672 | version = "1.15.0" 2673 | source = "registry+https://github.com/rust-lang/crates.io-index" 2674 | checksum = "fbbf1c778ec206785635ce8ad57fe52b3009ae9e0c9f574a728f3049d3e55838" 2675 | dependencies = [ 2676 | "bytes", 2677 | "libc", 2678 | "memchr", 2679 | "mio 0.7.14", 2680 | "num_cpus", 2681 | "once_cell", 2682 | "parking_lot 0.11.2", 2683 | "pin-project-lite", 2684 | "signal-hook-registry", 2685 | "tokio-macros", 2686 | "winapi", 2687 | ] 2688 | 2689 | [[package]] 2690 | name = "tokio-io-timeout" 2691 | version = "1.1.1" 2692 | source = "registry+https://github.com/rust-lang/crates.io-index" 2693 | checksum = "90c49f106be240de154571dd31fbe48acb10ba6c6dd6f6517ad603abffa42de9" 2694 | dependencies = [ 2695 | "pin-project-lite", 2696 | "tokio", 2697 | ] 2698 | 2699 | [[package]] 2700 | name = "tokio-macros" 2701 | version = "1.7.0" 2702 | source = "registry+https://github.com/rust-lang/crates.io-index" 2703 | checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" 2704 | dependencies = [ 2705 | "proc-macro2", 2706 | "quote", 2707 | "syn", 2708 | ] 2709 | 2710 | [[package]] 2711 | name = "tokio-native-tls" 2712 | version = "0.3.0" 2713 | source = "registry+https://github.com/rust-lang/crates.io-index" 2714 | checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" 2715 | dependencies = [ 2716 | "native-tls", 2717 | "tokio", 2718 | ] 2719 | 2720 | [[package]] 2721 | name = "tokio-rustls" 2722 | version = "0.22.0" 2723 | source = "registry+https://github.com/rust-lang/crates.io-index" 2724 | checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" 2725 | dependencies = [ 2726 | "rustls", 2727 | "tokio", 2728 | "webpki", 2729 | ] 2730 | 2731 | [[package]] 2732 | name = "tokio-stream" 2733 | version = "0.1.8" 2734 | source = "registry+https://github.com/rust-lang/crates.io-index" 2735 | checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" 2736 | dependencies = [ 2737 | "futures-core", 2738 | "pin-project-lite", 2739 | "tokio", 2740 | ] 2741 | 2742 | [[package]] 2743 | name = "tokio-util" 2744 | version = "0.6.9" 2745 | source = "registry+https://github.com/rust-lang/crates.io-index" 2746 | checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" 2747 | dependencies = [ 2748 | "bytes", 2749 | "futures-core", 2750 | "futures-sink", 2751 | "log", 2752 | "pin-project-lite", 2753 | "tokio", 2754 | ] 2755 | 2756 | [[package]] 2757 | name = "tonic" 2758 | version = "0.6.2" 2759 | source = "registry+https://github.com/rust-lang/crates.io-index" 2760 | checksum = "ff08f4649d10a70ffa3522ca559031285d8e421d727ac85c60825761818f5d0a" 2761 | dependencies = [ 2762 | "async-stream", 2763 | "async-trait", 2764 | "base64", 2765 | "bytes", 2766 | "futures-core", 2767 | "futures-util", 2768 | "h2", 2769 | "http", 2770 | "http-body", 2771 | "hyper", 2772 | "hyper-timeout", 2773 | "percent-encoding", 2774 | "pin-project", 2775 | "prost", 2776 | "prost-derive", 2777 | "tokio", 2778 | "tokio-stream", 2779 | "tokio-util", 2780 | "tower", 2781 | "tower-layer", 2782 | "tower-service", 2783 | "tracing", 2784 | "tracing-futures", 2785 | ] 2786 | 2787 | [[package]] 2788 | name = "tonic-build" 2789 | version = "0.6.2" 2790 | source = "registry+https://github.com/rust-lang/crates.io-index" 2791 | checksum = "9403f1bafde247186684b230dc6f38b5cd514584e8bec1dd32514be4745fa757" 2792 | dependencies = [ 2793 | "proc-macro2", 2794 | "prost-build", 2795 | "quote", 2796 | "syn", 2797 | ] 2798 | 2799 | [[package]] 2800 | name = "tower" 2801 | version = "0.4.11" 2802 | source = "registry+https://github.com/rust-lang/crates.io-index" 2803 | checksum = "5651b5f6860a99bd1adb59dbfe1db8beb433e73709d9032b413a77e2fb7c066a" 2804 | dependencies = [ 2805 | "futures-core", 2806 | "futures-util", 2807 | "indexmap", 2808 | "pin-project", 2809 | "pin-project-lite", 2810 | "rand", 2811 | "slab", 2812 | "tokio", 2813 | "tokio-stream", 2814 | "tokio-util", 2815 | "tower-layer", 2816 | "tower-service", 2817 | "tracing", 2818 | ] 2819 | 2820 | [[package]] 2821 | name = "tower-layer" 2822 | version = "0.3.1" 2823 | source = "registry+https://github.com/rust-lang/crates.io-index" 2824 | checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62" 2825 | 2826 | [[package]] 2827 | name = "tower-service" 2828 | version = "0.3.1" 2829 | source = "registry+https://github.com/rust-lang/crates.io-index" 2830 | checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" 2831 | 2832 | [[package]] 2833 | name = "tracing" 2834 | version = "0.1.29" 2835 | source = "registry+https://github.com/rust-lang/crates.io-index" 2836 | checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" 2837 | dependencies = [ 2838 | "cfg-if 1.0.0", 2839 | "log", 2840 | "pin-project-lite", 2841 | "tracing-attributes", 2842 | "tracing-core", 2843 | ] 2844 | 2845 | [[package]] 2846 | name = "tracing-attributes" 2847 | version = "0.1.18" 2848 | source = "registry+https://github.com/rust-lang/crates.io-index" 2849 | checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" 2850 | dependencies = [ 2851 | "proc-macro2", 2852 | "quote", 2853 | "syn", 2854 | ] 2855 | 2856 | [[package]] 2857 | name = "tracing-core" 2858 | version = "0.1.21" 2859 | source = "registry+https://github.com/rust-lang/crates.io-index" 2860 | checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" 2861 | dependencies = [ 2862 | "lazy_static", 2863 | ] 2864 | 2865 | [[package]] 2866 | name = "tracing-futures" 2867 | version = "0.2.5" 2868 | source = "registry+https://github.com/rust-lang/crates.io-index" 2869 | checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" 2870 | dependencies = [ 2871 | "pin-project", 2872 | "tracing", 2873 | ] 2874 | 2875 | [[package]] 2876 | name = "trust-dns-proto" 2877 | version = "0.20.3" 2878 | source = "registry+https://github.com/rust-lang/crates.io-index" 2879 | checksum = "ad0d7f5db438199a6e2609debe3f69f808d074e0a2888ee0bccb45fe234d03f4" 2880 | dependencies = [ 2881 | "async-trait", 2882 | "cfg-if 1.0.0", 2883 | "data-encoding", 2884 | "enum-as-inner", 2885 | "futures-channel", 2886 | "futures-io", 2887 | "futures-util", 2888 | "idna", 2889 | "ipnet", 2890 | "lazy_static", 2891 | "log", 2892 | "rand", 2893 | "smallvec", 2894 | "thiserror", 2895 | "tinyvec", 2896 | "tokio", 2897 | "url", 2898 | ] 2899 | 2900 | [[package]] 2901 | name = "trust-dns-resolver" 2902 | version = "0.20.3" 2903 | source = "registry+https://github.com/rust-lang/crates.io-index" 2904 | checksum = "f6ad17b608a64bd0735e67bde16b0636f8aa8591f831a25d18443ed00a699770" 2905 | dependencies = [ 2906 | "cfg-if 1.0.0", 2907 | "futures-util", 2908 | "ipconfig", 2909 | "lazy_static", 2910 | "log", 2911 | "lru-cache", 2912 | "parking_lot 0.11.2", 2913 | "resolv-conf", 2914 | "smallvec", 2915 | "thiserror", 2916 | "tokio", 2917 | "trust-dns-proto", 2918 | ] 2919 | 2920 | [[package]] 2921 | name = "try-lock" 2922 | version = "0.2.3" 2923 | source = "registry+https://github.com/rust-lang/crates.io-index" 2924 | checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" 2925 | 2926 | [[package]] 2927 | name = "typed-builder" 2928 | version = "0.9.1" 2929 | source = "registry+https://github.com/rust-lang/crates.io-index" 2930 | checksum = "a46ee5bd706ff79131be9c94e7edcb82b703c487766a114434e5790361cf08c5" 2931 | dependencies = [ 2932 | "proc-macro2", 2933 | "quote", 2934 | "syn", 2935 | ] 2936 | 2937 | [[package]] 2938 | name = "typenum" 2939 | version = "1.14.0" 2940 | source = "registry+https://github.com/rust-lang/crates.io-index" 2941 | checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" 2942 | 2943 | [[package]] 2944 | name = "unicase" 2945 | version = "2.6.0" 2946 | source = "registry+https://github.com/rust-lang/crates.io-index" 2947 | checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" 2948 | dependencies = [ 2949 | "version_check", 2950 | ] 2951 | 2952 | [[package]] 2953 | name = "unicode-bidi" 2954 | version = "0.3.7" 2955 | source = "registry+https://github.com/rust-lang/crates.io-index" 2956 | checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" 2957 | 2958 | [[package]] 2959 | name = "unicode-normalization" 2960 | version = "0.1.19" 2961 | source = "registry+https://github.com/rust-lang/crates.io-index" 2962 | checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" 2963 | dependencies = [ 2964 | "tinyvec", 2965 | ] 2966 | 2967 | [[package]] 2968 | name = "unicode-segmentation" 2969 | version = "1.8.0" 2970 | source = "registry+https://github.com/rust-lang/crates.io-index" 2971 | checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" 2972 | 2973 | [[package]] 2974 | name = "unicode-xid" 2975 | version = "0.2.2" 2976 | source = "registry+https://github.com/rust-lang/crates.io-index" 2977 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 2978 | 2979 | [[package]] 2980 | name = "unindent" 2981 | version = "0.1.7" 2982 | source = "registry+https://github.com/rust-lang/crates.io-index" 2983 | checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7" 2984 | 2985 | [[package]] 2986 | name = "untrusted" 2987 | version = "0.7.1" 2988 | source = "registry+https://github.com/rust-lang/crates.io-index" 2989 | checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" 2990 | 2991 | [[package]] 2992 | name = "url" 2993 | version = "2.2.2" 2994 | source = "registry+https://github.com/rust-lang/crates.io-index" 2995 | checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" 2996 | dependencies = [ 2997 | "form_urlencoded", 2998 | "idna", 2999 | "matches", 3000 | "percent-encoding", 3001 | "serde", 3002 | ] 3003 | 3004 | [[package]] 3005 | name = "uuid" 3006 | version = "0.8.2" 3007 | source = "registry+https://github.com/rust-lang/crates.io-index" 3008 | checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" 3009 | dependencies = [ 3010 | "getrandom", 3011 | "serde", 3012 | ] 3013 | 3014 | [[package]] 3015 | name = "vcpkg" 3016 | version = "0.2.15" 3017 | source = "registry+https://github.com/rust-lang/crates.io-index" 3018 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 3019 | 3020 | [[package]] 3021 | name = "version_check" 3022 | version = "0.9.3" 3023 | source = "registry+https://github.com/rust-lang/crates.io-index" 3024 | checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" 3025 | 3026 | [[package]] 3027 | name = "walkdir" 3028 | version = "2.3.2" 3029 | source = "registry+https://github.com/rust-lang/crates.io-index" 3030 | checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" 3031 | dependencies = [ 3032 | "same-file", 3033 | "winapi", 3034 | "winapi-util", 3035 | ] 3036 | 3037 | [[package]] 3038 | name = "want" 3039 | version = "0.3.0" 3040 | source = "registry+https://github.com/rust-lang/crates.io-index" 3041 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 3042 | dependencies = [ 3043 | "log", 3044 | "try-lock", 3045 | ] 3046 | 3047 | [[package]] 3048 | name = "wasi" 3049 | version = "0.10.0+wasi-snapshot-preview1" 3050 | source = "registry+https://github.com/rust-lang/crates.io-index" 3051 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" 3052 | 3053 | [[package]] 3054 | name = "wasm-bindgen" 3055 | version = "0.2.78" 3056 | source = "registry+https://github.com/rust-lang/crates.io-index" 3057 | checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" 3058 | dependencies = [ 3059 | "cfg-if 1.0.0", 3060 | "wasm-bindgen-macro", 3061 | ] 3062 | 3063 | [[package]] 3064 | name = "wasm-bindgen-backend" 3065 | version = "0.2.78" 3066 | source = "registry+https://github.com/rust-lang/crates.io-index" 3067 | checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" 3068 | dependencies = [ 3069 | "bumpalo", 3070 | "lazy_static", 3071 | "log", 3072 | "proc-macro2", 3073 | "quote", 3074 | "syn", 3075 | "wasm-bindgen-shared", 3076 | ] 3077 | 3078 | [[package]] 3079 | name = "wasm-bindgen-futures" 3080 | version = "0.4.28" 3081 | source = "registry+https://github.com/rust-lang/crates.io-index" 3082 | checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39" 3083 | dependencies = [ 3084 | "cfg-if 1.0.0", 3085 | "js-sys", 3086 | "wasm-bindgen", 3087 | "web-sys", 3088 | ] 3089 | 3090 | [[package]] 3091 | name = "wasm-bindgen-macro" 3092 | version = "0.2.78" 3093 | source = "registry+https://github.com/rust-lang/crates.io-index" 3094 | checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" 3095 | dependencies = [ 3096 | "quote", 3097 | "wasm-bindgen-macro-support", 3098 | ] 3099 | 3100 | [[package]] 3101 | name = "wasm-bindgen-macro-support" 3102 | version = "0.2.78" 3103 | source = "registry+https://github.com/rust-lang/crates.io-index" 3104 | checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" 3105 | dependencies = [ 3106 | "proc-macro2", 3107 | "quote", 3108 | "syn", 3109 | "wasm-bindgen-backend", 3110 | "wasm-bindgen-shared", 3111 | ] 3112 | 3113 | [[package]] 3114 | name = "wasm-bindgen-shared" 3115 | version = "0.2.78" 3116 | source = "registry+https://github.com/rust-lang/crates.io-index" 3117 | checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" 3118 | 3119 | [[package]] 3120 | name = "web-sys" 3121 | version = "0.3.55" 3122 | source = "registry+https://github.com/rust-lang/crates.io-index" 3123 | checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" 3124 | dependencies = [ 3125 | "js-sys", 3126 | "wasm-bindgen", 3127 | ] 3128 | 3129 | [[package]] 3130 | name = "webpki" 3131 | version = "0.21.4" 3132 | source = "registry+https://github.com/rust-lang/crates.io-index" 3133 | checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" 3134 | dependencies = [ 3135 | "ring", 3136 | "untrusted", 3137 | ] 3138 | 3139 | [[package]] 3140 | name = "webpki-roots" 3141 | version = "0.21.1" 3142 | source = "registry+https://github.com/rust-lang/crates.io-index" 3143 | checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" 3144 | dependencies = [ 3145 | "webpki", 3146 | ] 3147 | 3148 | [[package]] 3149 | name = "which" 3150 | version = "4.2.2" 3151 | source = "registry+https://github.com/rust-lang/crates.io-index" 3152 | checksum = "ea187a8ef279bc014ec368c27a920da2024d2a711109bfbe3440585d5cf27ad9" 3153 | dependencies = [ 3154 | "either", 3155 | "lazy_static", 3156 | "libc", 3157 | ] 3158 | 3159 | [[package]] 3160 | name = "widestring" 3161 | version = "0.4.3" 3162 | source = "registry+https://github.com/rust-lang/crates.io-index" 3163 | checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c" 3164 | 3165 | [[package]] 3166 | name = "winapi" 3167 | version = "0.3.9" 3168 | source = "registry+https://github.com/rust-lang/crates.io-index" 3169 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 3170 | dependencies = [ 3171 | "winapi-i686-pc-windows-gnu", 3172 | "winapi-x86_64-pc-windows-gnu", 3173 | ] 3174 | 3175 | [[package]] 3176 | name = "winapi-i686-pc-windows-gnu" 3177 | version = "0.4.0" 3178 | source = "registry+https://github.com/rust-lang/crates.io-index" 3179 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 3180 | 3181 | [[package]] 3182 | name = "winapi-util" 3183 | version = "0.1.5" 3184 | source = "registry+https://github.com/rust-lang/crates.io-index" 3185 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 3186 | dependencies = [ 3187 | "winapi", 3188 | ] 3189 | 3190 | [[package]] 3191 | name = "winapi-x86_64-pc-windows-gnu" 3192 | version = "0.4.0" 3193 | source = "registry+https://github.com/rust-lang/crates.io-index" 3194 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 3195 | 3196 | [[package]] 3197 | name = "winreg" 3198 | version = "0.6.2" 3199 | source = "registry+https://github.com/rust-lang/crates.io-index" 3200 | checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" 3201 | dependencies = [ 3202 | "winapi", 3203 | ] 3204 | 3205 | [[package]] 3206 | name = "winreg" 3207 | version = "0.7.0" 3208 | source = "registry+https://github.com/rust-lang/crates.io-index" 3209 | checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" 3210 | dependencies = [ 3211 | "winapi", 3212 | ] 3213 | 3214 | [[package]] 3215 | name = "zstd" 3216 | version = "0.9.0+zstd.1.5.0" 3217 | source = "registry+https://github.com/rust-lang/crates.io-index" 3218 | checksum = "07749a5dc2cb6b36661290245e350f15ec3bbb304e493db54a1d354480522ccd" 3219 | dependencies = [ 3220 | "zstd-safe", 3221 | ] 3222 | 3223 | [[package]] 3224 | name = "zstd-safe" 3225 | version = "4.1.1+zstd.1.5.0" 3226 | source = "registry+https://github.com/rust-lang/crates.io-index" 3227 | checksum = "c91c90f2c593b003603e5e0493c837088df4469da25aafff8bce42ba48caf079" 3228 | dependencies = [ 3229 | "libc", 3230 | "zstd-sys", 3231 | ] 3232 | 3233 | [[package]] 3234 | name = "zstd-sys" 3235 | version = "1.6.1+zstd.1.5.0" 3236 | source = "registry+https://github.com/rust-lang/crates.io-index" 3237 | checksum = "615120c7a2431d16cf1cf979e7fc31ba7a5b5e5707b29c8a99e5dbf8a8392a33" 3238 | dependencies = [ 3239 | "cc", 3240 | "libc", 3241 | ] 3242 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "grpc-telegram-bot/rpc", 4 | "grpc-telegram-bot/server", 5 | "grpc-telegram-bot/bot", 6 | 7 | "mongodb-redis" 8 | ] 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 Roman Kudryashov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /grpc-telegram-bot/.env: -------------------------------------------------------------------------------- 1 | TELEGRAM_API_URL=http://nginx 2 | GRPC_SERVER_PORT=9111 3 | DB_PASSWORD=password 4 | TELOXIDE_TOKEN= 5 | -------------------------------------------------------------------------------- /grpc-telegram-bot/bot/.env: -------------------------------------------------------------------------------- 1 | TELEGRAM_API_URL=https://api.telegram.org 2 | GRPC_SERVER_ADDRESS=http://[::1]:9111 3 | TELOXIDE_TOKEN= 4 | RUST_LOG=debug 5 | -------------------------------------------------------------------------------- /grpc-telegram-bot/bot/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "solar-system-info-bot" 3 | version = "0.1.0" 4 | authors = ["Roman Kudryashov "] 5 | edition = "2021" 6 | 7 | [dependencies] 8 | solar-system-info-rpc = { path = "../rpc" } 9 | teloxide = { version = "0.5.3", features = ["macros", "auto-send"] } 10 | tonic = "0.6.2" 11 | tokio = { version = "1.15.0", features = ["rt-multi-thread", "macros"] } 12 | futures = "0.3.19" 13 | indoc = "1.0.3" 14 | chrono = { version = "0.4.19", features = ["serde"] } 15 | lazy_static = "1.4.0" 16 | log = "0.4.14" 17 | pretty_env_logger = "0.4.0" 18 | dotenv = "0.15.0" 19 | reqwest = "0.11.7" 20 | -------------------------------------------------------------------------------- /grpc-telegram-bot/bot/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:1.57 2 | 3 | ENV CARGO_TERM_COLOR always 4 | RUN rustup component add rustfmt 5 | 6 | WORKDIR /usr/src/app/docker-build 7 | COPY /Cargo.lock ./ 8 | COPY /grpc-telegram-bot/bot/ ./ 9 | COPY /grpc-telegram-bot/rpc ../rpc 10 | RUN cargo install --path . --locked 11 | 12 | FROM debian:buster-slim 13 | RUN apt-get update && apt-get install -y openssl 14 | COPY --from=0 /usr/local/cargo/bin/solar-system-info-bot /usr/local/bin/solar-system-info-bot 15 | CMD ["solar-system-info-bot"] 16 | -------------------------------------------------------------------------------- /grpc-telegram-bot/bot/src/conversion.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::fmt::{Formatter, LowerExp}; 3 | 4 | use chrono::NaiveDateTime; 5 | use indoc::writedoc; 6 | 7 | use solar_system_info_rpc::solar_system_info::{Planet, Satellite, Type}; 8 | 9 | pub struct PlanetWrapper<'a>(pub &'a Planet); 10 | 11 | impl fmt::Display for PlanetWrapper<'_> { 12 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 13 | let planet = &self.0; 14 | let planet_type = convert_planet_type_to_string( 15 | Type::from_i32(planet.r#type).expect("Planet type not found"), 16 | ); 17 | let mass = format!("{:e}", PlanetMassWrapper(planet.mass)); 18 | 19 | writedoc!(f, " 20 | Information on {name} 21 | 22 | type: {type} 23 | mean radius: {mean_radius} km 24 | mass: {mass} kg 25 | ", 26 | name = planet.name, 27 | type = planet_type, 28 | mean_radius = planet.mean_radius, 29 | mass = mass, 30 | )?; 31 | 32 | if !planet.satellites.is_empty() { 33 | writedoc!(f, "satellites:\n")?; 34 | for satellite in &planet.satellites { 35 | writedoc!( 36 | f, 37 | " {satellite}", 38 | satellite = SatelliteWrapper(satellite) 39 | )?; 40 | writedoc!(f, "\n")?; 41 | } 42 | } 43 | Ok(()) 44 | } 45 | } 46 | 47 | struct PlanetMassWrapper(f32); 48 | 49 | impl LowerExp for PlanetMassWrapper { 50 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 51 | LowerExp::fmt(&self.0, f) 52 | } 53 | } 54 | 55 | struct SatelliteWrapper<'a>(&'a Satellite); 56 | 57 | impl fmt::Display for SatelliteWrapper<'_> { 58 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 59 | let satellite = &self.0; 60 | 61 | writedoc!(f, "{name}", name = satellite.name)?; 62 | 63 | if let Some(timestamp) = &satellite.first_spacecraft_landing_date { 64 | let date = NaiveDateTime::from_timestamp(timestamp.seconds, 0).date(); 65 | writedoc!(f, " (first spacecraft landing date: {date})", date = date)?; 66 | } 67 | 68 | Ok(()) 69 | } 70 | } 71 | 72 | fn convert_planet_type_to_string<'a>(planet_type: Type) -> &'a str { 73 | match planet_type { 74 | Type::TerrestrialPlanet => "Terrestrial planet", 75 | Type::GasGiant => "Gas giant", 76 | Type::IceGiant => "Ice giant", 77 | Type::DwarfPlanet => "Dwarf planet", 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /grpc-telegram-bot/bot/src/error.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | use std::fmt; 3 | 4 | #[derive(Debug)] 5 | pub struct SolarSystemInfoBotError { 6 | message: String, 7 | } 8 | 9 | impl SolarSystemInfoBotError { 10 | pub fn new(msg: &str) -> Self { 11 | SolarSystemInfoBotError { 12 | message: msg.to_string(), 13 | } 14 | } 15 | } 16 | 17 | impl fmt::Display for SolarSystemInfoBotError { 18 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 19 | write!(f, "{}", self.message) 20 | } 21 | } 22 | 23 | impl Error for SolarSystemInfoBotError {} 24 | -------------------------------------------------------------------------------- /grpc-telegram-bot/bot/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::env; 3 | use std::error::Error; 4 | use std::sync::Arc; 5 | 6 | use dotenv::dotenv; 7 | use indoc::formatdoc; 8 | use lazy_static::lazy_static; 9 | use reqwest::Url; 10 | use teloxide::adaptors::DefaultParseMode; 11 | use teloxide::types::InputFile; 12 | use teloxide::types::ParseMode::Html; 13 | use teloxide::{prelude::*, utils::command::BotCommand}; 14 | 15 | use solar_system_info_rpc::solar_system_info::solar_system_info_client::SolarSystemInfoClient; 16 | use solar_system_info_rpc::solar_system_info::PlanetRequest; 17 | 18 | use crate::conversion::PlanetWrapper; 19 | use crate::error::SolarSystemInfoBotError; 20 | 21 | mod conversion; 22 | mod error; 23 | 24 | lazy_static! { 25 | static ref GRPC_SERVER_ADDRESS: String = 26 | std::env::var("GRPC_SERVER_ADDRESS").expect("Can't read gRPC server address"); 27 | } 28 | 29 | #[tokio::main] 30 | async fn main() { 31 | dotenv().ok(); 32 | teloxide::enable_logging!(); 33 | log::info!("Starting Solar System info bot"); 34 | 35 | let api_url = std::env::var("TELEGRAM_API_URL").expect("Can't get Telegram API URL"); 36 | let api_url = Url::parse(&api_url).expect("Can't parse Telegram API URL"); 37 | 38 | let bot = Bot::from_env() 39 | .set_api_url(api_url) 40 | .parse_mode(Html) 41 | .auto_send(); 42 | 43 | // TODO: remove this line after the fix will be issued 44 | let bot = Arc::new(bot); 45 | 46 | let grpc_client = create_grpc_client().await; 47 | 48 | teloxide::commands_repl(bot, "solar-system-info-bot", move |cx, command| { 49 | answer(cx, command, grpc_client.clone()) 50 | }) 51 | .await; 52 | } 53 | 54 | type UpdateCtx = UpdateWithCx>>, Message>; 55 | 56 | async fn answer( 57 | ctx: UpdateCtx, 58 | command: Command, 59 | grpc_client: SolarSystemInfoClient, 60 | ) -> Result<(), Box> { 61 | match command { 62 | Command::Start => { 63 | let username: String = match ctx.update.from() { 64 | Some(user) => user.username.clone().unwrap_or_default(), 65 | None => String::new(), 66 | }; 67 | 68 | ctx.answer(get_start_message(&username)).await?; 69 | ctx.answer(Command::descriptions()).await?; 70 | } 71 | Command::Help => { 72 | ctx.answer(Command::descriptions()).await?; 73 | } 74 | Command::List => { 75 | let response = get_planets_list(grpc_client).await?; 76 | ctx.answer(response).await?; 77 | } 78 | Command::Planets => { 79 | get_planets(grpc_client, ctx).await?; 80 | } 81 | Command::Planet(planet_name) => { 82 | let response = get_planet(grpc_client, String::from(planet_name.trim())).await?; 83 | ctx.answer_photo(response.0).caption(response.1).await?; 84 | } 85 | }; 86 | 87 | Ok(()) 88 | } 89 | 90 | fn get_start_message(username: &str) -> String { 91 | formatdoc!( 92 | "Hi {username}, this bot can show basic information about planets in the Solar System", 93 | username = &username 94 | ) 95 | } 96 | 97 | async fn get_planets_list( 98 | mut grpc_client: SolarSystemInfoClient, 99 | ) -> Result { 100 | let response = grpc_client.get_planets_list(tonic::Request::new(())).await; 101 | 102 | return match response { 103 | Ok(response) => { 104 | let message = response 105 | .into_inner() 106 | .list 107 | .into_iter() 108 | .map(|planet_name| { 109 | format!( 110 | "{} (use /planet {})", 111 | planet_name, 112 | planet_name.to_lowercase() 113 | ) 114 | }) 115 | .collect::>() 116 | .join("\n"); 117 | Ok(message) 118 | } 119 | Err(e) => { 120 | log::error!("There was an error while handling list of planets: {}", e); 121 | Err(SolarSystemInfoBotError::new( 122 | "Internal error while handling list of planets", 123 | )) 124 | } 125 | }; 126 | } 127 | 128 | async fn get_planets( 129 | mut grpc_client: SolarSystemInfoClient, 130 | ctx: UpdateCtx, 131 | ) -> Result<(), SolarSystemInfoBotError> { 132 | let response = grpc_client.get_planets(tonic::Request::new(())).await; 133 | 134 | match response { 135 | Ok(response) => { 136 | let mut planets_stream = response.into_inner(); 137 | 138 | while let Some(response) = planets_stream.message().await.expect("Can't get a planet") { 139 | match response.planet { 140 | Some(planet) => { 141 | let message = PlanetWrapper(&planet).to_string(); 142 | match ctx.answer(message).await { 143 | Ok(_) => {} 144 | Err(e) => { 145 | log::error!("Something went wrong while handling planets: {}", e); 146 | } 147 | }; 148 | } 149 | None => { 150 | log::error!("Something went wrong while handling planets"); 151 | return Err(SolarSystemInfoBotError::new( 152 | "Something went wrong while handling planets", 153 | )); 154 | } 155 | } 156 | } 157 | } 158 | Err(e) => { 159 | log::error!("There was an error while handling planets: {}", e); 160 | return Err(SolarSystemInfoBotError::new( 161 | "Internal error while handling planets", 162 | )); 163 | } 164 | }; 165 | 166 | Ok(()) 167 | } 168 | 169 | async fn get_planet( 170 | mut grpc_client: SolarSystemInfoClient, 171 | planet_name: String, 172 | ) -> Result<(InputFile, String), SolarSystemInfoBotError> { 173 | let request = tonic::Request::new(PlanetRequest { 174 | name: planet_name.to_string(), 175 | }); 176 | let response = grpc_client.get_planet(request).await; 177 | 178 | return match response { 179 | Ok(response) => match response.into_inner().planet { 180 | Some(planet) => { 181 | let pw = PlanetWrapper(&planet); 182 | let file = InputFile::Memory { 183 | file_name: format!("{}.png", &planet_name), 184 | data: Cow::from(planet.image.clone()), 185 | }; 186 | 187 | Ok((file, pw.to_string())) 188 | } 189 | None => { 190 | log::error!( 191 | "Something went wrong while handling a planet {}", 192 | &planet_name 193 | ); 194 | Err(SolarSystemInfoBotError::new( 195 | format!( 196 | "Something went wrong while handling a planet {}", 197 | planet_name 198 | ) 199 | .as_str(), 200 | )) 201 | } 202 | }, 203 | Err(status) => { 204 | log::error!("There was an error while handling a planet: {}", status); 205 | match status.code() { 206 | tonic::Code::NotFound => Err(SolarSystemInfoBotError::new( 207 | format!("Planet with name {} not found", planet_name).as_str(), 208 | )), 209 | _ => Err(SolarSystemInfoBotError::new( 210 | format!("Internal error while handling a planet {}", planet_name).as_str(), 211 | )), 212 | } 213 | } 214 | }; 215 | } 216 | 217 | #[derive(BotCommand)] 218 | #[command(rename = "lowercase", description = "These commands are supported:")] 219 | enum Command { 220 | Start, 221 | #[command(description = "show this message")] 222 | Help, 223 | #[command(description = "show list of planets")] 224 | List, 225 | #[command(description = "show info about all planets")] 226 | Planets, 227 | #[command( 228 | description = "show info about specified planet (for example, enter /planet neptune)" 229 | )] 230 | Planet(String), 231 | } 232 | 233 | async fn create_grpc_client() -> SolarSystemInfoClient { 234 | let channel = tonic::transport::Channel::from_static(&GRPC_SERVER_ADDRESS) 235 | .connect() 236 | .await 237 | .expect("Can't create a channel"); 238 | 239 | SolarSystemInfoClient::new(channel) 240 | } 241 | -------------------------------------------------------------------------------- /grpc-telegram-bot/docker-compose.override.yml: -------------------------------------------------------------------------------- 1 | # development configuration 2 | version: '3.8' 3 | services: 4 | 5 | solar-system-info-bot: 6 | build: 7 | context: .. 8 | dockerfile: ./grpc-telegram-bot/bot/Dockerfile 9 | 10 | solar-system-info-server: 11 | build: 12 | context: .. 13 | dockerfile: ./grpc-telegram-bot/server/Dockerfile 14 | 15 | nginx: 16 | build: 17 | context: ./nginx 18 | dockerfile: Dockerfile 19 | -------------------------------------------------------------------------------- /grpc-telegram-bot/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | services: 3 | 4 | solar-system-info-server: 5 | image: kudryashovroman/solar-system-info:server 6 | container_name: solar-system-info-server 7 | restart: always 8 | depends_on: 9 | - solar-system-info-db 10 | environment: 11 | GRPC_SERVER_ADDRESS: 0.0.0.0:$GRPC_SERVER_PORT 12 | DATABASE_URL: postgres://postgres:$DB_PASSWORD@solar-system-info-db/solar_system_info 13 | RUST_LOG: debug 14 | ports: 15 | - $GRPC_SERVER_PORT:$GRPC_SERVER_PORT 16 | 17 | solar-system-info-db: 18 | image: postgres:13.2-alpine 19 | container_name: solar-system-info-db 20 | restart: always 21 | environment: 22 | POSTGRES_DB: solar_system_info 23 | POSTGRES_PASSWORD: $DB_PASSWORD 24 | healthcheck: 25 | test: [ "CMD-SHELL", "pg_isready -U postgres" ] 26 | interval: 1m 27 | timeout: 5s 28 | start_period: 10s 29 | retries: 3 30 | 31 | solar-system-info-bot: 32 | image: kudryashovroman/solar-system-info:bot 33 | container_name: solar-system-info-bot 34 | restart: always 35 | depends_on: 36 | - solar-system-info-server 37 | - nginx 38 | environment: 39 | TELEGRAM_API_URL: $TELEGRAM_API_URL 40 | GRPC_SERVER_ADDRESS: http://solar-system-info-server:$GRPC_SERVER_PORT 41 | TELOXIDE_TOKEN: $TELOXIDE_TOKEN 42 | RUST_LOG: debug 43 | 44 | nginx: 45 | image: kudryashovroman/solar-system-info:nginx 46 | container_name: nginx 47 | restart: always 48 | -------------------------------------------------------------------------------- /grpc-telegram-bot/nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:latest 2 | COPY ./nginx.conf /etc/nginx/conf.d/default.conf 3 | -------------------------------------------------------------------------------- /grpc-telegram-bot/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | location / { 3 | resolver 8.8.8.8 valid=30s ipv6=off; 4 | proxy_pass https://api.telegram.org; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /grpc-telegram-bot/readme.adoc: -------------------------------------------------------------------------------- 1 | = Solar System Info 2 | 3 | The project demonstrates: 4 | 5 | * implementation of gRPC server and client using https://github.com/hyperium/tonic[tonic] 6 | * implementation of a Telegram bot using https://github.com/teloxide/teloxide[teloxide] 7 | 8 | image:https://romankudryashov.com/blog/2021/04/grpc-rust/images/architecture.png[Architecture,800] 9 | 10 | https://romankudryashov.com/blog/2021/04/grpc-rust/[Detailed description] 11 | 12 | https://t.me/solarsysteminfobot[Live demo] 13 | -------------------------------------------------------------------------------- /grpc-telegram-bot/rpc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "solar-system-info-rpc" 3 | version = "0.1.0" 4 | authors = ["Roman Kudryashov "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | tonic = "0.6.2" 9 | prost = "0.9.0" 10 | prost-types = "0.9.0" 11 | 12 | [build-dependencies] 13 | tonic-build = "0.6.2" 14 | -------------------------------------------------------------------------------- /grpc-telegram-bot/rpc/build.rs: -------------------------------------------------------------------------------- 1 | fn main() -> Result<(), Box> { 2 | tonic_build::compile_protos("proto/solar-system-info/solar-system-info.proto")?; 3 | Ok(()) 4 | } 5 | -------------------------------------------------------------------------------- /grpc-telegram-bot/rpc/proto/solar-system-info/solar-system-info.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package solar_system_info; 4 | 5 | import "google/protobuf/timestamp.proto"; 6 | import "google/protobuf/empty.proto"; 7 | 8 | service SolarSystemInfo { 9 | rpc GetPlanetsList (google.protobuf.Empty) returns (PlanetsListResponse); 10 | rpc GetPlanet (PlanetRequest) returns (PlanetResponse); 11 | rpc GetPlanets (google.protobuf.Empty) returns (stream PlanetResponse); 12 | } 13 | 14 | message PlanetsListResponse { 15 | repeated string list = 1; 16 | } 17 | 18 | message PlanetRequest { 19 | string name = 1; 20 | } 21 | 22 | message PlanetResponse { 23 | Planet planet = 1; 24 | } 25 | 26 | message Planet { 27 | uint64 id = 1; 28 | string name = 2; 29 | Type type = 3; 30 | float meanRadius = 4; 31 | float mass = 5; 32 | repeated Satellite satellites = 6; 33 | bytes image = 7; 34 | } 35 | 36 | enum Type { 37 | TERRESTRIAL_PLANET = 0; 38 | GAS_GIANT = 1; 39 | ICE_GIANT = 2; 40 | DWARF_PLANET = 3; 41 | } 42 | 43 | message Satellite { 44 | uint64 id = 1; 45 | string name = 2; 46 | google.protobuf.Timestamp first_spacecraft_landing_date = 3; 47 | } 48 | -------------------------------------------------------------------------------- /grpc-telegram-bot/rpc/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod solar_system_info { 2 | tonic::include_proto!("solar_system_info"); 3 | } 4 | -------------------------------------------------------------------------------- /grpc-telegram-bot/server/.env: -------------------------------------------------------------------------------- 1 | RUST_LOG=debug 2 | GRPC_SERVER_ADDRESS=[::1]:9111 3 | DATABASE_URL=postgres://postgres:password@localhost/solar_system_info 4 | -------------------------------------------------------------------------------- /grpc-telegram-bot/server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "solar-system-info-server" 3 | version = "0.1.0" 4 | authors = ["Roman Kudryashov "] 5 | edition = "2021" 6 | 7 | [dependencies] 8 | solar-system-info-rpc = { path = "../rpc" } 9 | tonic = "0.6.2" 10 | prost = "0.9.0" 11 | prost-types = "0.9.0" 12 | tokio = { version = "1.15.0", features = ["rt-multi-thread"] } 13 | tokio-stream = "0.1.8" 14 | futures = "0.3.19" 15 | diesel = { version = "1.4.8", features = ["postgres", "r2d2", "numeric", "chrono"] } 16 | diesel_migrations = "1.4.0" 17 | chrono = { version = "0.4.19", features = ["serde"] } 18 | bigdecimal = "0.1.2" 19 | serde = { version = "1.0.132", features = ["derive"] } 20 | serde_json = "1.0.73" 21 | log = "0.4.14" 22 | env_logger = "0.9.0" 23 | dotenv = "0.15.0" 24 | rust-embed = "6.3.0" 25 | -------------------------------------------------------------------------------- /grpc-telegram-bot/server/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:1.57 2 | 3 | ENV CARGO_TERM_COLOR always 4 | RUN apt-get update && apt-get install -y libpq-dev 5 | RUN rustup component add rustfmt 6 | 7 | WORKDIR /usr/src/app/docker-build 8 | COPY /Cargo.lock ./ 9 | COPY /grpc-telegram-bot/server/ ./ 10 | COPY /grpc-telegram-bot/rpc ../rpc 11 | RUN cargo install --path . --locked 12 | 13 | FROM debian:buster-slim 14 | RUN apt-get update && apt-get install -y libpq-dev 15 | COPY --from=0 /usr/local/cargo/bin/solar-system-info-server /usr/local/bin/solar-system-info-server 16 | CMD ["solar-system-info-server"] 17 | -------------------------------------------------------------------------------- /grpc-telegram-bot/server/diesel.toml: -------------------------------------------------------------------------------- 1 | # For documentation on how to configure this file, 2 | # see diesel.rs/guides/configuring-diesel-cli 3 | 4 | [print_schema] 5 | file = "src/persistence/schema.rs" 6 | -------------------------------------------------------------------------------- /grpc-telegram-bot/server/images/earth.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkudryashov/exploring-rust-ecosystem/56f17d089d7383313981dbd8208527158bd3bb65/grpc-telegram-bot/server/images/earth.jpg -------------------------------------------------------------------------------- /grpc-telegram-bot/server/images/jupiter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkudryashov/exploring-rust-ecosystem/56f17d089d7383313981dbd8208527158bd3bb65/grpc-telegram-bot/server/images/jupiter.jpg -------------------------------------------------------------------------------- /grpc-telegram-bot/server/images/mars.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkudryashov/exploring-rust-ecosystem/56f17d089d7383313981dbd8208527158bd3bb65/grpc-telegram-bot/server/images/mars.jpg -------------------------------------------------------------------------------- /grpc-telegram-bot/server/images/mercury.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkudryashov/exploring-rust-ecosystem/56f17d089d7383313981dbd8208527158bd3bb65/grpc-telegram-bot/server/images/mercury.jpg -------------------------------------------------------------------------------- /grpc-telegram-bot/server/images/neptune.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkudryashov/exploring-rust-ecosystem/56f17d089d7383313981dbd8208527158bd3bb65/grpc-telegram-bot/server/images/neptune.jpg -------------------------------------------------------------------------------- /grpc-telegram-bot/server/images/saturn.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkudryashov/exploring-rust-ecosystem/56f17d089d7383313981dbd8208527158bd3bb65/grpc-telegram-bot/server/images/saturn.jpg -------------------------------------------------------------------------------- /grpc-telegram-bot/server/images/uranus.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkudryashov/exploring-rust-ecosystem/56f17d089d7383313981dbd8208527158bd3bb65/grpc-telegram-bot/server/images/uranus.jpg -------------------------------------------------------------------------------- /grpc-telegram-bot/server/images/venus.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkudryashov/exploring-rust-ecosystem/56f17d089d7383313981dbd8208527158bd3bb65/grpc-telegram-bot/server/images/venus.jpg -------------------------------------------------------------------------------- /grpc-telegram-bot/server/migrations/00000000000000_diesel_initial_setup/down.sql: -------------------------------------------------------------------------------- 1 | -- This file was automatically created by Diesel to setup helper functions 2 | -- and other internal bookkeeping. This file is safe to edit, any future 3 | -- changes will be added to existing projects as new migrations. 4 | 5 | DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass); 6 | DROP FUNCTION IF EXISTS diesel_set_updated_at(); 7 | -------------------------------------------------------------------------------- /grpc-telegram-bot/server/migrations/00000000000000_diesel_initial_setup/up.sql: -------------------------------------------------------------------------------- 1 | -- This file was automatically created by Diesel to setup helper functions 2 | -- and other internal bookkeeping. This file is safe to edit, any future 3 | -- changes will be added to existing projects as new migrations. 4 | 5 | 6 | 7 | 8 | -- Sets up a trigger for the given table to automatically set a column called 9 | -- `updated_at` whenever the row is modified (unless `updated_at` was included 10 | -- in the modified columns) 11 | -- 12 | -- # Example 13 | -- 14 | -- ```sql 15 | -- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW()); 16 | -- 17 | -- SELECT diesel_manage_updated_at('users'); 18 | -- ``` 19 | CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$ 20 | BEGIN 21 | EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s 22 | FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl); 23 | END; 24 | $$ LANGUAGE plpgsql; 25 | 26 | CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$ 27 | BEGIN 28 | IF ( 29 | NEW IS DISTINCT FROM OLD AND 30 | NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at 31 | ) THEN 32 | NEW.updated_at := current_timestamp; 33 | END IF; 34 | RETURN NEW; 35 | END; 36 | $$ LANGUAGE plpgsql; 37 | -------------------------------------------------------------------------------- /grpc-telegram-bot/server/migrations/2021-02-20-104857_init_structure/down.sql: -------------------------------------------------------------------------------- 1 | drop table satellites; 2 | drop table planets; 3 | -------------------------------------------------------------------------------- /grpc-telegram-bot/server/migrations/2021-02-20-104857_init_structure/up.sql: -------------------------------------------------------------------------------- 1 | create table planets ( 2 | id serial primary key, 3 | name varchar not null unique, 4 | type varchar(20) not null, 5 | mean_radius numeric(10,1) not null, 6 | mass numeric(30) not null 7 | ); 8 | 9 | create table satellites ( 10 | id serial primary key, 11 | name varchar not null unique, 12 | first_spacecraft_landing_date date, 13 | planet_id integer not null references planets(id) 14 | ); 15 | -------------------------------------------------------------------------------- /grpc-telegram-bot/server/migrations/2021-02-20-104902_init_data/down.sql: -------------------------------------------------------------------------------- 1 | delete * from satellites; 2 | delete * from planets; 3 | -------------------------------------------------------------------------------- /grpc-telegram-bot/server/migrations/2021-02-20-104902_init_data/up.sql: -------------------------------------------------------------------------------- 1 | insert into planets(name, type, mean_radius, mass) values ('Mercury', 'TERRESTRIAL_PLANET', 2439.7, 0.33 * power(10, 24)); 2 | insert into planets(name, type, mean_radius, mass) values ('Venus', 'TERRESTRIAL_PLANET', 6051.8, 4.87 * power(10, 24)); 3 | insert into planets(name, type, mean_radius, mass) values ('Earth', 'TERRESTRIAL_PLANET', 6371.0, 5.97 * power(10, 24)); 4 | insert into planets(name, type, mean_radius, mass) values ('Mars', 'TERRESTRIAL_PLANET', 3389.5, 0.642 * power(10, 24)); 5 | insert into planets(name, type, mean_radius, mass) values ('Jupiter', 'GAS_GIANT', 69911.0, 1898.0 * power(10, 24)); 6 | insert into planets(name, type, mean_radius, mass) values ('Saturn', 'GAS_GIANT', 58232.0, 568.0 * power(10, 24)); 7 | insert into planets(name, type, mean_radius, mass) values ('Uranus', 'ICE_GIANT', 25362.0, 86.8 * power(10, 24)); 8 | insert into planets(name, type, mean_radius, mass) values ('Neptune', 'ICE_GIANT', 24622.0, 102.0 * power(10, 24)); 9 | 10 | insert into satellites(name, first_spacecraft_landing_date, planet_id) values ('Moon', '1959-09-13', (select id from planets where name = 'Earth')); 11 | insert into satellites(name, planet_id) values ('Phobos', (select id from planets where name = 'Mars')); 12 | insert into satellites(name, planet_id) values ('Deimos', (select id from planets where name = 'Mars')); 13 | insert into satellites(name, planet_id) values ('Io', (select id from planets where name = 'Jupiter')); 14 | insert into satellites(name, planet_id) values ('Europa', (select id from planets where name = 'Jupiter')); 15 | insert into satellites(name, planet_id) values ('Ganymede', (select id from planets where name = 'Jupiter')); 16 | insert into satellites(name, planet_id) values ('Callisto', (select id from planets where name = 'Jupiter')); 17 | insert into satellites(name, planet_id) values ('Titan', (select id from planets where name = 'Saturn')); 18 | insert into satellites(name, planet_id) values ('Ariel', (select id from planets where name = 'Uranus')); 19 | insert into satellites(name, planet_id) values ('Umbriel', (select id from planets where name = 'Uranus')); 20 | insert into satellites(name, planet_id) values ('Titania', (select id from planets where name = 'Uranus')); 21 | insert into satellites(name, planet_id) values ('Oberon', (select id from planets where name = 'Uranus')); 22 | insert into satellites(name, planet_id) values ('Miranda', (select id from planets where name = 'Uranus')); 23 | insert into satellites(name, planet_id) values ('Triton', (select id from planets where name = 'Neptune')); 24 | -------------------------------------------------------------------------------- /grpc-telegram-bot/server/src/conversion.rs: -------------------------------------------------------------------------------- 1 | use bigdecimal::ToPrimitive; 2 | use chrono::NaiveTime; 3 | use rust_embed::RustEmbed; 4 | 5 | use solar_system_info_rpc::solar_system_info::{ 6 | Planet as GrpcPlanet, Satellite as GrpcSatellite, Type as GrpcType, 7 | }; 8 | 9 | use crate::persistence::model::{PlanetEntity, SatelliteEntity}; 10 | 11 | pub struct PlanetWrapper { 12 | pub(crate) planet: PlanetEntity, 13 | pub(crate) satellites: Vec, 14 | } 15 | 16 | impl From for GrpcPlanet { 17 | fn from(pw: PlanetWrapper) -> GrpcPlanet { 18 | let planet = pw.planet; 19 | let planet_type: GrpcType = convert_string_to_planet_type(&planet.type_); 20 | 21 | let filename = format!("{}.jpg", planet.name.to_lowercase()); 22 | let image = Asset::get(&filename).expect("Failed to open image"); 23 | 24 | GrpcPlanet { 25 | id: planet.id as u64, 26 | name: planet.name, 27 | r#type: planet_type.into(), 28 | mean_radius: planet.mean_radius.to_f32().expect("Can't convert to f32"), 29 | mass: planet.mass.to_f32().expect("Can't convert mass"), 30 | satellites: pw.satellites.into_iter().map(|s| s.into()).collect(), 31 | image: image.data.to_vec(), 32 | } 33 | } 34 | } 35 | 36 | impl From for GrpcSatellite { 37 | fn from(entity: SatelliteEntity) -> Self { 38 | let first_spacecraft_landing_date: Option<::prost_types::Timestamp> = entity 39 | .first_spacecraft_landing_date 40 | .map(|d| ::prost_types::Timestamp { 41 | seconds: d.and_time(NaiveTime::from_hms(0, 0, 0)).timestamp(), 42 | nanos: 0, 43 | }); 44 | GrpcSatellite { 45 | id: entity.id as u64, 46 | name: entity.name, 47 | first_spacecraft_landing_date, 48 | } 49 | } 50 | } 51 | 52 | fn convert_string_to_planet_type(planet_type: &str) -> GrpcType { 53 | match planet_type { 54 | "TERRESTRIAL_PLANET" => GrpcType::TerrestrialPlanet, 55 | "GAS_GIANT" => GrpcType::GasGiant, 56 | "ICE_GIANT" => GrpcType::IceGiant, 57 | "DWARF_PLANET" => GrpcType::DwarfPlanet, 58 | _ => panic!("Planet type {} is not found", planet_type), 59 | } 60 | } 61 | 62 | #[derive(RustEmbed)] 63 | #[folder = "images"] 64 | struct Asset; 65 | -------------------------------------------------------------------------------- /grpc-telegram-bot/server/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate diesel; 3 | #[macro_use] 4 | extern crate diesel_migrations; 5 | 6 | use std::pin::Pin; 7 | 8 | use diesel::r2d2::{ConnectionManager, PooledConnection}; 9 | use diesel::result::Error; 10 | use diesel::PgConnection; 11 | use dotenv::dotenv; 12 | use futures::Stream; 13 | use log::{debug, error, info}; 14 | use tokio::sync::mpsc; 15 | use tokio_stream::StreamExt; 16 | use tonic::{transport::Server, Request, Response, Status}; 17 | 18 | use solar_system_info_rpc::solar_system_info::solar_system_info_server::{ 19 | SolarSystemInfo, SolarSystemInfoServer, 20 | }; 21 | use solar_system_info_rpc::solar_system_info::{ 22 | Planet, PlanetRequest, PlanetResponse, PlanetsListResponse, 23 | }; 24 | 25 | use crate::conversion::PlanetWrapper; 26 | use crate::persistence::connection::{create_connection_pool, PgPool}; 27 | 28 | embed_migrations!(); 29 | 30 | mod conversion; 31 | mod persistence; 32 | 33 | #[tokio::main] 34 | async fn main() -> Result<(), Box> { 35 | dotenv().ok(); 36 | env_logger::init(); 37 | 38 | info!("Starting Solar System info server"); 39 | 40 | let addr = std::env::var("GRPC_SERVER_ADDRESS")?.parse()?; 41 | 42 | let pool = create_connection_pool(); 43 | run_migrations(&pool); 44 | 45 | let solar_system_info = SolarSystemInfoService { pool }; 46 | let svc = SolarSystemInfoServer::new(solar_system_info); 47 | 48 | Server::builder().add_service(svc).serve(addr).await?; 49 | 50 | Ok(()) 51 | } 52 | 53 | struct SolarSystemInfoService { 54 | pool: PgPool, 55 | } 56 | 57 | #[tonic::async_trait] 58 | impl SolarSystemInfo for SolarSystemInfoService { 59 | type GetPlanetsStream = 60 | Pin> + Send + Sync + 'static>>; 61 | 62 | async fn get_planets_list( 63 | &self, 64 | request: Request<()>, 65 | ) -> Result, Status> { 66 | debug!("Got a request: {:?}", request); 67 | 68 | let names_of_planets = persistence::repository::get_names(&get_connection(&self.pool)) 69 | .expect("Can't get names of the planets"); 70 | 71 | let reply = PlanetsListResponse { 72 | list: names_of_planets, 73 | }; 74 | 75 | Ok(Response::new(reply)) 76 | } 77 | 78 | async fn get_planets( 79 | &self, 80 | request: Request<()>, 81 | ) -> Result, Status> { 82 | debug!("Got a request: {:?}", request); 83 | 84 | let (tx, rx) = mpsc::channel(4); 85 | 86 | let planets: Vec = persistence::repository::get_all(&get_connection(&self.pool)) 87 | .expect("Can't load planets") 88 | .into_iter() 89 | .map(|p| { 90 | PlanetWrapper { 91 | planet: p.0, 92 | satellites: p.1, 93 | } 94 | .into() 95 | }) 96 | .collect(); 97 | 98 | tokio::spawn(async move { 99 | let mut stream = tokio_stream::iter(&planets); 100 | 101 | while let Some(planet) = stream.next().await { 102 | tx.send(Ok(PlanetResponse { 103 | planet: Some(planet.clone()), 104 | })) 105 | .await 106 | .unwrap(); 107 | } 108 | }); 109 | 110 | Ok(Response::new(Box::pin( 111 | tokio_stream::wrappers::ReceiverStream::new(rx), 112 | ))) 113 | } 114 | 115 | async fn get_planet( 116 | &self, 117 | request: Request, 118 | ) -> Result, Status> { 119 | debug!("Got a request: {:?}", request); 120 | 121 | let planet_name = request.into_inner().name; 122 | 123 | let planet = 124 | persistence::repository::get_by_name(&planet_name, &get_connection(&self.pool)); 125 | 126 | match planet { 127 | Ok(planet) => { 128 | let planet = PlanetWrapper { 129 | planet: planet.0, 130 | satellites: planet.1, 131 | } 132 | .into(); 133 | 134 | let reply = PlanetResponse { 135 | planet: Some(planet), 136 | }; 137 | 138 | Ok(Response::new(reply)) 139 | } 140 | Err(e) => { 141 | error!( 142 | "There was an error while getting a planet {}: {}", 143 | &planet_name, e 144 | ); 145 | match e { 146 | Error::NotFound => Err(Status::not_found(format!( 147 | "Planet with name {} not found", 148 | &planet_name 149 | ))), 150 | _ => Err(Status::unknown(format!( 151 | "There was an error while getting a planet {}: {}", 152 | &planet_name, e 153 | ))), 154 | } 155 | } 156 | } 157 | } 158 | } 159 | 160 | fn run_migrations(pool: &PgPool) { 161 | let conn = pool.get().expect("Can't get DB connection"); 162 | embedded_migrations::run(&conn).expect("Failed to run database migrations"); 163 | } 164 | 165 | type Conn = PooledConnection>; 166 | 167 | fn get_connection(pool: &PgPool) -> Conn { 168 | pool.get().expect("Can't get DB connection") 169 | } 170 | -------------------------------------------------------------------------------- /grpc-telegram-bot/server/src/persistence/connection.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | use diesel::pg::PgConnection; 4 | use diesel::r2d2::{ConnectionManager, Pool}; 5 | 6 | pub type PgPool = Pool>; 7 | 8 | pub fn create_connection_pool() -> PgPool { 9 | let db_url = env::var("DATABASE_URL").expect("Can't get DB URL"); 10 | let manager = ConnectionManager::::new(db_url); 11 | Pool::builder() 12 | .build(manager) 13 | .expect("Failed to create pool") 14 | } 15 | -------------------------------------------------------------------------------- /grpc-telegram-bot/server/src/persistence/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod connection; 2 | pub(crate) mod model; 3 | pub(crate) mod repository; 4 | mod schema; 5 | -------------------------------------------------------------------------------- /grpc-telegram-bot/server/src/persistence/model.rs: -------------------------------------------------------------------------------- 1 | use bigdecimal::BigDecimal; 2 | use chrono::NaiveDate; 3 | 4 | use crate::persistence::schema::{planets, satellites}; 5 | 6 | #[derive(Identifiable, Queryable)] 7 | #[table_name = "planets"] 8 | pub struct PlanetEntity { 9 | pub id: i32, 10 | pub name: String, 11 | pub type_: String, 12 | pub mean_radius: BigDecimal, 13 | pub mass: BigDecimal, 14 | } 15 | 16 | #[derive(Identifiable, Queryable, Associations)] 17 | #[table_name = "satellites"] 18 | #[belongs_to(PlanetEntity, foreign_key = "planet_id")] 19 | pub struct SatelliteEntity { 20 | pub id: i32, 21 | pub name: String, 22 | pub first_spacecraft_landing_date: Option, 23 | pub planet_id: i32, 24 | } 25 | -------------------------------------------------------------------------------- /grpc-telegram-bot/server/src/persistence/repository.rs: -------------------------------------------------------------------------------- 1 | use diesel::prelude::*; 2 | 3 | use crate::persistence::model::{PlanetEntity, SatelliteEntity}; 4 | use crate::persistence::schema::planets; 5 | use crate::Conn; 6 | 7 | pub fn get_names(conn: &Conn) -> QueryResult> { 8 | let names = planets::table.select(planets::name).load(conn)?; 9 | Ok(names) 10 | } 11 | 12 | pub fn get_all(conn: &Conn) -> QueryResult)>> { 13 | let planets: Vec = planets::table.load(conn)?; 14 | let satellites = SatelliteEntity::belonging_to(&planets) 15 | .load(conn)? 16 | .grouped_by(&planets); 17 | 18 | let result = planets.into_iter().zip(satellites).collect::>(); 19 | 20 | Ok(result) 21 | } 22 | 23 | pub fn get_by_name(name: &str, conn: &Conn) -> QueryResult<(PlanetEntity, Vec)> { 24 | let planet: PlanetEntity = planets::table 25 | .filter(planets::name.ilike(name)) 26 | .first(conn)?; 27 | let satellites = SatelliteEntity::belonging_to(&planet).load(conn)?; 28 | 29 | Ok((planet, satellites)) 30 | } 31 | -------------------------------------------------------------------------------- /grpc-telegram-bot/server/src/persistence/schema.rs: -------------------------------------------------------------------------------- 1 | table! { 2 | planets (id) { 3 | id -> Int4, 4 | name -> Varchar, 5 | #[sql_name = "type"] 6 | type_ -> Varchar, 7 | mean_radius -> Numeric, 8 | mass -> Numeric, 9 | } 10 | } 11 | 12 | table! { 13 | satellites (id) { 14 | id -> Int4, 15 | name -> Varchar, 16 | first_spacecraft_landing_date -> Nullable, 17 | planet_id -> Int4, 18 | } 19 | } 20 | 21 | joinable!(satellites -> planets (planet_id)); 22 | 23 | allow_tables_to_appear_in_same_query!(planets, satellites,); 24 | -------------------------------------------------------------------------------- /mongodb-redis/.env: -------------------------------------------------------------------------------- 1 | # used when the application runs in Docker container 2 | MONGODB_USERNAME=admin 3 | MONGODB_PASSWORD=password 4 | -------------------------------------------------------------------------------- /mongodb-redis/.env.local: -------------------------------------------------------------------------------- 1 | # used when the application runs on a host system 2 | MONGODB_URI=mongodb://admin:password@localhost:27017 3 | REDIS_URI=redis://localhost 4 | RUST_LOG=debug 5 | # defines whether to enable REST C(R)UD methods 6 | ENABLE_WRITING_HANDLERS=true 7 | -------------------------------------------------------------------------------- /mongodb-redis/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mongodb-redis" 3 | version = "0.1.0" 4 | authors = ["Roman Kudryashov "] 5 | edition = "2021" 6 | 7 | [dependencies] 8 | mongodb = "2.1.0" 9 | redis = { version = "0.21.4", features = ["tokio-comp", "connection-manager"] } 10 | actix-web = "4.0.0-beta.15" 11 | tokio = "1.15.0" 12 | tokio-stream = "0.1.8" 13 | chrono = { version = "0.4.19", features = ["serde"] } 14 | serde = "1.0.132" 15 | serde_json = "1.0.73" 16 | dotenv = "0.15.0" 17 | derive_more = "0.99.17" 18 | log = "0.4.14" 19 | env_logger = "0.9.0" 20 | rust-embed = "6.3.0" 21 | mime = "0.3.16" 22 | prometheus = { version = "0.13.0", features = ["process"] } 23 | lazy_static = "1.4.0" 24 | -------------------------------------------------------------------------------- /mongodb-redis/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:1.57 2 | 3 | ENV CARGO_TERM_COLOR always 4 | 5 | # create empty project for caching dependencies 6 | RUN USER=root cargo new --bin /mongodb-redis/docker-build 7 | WORKDIR /mongodb-redis/docker-build 8 | COPY /Cargo.lock ./ 9 | COPY /mongodb-redis/Cargo.toml ./ 10 | # cache dependencies 11 | RUN cargo install --path . --locked 12 | COPY /mongodb-redis/ ./ 13 | RUN touch ./src/main.rs 14 | RUN cargo install --path . --locked 15 | 16 | FROM debian:buster-slim 17 | COPY --from=0 /usr/local/cargo/bin/mongodb-redis /usr/local/bin/mongodb-redis 18 | CMD ["mongodb-redis"] 19 | -------------------------------------------------------------------------------- /mongodb-redis/docker-compose.override.yml: -------------------------------------------------------------------------------- 1 | # development configuration + services for monitoring 2 | version: '3.8' 3 | services: 4 | 5 | mongodb-redis: 6 | build: 7 | context: .. 8 | dockerfile: ./mongodb-redis/Dockerfile 9 | environment: 10 | ENABLE_WRITING_HANDLERS: 'true' 11 | 12 | # services for monitoring 13 | # comment services below if you don't need monitoring 14 | prometheus: 15 | image: prom/prometheus:latest 16 | container_name: prometheus 17 | restart: always 18 | ports: 19 | - '9090:9090' 20 | volumes: 21 | - ./monitoring/prometheus:/etc/prometheus 22 | command: 23 | - '--config.file=/etc/prometheus/prometheus.yml' 24 | - '--web.external-url=http://localhost:9090' 25 | 26 | grafana: 27 | image: grafana/grafana:latest 28 | container_name: grafana 29 | restart: always 30 | ports: 31 | - '3000:3000' 32 | volumes: 33 | - ./monitoring/grafana/data:/var/lib/grafana 34 | - ./monitoring/grafana/provisioning:/etc/grafana/provisioning 35 | environment: 36 | GF_SECURITY_ADMIN_USER: admin 37 | GF_SECURITY_ADMIN_PASSWORD: admin 38 | 39 | alertmanager: 40 | image: prom/alertmanager:latest 41 | container_name: alertmanager 42 | ports: 43 | - '9093:9093' 44 | volumes: 45 | - ./monitoring/alertmanager:/etc/alertmanager 46 | command: 47 | - '--config.file=/etc/alertmanager/alertmanager.yml' 48 | - '--web.external-url=http://localhost:9093' 49 | 50 | cadvisor: 51 | image: gcr.io/cadvisor/cadvisor:latest 52 | container_name: cadvisor 53 | restart: always 54 | ports: 55 | - '8080:8080' 56 | volumes: 57 | - /:/rootfs:ro 58 | - /var/run:/var/run:rw 59 | - /sys:/sys:ro 60 | - /var/lib/docker/:/var/lib/docker:ro 61 | -------------------------------------------------------------------------------- /mongodb-redis/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | 5 | mongodb-redis: 6 | image: kudryashovroman/mongodb-redis:latest 7 | container_name: mongodb-redis 8 | restart: always 9 | depends_on: 10 | - mongodb 11 | - redis 12 | environment: 13 | MONGODB_URI: mongodb://$MONGODB_USERNAME:$MONGODB_PASSWORD@mongodb:27017 14 | REDIS_URI: redis://redis 15 | RUST_LOG: debug 16 | ENABLE_WRITING_HANDLERS: 'false' 17 | ports: 18 | - '9000:9000' 19 | 20 | mongodb: 21 | image: mongo:5 22 | container_name: mongodb 23 | restart: always 24 | environment: 25 | MONGO_INITDB_ROOT_USERNAME: $MONGODB_USERNAME 26 | MONGO_INITDB_ROOT_PASSWORD: $MONGODB_PASSWORD 27 | MONGO_INITDB_DATABASE: solar_system_info 28 | ports: 29 | - '27017:27017' 30 | 31 | mongodb-seed: 32 | image: mongo:5 33 | container_name: mongodb-seed 34 | depends_on: 35 | - mongodb 36 | volumes: 37 | - ./mongodb-init:/mongodb-init 38 | links: 39 | - mongodb 40 | command: 41 | mongoimport --host mongodb --db solar_system_info --collection planets --authenticationDatabase admin --username $MONGODB_USERNAME --password $MONGODB_PASSWORD --drop --jsonArray --file /mongodb-init/init.json 42 | 43 | redis: 44 | image: redis:alpine 45 | container_name: redis 46 | restart: always 47 | ports: 48 | - '6379:6379' 49 | -------------------------------------------------------------------------------- /mongodb-redis/images/earth.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkudryashov/exploring-rust-ecosystem/56f17d089d7383313981dbd8208527158bd3bb65/mongodb-redis/images/earth.jpg -------------------------------------------------------------------------------- /mongodb-redis/images/jupiter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkudryashov/exploring-rust-ecosystem/56f17d089d7383313981dbd8208527158bd3bb65/mongodb-redis/images/jupiter.jpg -------------------------------------------------------------------------------- /mongodb-redis/images/mars.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkudryashov/exploring-rust-ecosystem/56f17d089d7383313981dbd8208527158bd3bb65/mongodb-redis/images/mars.jpg -------------------------------------------------------------------------------- /mongodb-redis/images/mercury.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkudryashov/exploring-rust-ecosystem/56f17d089d7383313981dbd8208527158bd3bb65/mongodb-redis/images/mercury.jpg -------------------------------------------------------------------------------- /mongodb-redis/images/neptune.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkudryashov/exploring-rust-ecosystem/56f17d089d7383313981dbd8208527158bd3bb65/mongodb-redis/images/neptune.jpg -------------------------------------------------------------------------------- /mongodb-redis/images/saturn.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkudryashov/exploring-rust-ecosystem/56f17d089d7383313981dbd8208527158bd3bb65/mongodb-redis/images/saturn.jpg -------------------------------------------------------------------------------- /mongodb-redis/images/uranus.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkudryashov/exploring-rust-ecosystem/56f17d089d7383313981dbd8208527158bd3bb65/mongodb-redis/images/uranus.jpg -------------------------------------------------------------------------------- /mongodb-redis/images/venus.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkudryashov/exploring-rust-ecosystem/56f17d089d7383313981dbd8208527158bd3bb65/mongodb-redis/images/venus.jpg -------------------------------------------------------------------------------- /mongodb-redis/mongodb-init/init.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Mercury", 4 | "type": "TerrestrialPlanet", 5 | "mean_radius": 2439.7 6 | }, 7 | { 8 | "name": "Venus", 9 | "type": "TerrestrialPlanet", 10 | "mean_radius": 6051.8 11 | }, 12 | { 13 | "name": "Earth", 14 | "type": "TerrestrialPlanet", 15 | "mean_radius": 6371.0, 16 | "satellites": [ 17 | { 18 | "name": "Moon", 19 | "first_spacecraft_landing_date": { 20 | "$date": "1959-09-13T00:00:00Z" 21 | } 22 | } 23 | ] 24 | }, 25 | { 26 | "name": "Mars", 27 | "type": "TerrestrialPlanet", 28 | "mean_radius": 3389.5, 29 | "satellites": [ 30 | { 31 | "name": "Phobos" 32 | }, 33 | { 34 | "name": "Deimos" 35 | } 36 | ] 37 | }, 38 | { 39 | "name": "Jupiter", 40 | "type": "GasGiant", 41 | "mean_radius": 69911.0, 42 | "satellites": [ 43 | { 44 | "name": "Io" 45 | }, 46 | { 47 | "name": "Europa" 48 | }, 49 | { 50 | "name": "Ganymede" 51 | }, 52 | { 53 | "name": "Callisto" 54 | } 55 | ] 56 | }, 57 | { 58 | "name": "Saturn", 59 | "type": "GasGiant", 60 | "mean_radius": 58232.0, 61 | "satellites": [ 62 | { 63 | "name": "Titan" 64 | } 65 | ] 66 | }, 67 | { 68 | "name": "Uranus", 69 | "type": "IceGiant", 70 | "mean_radius": 25362.0, 71 | "satellites": [ 72 | { 73 | "name": "Ariel" 74 | }, 75 | { 76 | "name": "Umbriel" 77 | }, 78 | { 79 | "name": "Titania" 80 | }, 81 | { 82 | "name": "Oberon" 83 | }, 84 | { 85 | "name": "Miranda" 86 | } 87 | ] 88 | }, 89 | { 90 | "name": "Neptune", 91 | "type": "IceGiant", 92 | "mean_radius": 24622.0, 93 | "satellites": [ 94 | { 95 | "name": "Triton" 96 | } 97 | ] 98 | } 99 | ] 100 | -------------------------------------------------------------------------------- /mongodb-redis/monitoring/alertmanager/alertmanager.yml: -------------------------------------------------------------------------------- 1 | route: 2 | receiver: gmail 3 | 4 | receivers: 5 | - name: gmail 6 | email_configs: 7 | - to: recipient@gmail.com 8 | from: email_id@gmail.com 9 | smarthost: smtp.gmail.com:587 10 | auth_username: email_id@gmail.com 11 | auth_identity: email_id@gmail.com 12 | auth_password: password 13 | -------------------------------------------------------------------------------- /mongodb-redis/monitoring/grafana/provisioning/dashboards/providers.yml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | providers: 4 | - name: 'default' 5 | folder: 'default' 6 | type: file 7 | allowUiUpdates: true 8 | updateIntervalSeconds: 30 9 | options: 10 | path: /etc/grafana/provisioning/dashboards 11 | foldersFromFilesStructure: true 12 | -------------------------------------------------------------------------------- /mongodb-redis/monitoring/grafana/provisioning/dashboards/webapp_metrics.json: -------------------------------------------------------------------------------- 1 | { 2 | "annotations": { 3 | "list": [ 4 | { 5 | "builtIn": 1, 6 | "datasource": "-- Grafana --", 7 | "enable": true, 8 | "hide": true, 9 | "iconColor": "rgba(0, 211, 255, 1)", 10 | "name": "Annotations & Alerts", 11 | "target": { 12 | "limit": 100, 13 | "matchAny": false, 14 | "tags": [], 15 | "type": "dashboard" 16 | }, 17 | "type": "dashboard" 18 | } 19 | ] 20 | }, 21 | "editable": true, 22 | "gnetId": null, 23 | "graphTooltip": 0, 24 | "id": 1, 25 | "links": [], 26 | "panels": [ 27 | { 28 | "collapsed": false, 29 | "datasource": null, 30 | "fieldConfig": { 31 | "defaults": {}, 32 | "overrides": [] 33 | }, 34 | "gridPos": { 35 | "h": 1, 36 | "w": 24, 37 | "x": 0, 38 | "y": 0 39 | }, 40 | "id": 14, 41 | "panels": [], 42 | "title": "Web application metrics", 43 | "type": "row" 44 | }, 45 | { 46 | "datasource": null, 47 | "fieldConfig": { 48 | "defaults": { 49 | "color": { 50 | "mode": "continuous-YlBl" 51 | }, 52 | "custom": { 53 | "axisLabel": "", 54 | "axisPlacement": "auto", 55 | "barAlignment": 0, 56 | "drawStyle": "line", 57 | "fillOpacity": 15, 58 | "gradientMode": "none", 59 | "hideFrom": { 60 | "legend": false, 61 | "tooltip": false, 62 | "viz": false 63 | }, 64 | "lineInterpolation": "linear", 65 | "lineWidth": 1, 66 | "pointSize": 5, 67 | "scaleDistribution": { 68 | "type": "linear" 69 | }, 70 | "showPoints": "never", 71 | "spanNulls": false, 72 | "stacking": { 73 | "group": "A", 74 | "mode": "none" 75 | }, 76 | "thresholdsStyle": { 77 | "mode": "off" 78 | } 79 | }, 80 | "decimals": 2, 81 | "mappings": [], 82 | "thresholds": { 83 | "mode": "absolute", 84 | "steps": [ 85 | { 86 | "color": "green", 87 | "value": null 88 | }, 89 | { 90 | "color": "red", 91 | "value": 80 92 | } 93 | ] 94 | } 95 | }, 96 | "overrides": [] 97 | }, 98 | "gridPos": { 99 | "h": 11, 100 | "w": 12, 101 | "x": 0, 102 | "y": 1 103 | }, 104 | "id": 1, 105 | "interval": "5s", 106 | "options": { 107 | "legend": { 108 | "calcs": [], 109 | "displayMode": "list", 110 | "placement": "right" 111 | }, 112 | "tooltip": { 113 | "mode": "single" 114 | } 115 | }, 116 | "targets": [ 117 | { 118 | "exemplar": true, 119 | "expr": "rate(http_requests_total[5m])", 120 | "interval": "", 121 | "legendFormat": "{{method}} {{path}}", 122 | "refId": "A" 123 | } 124 | ], 125 | "title": "HTTP requests per second", 126 | "type": "timeseries" 127 | }, 128 | { 129 | "datasource": null, 130 | "fieldConfig": { 131 | "defaults": { 132 | "color": { 133 | "mode": "continuous-YlBl" 134 | }, 135 | "custom": { 136 | "axisLabel": "", 137 | "axisPlacement": "auto", 138 | "barAlignment": 0, 139 | "drawStyle": "line", 140 | "fillOpacity": 15, 141 | "gradientMode": "none", 142 | "hideFrom": { 143 | "legend": false, 144 | "tooltip": false, 145 | "viz": false 146 | }, 147 | "lineInterpolation": "linear", 148 | "lineWidth": 1, 149 | "pointSize": 5, 150 | "scaleDistribution": { 151 | "type": "linear" 152 | }, 153 | "showPoints": "never", 154 | "spanNulls": false, 155 | "stacking": { 156 | "group": "A", 157 | "mode": "none" 158 | }, 159 | "thresholdsStyle": { 160 | "mode": "off" 161 | } 162 | }, 163 | "mappings": [], 164 | "thresholds": { 165 | "mode": "absolute", 166 | "steps": [ 167 | { 168 | "color": "green", 169 | "value": null 170 | }, 171 | { 172 | "color": "red", 173 | "value": 80 174 | } 175 | ] 176 | }, 177 | "unit": "s" 178 | }, 179 | "overrides": [] 180 | }, 181 | "gridPos": { 182 | "h": 11, 183 | "w": 12, 184 | "x": 12, 185 | "y": 1 186 | }, 187 | "id": 3, 188 | "interval": "5s", 189 | "options": { 190 | "legend": { 191 | "calcs": [], 192 | "displayMode": "list", 193 | "placement": "right" 194 | }, 195 | "tooltip": { 196 | "mode": "single" 197 | } 198 | }, 199 | "pluginVersion": "8.1.4", 200 | "targets": [ 201 | { 202 | "exemplar": true, 203 | "expr": "rate(http_response_time_seconds_sum[5m]) / rate(http_response_time_seconds_count[5m])", 204 | "interval": "", 205 | "legendFormat": "{{method}} {{path}}", 206 | "refId": "A" 207 | } 208 | ], 209 | "title": "Mean response time", 210 | "type": "timeseries" 211 | }, 212 | { 213 | "datasource": null, 214 | "fieldConfig": { 215 | "defaults": { 216 | "color": { 217 | "mode": "thresholds" 218 | }, 219 | "mappings": [], 220 | "thresholds": { 221 | "mode": "absolute", 222 | "steps": [ 223 | { 224 | "color": "green", 225 | "value": null 226 | }, 227 | { 228 | "color": "red", 229 | "value": 80 230 | } 231 | ] 232 | } 233 | }, 234 | "overrides": [] 235 | }, 236 | "gridPos": { 237 | "h": 6, 238 | "w": 3, 239 | "x": 0, 240 | "y": 12 241 | }, 242 | "id": 18, 243 | "options": { 244 | "reduceOptions": { 245 | "calcs": [ 246 | "lastNotNull" 247 | ], 248 | "fields": "", 249 | "values": false 250 | }, 251 | "showThresholdLabels": false, 252 | "showThresholdMarkers": true, 253 | "text": {} 254 | }, 255 | "pluginVersion": "8.1.4", 256 | "targets": [ 257 | { 258 | "exemplar": true, 259 | "expr": "increase(http_requests_total{method=\"GET\",path=\"/planets\"}[1m])", 260 | "interval": "", 261 | "legendFormat": "", 262 | "refId": "A" 263 | } 264 | ], 265 | "title": "Requests number for the last minute", 266 | "type": "gauge" 267 | }, 268 | { 269 | "cards": { 270 | "cardPadding": null, 271 | "cardRound": null 272 | }, 273 | "color": { 274 | "cardColor": "#b4ff00", 275 | "colorScale": "sqrt", 276 | "colorScheme": "interpolateBlues", 277 | "exponent": 0.5, 278 | "mode": "spectrum" 279 | }, 280 | "dataFormat": "tsbuckets", 281 | "datasource": null, 282 | "gridPos": { 283 | "h": 12, 284 | "w": 21, 285 | "x": 3, 286 | "y": 12 287 | }, 288 | "heatmap": {}, 289 | "hideZeroBuckets": false, 290 | "highlightCards": true, 291 | "id": 16, 292 | "interval": null, 293 | "legend": { 294 | "show": false 295 | }, 296 | "maxDataPoints": null, 297 | "pluginVersion": "8.1.4", 298 | "reverseYBuckets": false, 299 | "targets": [ 300 | { 301 | "exemplar": true, 302 | "expr": "sum(increase(http_response_time_seconds_bucket{path=\"/planets\"}[30s])) by (le)", 303 | "format": "heatmap", 304 | "interval": "", 305 | "legendFormat": "{{le}}", 306 | "refId": "A" 307 | } 308 | ], 309 | "timeFrom": null, 310 | "title": "Response time distribution over time (`GET /planets`)", 311 | "tooltip": { 312 | "show": true, 313 | "showHistogram": false 314 | }, 315 | "type": "heatmap", 316 | "xAxis": { 317 | "show": true 318 | }, 319 | "xBucketNumber": 10, 320 | "xBucketSize": null, 321 | "yAxis": { 322 | "decimals": 2, 323 | "format": "s", 324 | "logBase": 1, 325 | "max": null, 326 | "min": null, 327 | "show": true, 328 | "splitFactor": null 329 | }, 330 | "yBucketBound": "auto", 331 | "yBucketNumber": 10, 332 | "yBucketSize": null 333 | }, 334 | { 335 | "datasource": null, 336 | "fieldConfig": { 337 | "defaults": { 338 | "color": { 339 | "mode": "continuous-YlBl" 340 | }, 341 | "decimals": 0, 342 | "mappings": [], 343 | "thresholds": { 344 | "mode": "absolute", 345 | "steps": [ 346 | { 347 | "color": "green", 348 | "value": null 349 | }, 350 | { 351 | "color": "red", 352 | "value": 80 353 | } 354 | ] 355 | } 356 | }, 357 | "overrides": [] 358 | }, 359 | "gridPos": { 360 | "h": 6, 361 | "w": 3, 362 | "x": 0, 363 | "y": 18 364 | }, 365 | "id": 2, 366 | "interval": "5s", 367 | "options": { 368 | "orientation": "auto", 369 | "reduceOptions": { 370 | "calcs": [ 371 | "lastNotNull" 372 | ], 373 | "fields": "", 374 | "values": false 375 | }, 376 | "showThresholdLabels": false, 377 | "showThresholdMarkers": true, 378 | "text": {} 379 | }, 380 | "pluginVersion": "8.1.4", 381 | "targets": [ 382 | { 383 | "exemplar": true, 384 | "expr": "http_connected_sse_clients", 385 | "interval": "", 386 | "legendFormat": "", 387 | "refId": "A" 388 | } 389 | ], 390 | "title": "Connected SSE clients", 391 | "type": "gauge" 392 | }, 393 | { 394 | "collapsed": false, 395 | "datasource": null, 396 | "fieldConfig": { 397 | "defaults": {}, 398 | "overrides": [] 399 | }, 400 | "gridPos": { 401 | "h": 1, 402 | "w": 24, 403 | "x": 0, 404 | "y": 24 405 | }, 406 | "id": 12, 407 | "panels": [], 408 | "title": "System metrics", 409 | "type": "row" 410 | }, 411 | { 412 | "datasource": null, 413 | "fieldConfig": { 414 | "defaults": { 415 | "color": { 416 | "mode": "continuous-YlBl" 417 | }, 418 | "custom": { 419 | "axisLabel": "", 420 | "axisPlacement": "auto", 421 | "barAlignment": 0, 422 | "drawStyle": "line", 423 | "fillOpacity": 15, 424 | "gradientMode": "none", 425 | "hideFrom": { 426 | "legend": false, 427 | "tooltip": false, 428 | "viz": false 429 | }, 430 | "lineInterpolation": "linear", 431 | "lineWidth": 1, 432 | "pointSize": 5, 433 | "scaleDistribution": { 434 | "type": "linear" 435 | }, 436 | "showPoints": "never", 437 | "spanNulls": false, 438 | "stacking": { 439 | "group": "A", 440 | "mode": "none" 441 | }, 442 | "thresholdsStyle": { 443 | "mode": "off" 444 | } 445 | }, 446 | "decimals": 2, 447 | "mappings": [], 448 | "thresholds": { 449 | "mode": "absolute", 450 | "steps": [ 451 | { 452 | "color": "green", 453 | "value": null 454 | }, 455 | { 456 | "color": "red", 457 | "value": 80 458 | } 459 | ] 460 | }, 461 | "unit": "percentunit" 462 | }, 463 | "overrides": [] 464 | }, 465 | "gridPos": { 466 | "h": 11, 467 | "w": 12, 468 | "x": 0, 469 | "y": 25 470 | }, 471 | "id": 4, 472 | "interval": "5s", 473 | "options": { 474 | "legend": { 475 | "calcs": [], 476 | "displayMode": "list", 477 | "placement": "bottom" 478 | }, 479 | "tooltip": { 480 | "mode": "single" 481 | } 482 | }, 483 | "targets": [ 484 | { 485 | "exemplar": true, 486 | "expr": "rate(process_cpu_seconds_total{job=\"mongodb_redis_web_app\"}[1m])", 487 | "interval": "", 488 | "legendFormat": "process", 489 | "refId": "A" 490 | }, 491 | { 492 | "exemplar": true, 493 | "expr": "sum(rate(container_cpu_usage_seconds_total{name='mongodb-redis'}[1m])) by (name)", 494 | "interval": "", 495 | "legendFormat": "container", 496 | "refId": "B" 497 | } 498 | ], 499 | "title": "CPU usage", 500 | "type": "timeseries" 501 | }, 502 | { 503 | "datasource": null, 504 | "fieldConfig": { 505 | "defaults": { 506 | "color": { 507 | "mode": "continuous-YlBl" 508 | }, 509 | "custom": { 510 | "axisLabel": "MiB", 511 | "axisPlacement": "left", 512 | "barAlignment": 0, 513 | "drawStyle": "line", 514 | "fillOpacity": 15, 515 | "gradientMode": "none", 516 | "hideFrom": { 517 | "legend": false, 518 | "tooltip": false, 519 | "viz": false 520 | }, 521 | "lineInterpolation": "linear", 522 | "lineWidth": 1, 523 | "pointSize": 5, 524 | "scaleDistribution": { 525 | "type": "linear" 526 | }, 527 | "showPoints": "never", 528 | "spanNulls": false, 529 | "stacking": { 530 | "group": "A", 531 | "mode": "none" 532 | }, 533 | "thresholdsStyle": { 534 | "mode": "off" 535 | } 536 | }, 537 | "mappings": [], 538 | "thresholds": { 539 | "mode": "absolute", 540 | "steps": [ 541 | { 542 | "color": "green", 543 | "value": null 544 | }, 545 | { 546 | "color": "red", 547 | "value": 80 548 | } 549 | ] 550 | } 551 | }, 552 | "overrides": [] 553 | }, 554 | "gridPos": { 555 | "h": 11, 556 | "w": 12, 557 | "x": 12, 558 | "y": 25 559 | }, 560 | "id": 5, 561 | "interval": "5s", 562 | "options": { 563 | "legend": { 564 | "calcs": [], 565 | "displayMode": "list", 566 | "placement": "bottom" 567 | }, 568 | "tooltip": { 569 | "mode": "single" 570 | } 571 | }, 572 | "targets": [ 573 | { 574 | "exemplar": true, 575 | "expr": "process_resident_memory_bytes{job=\"mongodb_redis_web_app\"} / 1024 / 1024", 576 | "interval": "", 577 | "legendFormat": "process", 578 | "refId": "A" 579 | }, 580 | { 581 | "exemplar": true, 582 | "expr": "container_memory_usage_bytes{name=\"mongodb-redis\"} / 1024 / 1024", 583 | "interval": "", 584 | "legendFormat": "container", 585 | "refId": "B" 586 | } 587 | ], 588 | "title": "Memory usage", 589 | "type": "timeseries" 590 | } 591 | ], 592 | "refresh": "5s", 593 | "schemaVersion": 1, 594 | "style": "dark", 595 | "tags": [], 596 | "templating": { 597 | "list": [] 598 | }, 599 | "time": { 600 | "from": "now-15m", 601 | "to": "now" 602 | }, 603 | "timepicker": { 604 | "refresh_intervals": [ 605 | "5s", 606 | "10s", 607 | "30s", 608 | "1m", 609 | "5m" 610 | ] 611 | }, 612 | "timezone": "", 613 | "title": "webapp_metrics", 614 | "uid": "7wcZ94dnz", 615 | "version": 1 616 | } 617 | -------------------------------------------------------------------------------- /mongodb-redis/monitoring/grafana/provisioning/datasources/datasources.yml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | datasources: 4 | - name: Prometheus 5 | type: prometheus 6 | access: proxy 7 | url: prometheus:9090 8 | isDefault: true 9 | -------------------------------------------------------------------------------- /mongodb-redis/monitoring/prometheus/prometheus.yml: -------------------------------------------------------------------------------- 1 | scrape_configs: 2 | 3 | - job_name: mongodb_redis_web_app 4 | scrape_interval: 5s 5 | static_configs: 6 | # if this is not working when you're running the application in a container (not on a host machine) try to use 'mongodb-redis:9000' 7 | - targets: ['host.docker.internal:9000'] 8 | 9 | - job_name: cadvisor 10 | scrape_interval: 5s 11 | static_configs: 12 | - targets: ['cadvisor:8080'] 13 | 14 | rule_files: 15 | - 'rules.yml' 16 | 17 | alerting: 18 | alertmanagers: 19 | - static_configs: 20 | - targets: ['alertmanager:9093'] 21 | -------------------------------------------------------------------------------- /mongodb-redis/monitoring/prometheus/rules.yml: -------------------------------------------------------------------------------- 1 | groups: 2 | - name: default 3 | rules: 4 | - alert: SseClients 5 | expr: http_connected_sse_clients > 0 6 | for: 1m 7 | labels: 8 | severity: high 9 | annotations: 10 | summary: Too many SSE clients 11 | -------------------------------------------------------------------------------- /mongodb-redis/readme.adoc: -------------------------------------------------------------------------------- 1 | = MongoDB + Redis demo 2 | 3 | image:https://romankudryashov.com/blog/2021/06/mongodb-redis-rust/images/architecture.png[Architecture,800] 4 | 5 | https://romankudryashov.com/blog/2021/06/mongodb-redis-rust/[Detailed description] 6 | 7 | == Monitoring Rust web application 8 | 9 | image:https://romankudryashov.com/blog/2021/11/monitoring-rust-web-application/images/architecture.png[Architecture,800] 10 | 11 | https://romankudryashov.com/blog/2021/11/monitoring-rust-web-application/[Detailed description] 12 | -------------------------------------------------------------------------------- /mongodb-redis/src/broadcaster.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Mutex; 2 | use std::time::Duration; 3 | 4 | use actix_web::web::{Bytes, Data}; 5 | use tokio::sync::mpsc::{self, Receiver, Sender}; 6 | use tokio::time; 7 | 8 | use crate::errors::CustomError; 9 | 10 | #[derive(Clone)] 11 | pub struct Broadcaster { 12 | clients: Vec>>, 13 | } 14 | 15 | impl Broadcaster { 16 | fn new() -> Self { 17 | Broadcaster { 18 | clients: Vec::new(), 19 | } 20 | } 21 | 22 | pub fn create() -> Data> { 23 | let me = Data::new(Mutex::new(Broadcaster::new())); 24 | 25 | // ping clients every 10 seconds to see if they are alive 26 | Broadcaster::spawn_ping(me.clone()); 27 | 28 | me 29 | } 30 | 31 | pub async fn new_client(&mut self) -> Receiver> { 32 | let (tx, rx) = mpsc::channel::>(100); 33 | 34 | tx.clone() 35 | .send(Ok(Bytes::from("data: Connected\n\n"))) 36 | .await 37 | .expect("Can't create a client"); 38 | 39 | self.clients.push(tx); 40 | crate::metrics::HTTP_CONNECTED_SSE_CLIENTS.inc(); 41 | rx 42 | } 43 | 44 | pub fn send(&self, msg: Bytes) { 45 | for client in self.clients.iter() { 46 | client 47 | .try_send(Ok(msg.clone())) 48 | .expect("Can't send a message"); 49 | } 50 | } 51 | 52 | fn spawn_ping(me: Data>) { 53 | tokio::spawn(async move { 54 | let mut interval = time::interval(Duration::from_secs(10)); 55 | 56 | loop { 57 | interval.tick().await; 58 | let mut broadcaster_mutex = me.lock().expect("Failed to lock broadcaster"); 59 | broadcaster_mutex.remove_stale_clients(); 60 | crate::metrics::HTTP_CONNECTED_SSE_CLIENTS 61 | .set(broadcaster_mutex.clients.len() as i64) 62 | } 63 | }); 64 | } 65 | 66 | fn remove_stale_clients(&mut self) { 67 | let mut ok_clients = Vec::new(); 68 | for client in self.clients.iter() { 69 | let result = client.clone().try_send(Ok(Bytes::from("data: Ping\n\n"))); 70 | 71 | if let Ok(()) = result { 72 | ok_clients.push(client.clone()); 73 | } 74 | } 75 | self.clients = ok_clients; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /mongodb-redis/src/db.rs: -------------------------------------------------------------------------------- 1 | use mongodb::bson::{doc, oid::ObjectId, Document}; 2 | use mongodb::{Client, Collection}; 3 | use rust_embed::RustEmbed; 4 | use tokio_stream::StreamExt; 5 | 6 | use crate::errors::CustomError; 7 | use crate::errors::CustomError::NotFound; 8 | use crate::model::{Planet, PlanetType}; 9 | 10 | const DB_NAME: &str = "solar_system_info"; 11 | const COLLECTION_NAME: &str = "planets"; 12 | 13 | #[derive(Clone, Debug)] 14 | pub struct MongoDbClient { 15 | client: Client, 16 | } 17 | 18 | impl MongoDbClient { 19 | pub async fn new(mongodb_uri: String) -> Self { 20 | let mongodb_client = Client::with_uri_str(mongodb_uri) 21 | .await 22 | .expect("Failed to create MongoDB client"); 23 | 24 | MongoDbClient { 25 | client: mongodb_client, 26 | } 27 | } 28 | 29 | pub async fn get_planets( 30 | &self, 31 | planet_type: Option, 32 | ) -> Result, CustomError> { 33 | let filter = planet_type.map(|pt| { 34 | doc! { "type": pt.to_string() } 35 | }); 36 | 37 | let mut planets = self.get_planets_collection().find(filter, None).await?; 38 | 39 | let mut result: Vec = Vec::new(); 40 | while let Some(planet) = planets.next().await { 41 | result.push(planet?); 42 | } 43 | 44 | Ok(result) 45 | } 46 | 47 | pub async fn create_planet(&self, planet: Planet) -> Result { 48 | let collection = self.get_planets_collection(); 49 | 50 | let insert_result = collection.insert_one(planet, None).await?; 51 | let filter = doc! { "_id": &insert_result.inserted_id }; 52 | collection.find_one(filter, None).await?.ok_or(NotFound { 53 | message: String::from("Can't find a created planet"), 54 | }) 55 | } 56 | 57 | pub async fn get_planet(&self, id: ObjectId) -> Result { 58 | let collection = self.get_planets_collection(); 59 | 60 | let filter = doc! { "_id": &id }; 61 | collection.find_one(filter, None).await?.ok_or(NotFound { 62 | message: format!("Can't find a planet by id: {}", &id), 63 | }) 64 | } 65 | 66 | pub async fn update_planet(&self, id: ObjectId, planet: Planet) -> Result { 67 | let collection = self.get_planets_collection(); 68 | 69 | let query = doc! { "_id": &id }; 70 | let update = doc! { "$set": Document::from(&planet) }; 71 | let _update_result = collection.update_one(query, update, None).await?; 72 | 73 | let filter = doc! { "_id": &id }; 74 | collection.find_one(filter, None).await?.ok_or(NotFound { 75 | message: format!("Can't find an updated planet by id: {}", &id), 76 | }) 77 | } 78 | 79 | pub async fn delete_planet(&self, id: ObjectId) -> Result<(), CustomError> { 80 | let collection = self.get_planets_collection(); 81 | 82 | let filter = doc! { "_id": &id }; 83 | collection 84 | .find_one_and_delete(filter, None) 85 | .await? 86 | .ok_or(NotFound { 87 | message: format!("Can't delete a planet by id: {}", id), 88 | })?; 89 | 90 | Ok(()) 91 | } 92 | 93 | fn get_planets_collection(&self) -> Collection { 94 | self.client 95 | .database(DB_NAME) 96 | .collection::(COLLECTION_NAME) 97 | } 98 | } 99 | 100 | pub async fn get_image_of_planet(planet_name: &str) -> Vec { 101 | let filename = format!("{}.jpg", planet_name.to_lowercase()); 102 | let image = Asset::get(&filename).expect("Failed to open image"); 103 | image.data.to_vec() 104 | } 105 | 106 | #[derive(RustEmbed)] 107 | #[folder = "images"] 108 | struct Asset; 109 | -------------------------------------------------------------------------------- /mongodb-redis/src/dto.rs: -------------------------------------------------------------------------------- 1 | use chrono::{NaiveDate, NaiveDateTime}; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use crate::model::{Planet, PlanetType, Satellite}; 5 | 6 | #[derive(Serialize, Deserialize)] 7 | pub struct PlanetDto { 8 | pub id: Option, 9 | pub name: String, 10 | pub r#type: PlanetType, 11 | pub mean_radius: f32, 12 | pub satellites: Option>, 13 | } 14 | 15 | #[derive(Serialize, Deserialize)] 16 | pub struct SatelliteDto { 17 | pub name: String, 18 | pub first_spacecraft_landing_date: Option, 19 | } 20 | 21 | #[derive(Serialize)] 22 | pub struct PlanetMessage { 23 | pub id: String, 24 | pub name: String, 25 | pub r#type: PlanetType, 26 | } 27 | 28 | impl From for PlanetDto { 29 | fn from(source: Planet) -> Self { 30 | PlanetDto { 31 | id: source.id.map(|id| id.to_string()), 32 | name: source.name, 33 | r#type: source.r#type, 34 | mean_radius: source.mean_radius, 35 | satellites: source 36 | .satellites 37 | .map(|satellites| satellites.into_iter().map(SatelliteDto::from).collect()), 38 | } 39 | } 40 | } 41 | 42 | impl From for SatelliteDto { 43 | fn from(source: Satellite) -> Self { 44 | SatelliteDto { 45 | name: source.name, 46 | first_spacecraft_landing_date: source 47 | .first_spacecraft_landing_date 48 | .map(|d| NaiveDateTime::from_timestamp(d.timestamp_millis() / 1000, 0).date()), 49 | } 50 | } 51 | } 52 | 53 | impl From<&Planet> for PlanetMessage { 54 | fn from(source: &Planet) -> Self { 55 | PlanetMessage { 56 | id: source 57 | .id 58 | .map(|id| id.to_string()) 59 | .expect("Planet.id is not specified"), 60 | name: source.name.clone(), 61 | r#type: source.r#type, 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /mongodb-redis/src/errors.rs: -------------------------------------------------------------------------------- 1 | use actix_web::error::ResponseError; 2 | use actix_web::http::header::ContentType; 3 | use actix_web::http::StatusCode; 4 | use actix_web::{HttpResponse, HttpResponseBuilder}; 5 | use derive_more::{Display, Error}; 6 | use log::error; 7 | use redis::RedisError; 8 | use serde::Serialize; 9 | 10 | #[derive(Debug, Display, Error)] 11 | pub enum CustomError { 12 | #[display(fmt = message)] 13 | MongoDbError { 14 | message: String, 15 | }, 16 | #[display(fmt = message)] 17 | RedisError { 18 | message: String, 19 | }, 20 | #[display(fmt = message)] 21 | NotFound { 22 | message: String, 23 | }, 24 | InternalError, 25 | #[display( 26 | fmt = "Actual requests count: {}. Permitted requests count: {}", 27 | actual_count, 28 | permitted_count 29 | )] 30 | TooManyRequests { 31 | actual_count: u64, 32 | permitted_count: u64, 33 | }, 34 | } 35 | 36 | impl CustomError { 37 | fn name(&self) -> String { 38 | let name = match self { 39 | Self::MongoDbError { message: _ } => "MongoDB error", 40 | Self::RedisError { message: _ } => "Redis error", 41 | Self::NotFound { message: _ } => "Resource not found", 42 | Self::InternalError => "Internal error", 43 | Self::TooManyRequests { 44 | actual_count: _, 45 | permitted_count: _, 46 | } => "Too many requests", 47 | }; 48 | 49 | String::from(name) 50 | } 51 | } 52 | 53 | impl ResponseError for CustomError { 54 | fn status_code(&self) -> StatusCode { 55 | match *self { 56 | CustomError::MongoDbError { message: _ } => StatusCode::INTERNAL_SERVER_ERROR, 57 | CustomError::RedisError { message: _ } => StatusCode::INTERNAL_SERVER_ERROR, 58 | CustomError::NotFound { message: _ } => StatusCode::NOT_FOUND, 59 | CustomError::InternalError => StatusCode::INTERNAL_SERVER_ERROR, 60 | CustomError::TooManyRequests { 61 | actual_count: _, 62 | permitted_count: _, 63 | } => StatusCode::TOO_MANY_REQUESTS, 64 | } 65 | } 66 | 67 | fn error_response(&self) -> HttpResponse { 68 | error!("Error: {}", self.to_string()); 69 | 70 | let error_response = ErrorResponse { 71 | error: self.name(), 72 | message: self.to_string(), 73 | }; 74 | 75 | HttpResponseBuilder::new(self.status_code()) 76 | .content_type(ContentType::json()) 77 | .body(serde_json::to_string(&error_response).expect("Can't serialize error response")) 78 | } 79 | } 80 | 81 | #[derive(Serialize)] 82 | struct ErrorResponse { 83 | error: String, 84 | message: String, 85 | } 86 | 87 | impl From for CustomError { 88 | fn from(source: mongodb::error::Error) -> Self { 89 | Self::MongoDbError { 90 | message: source.to_string(), 91 | } 92 | } 93 | } 94 | 95 | impl From for CustomError { 96 | fn from(source: mongodb::bson::de::Error) -> Self { 97 | Self::MongoDbError { 98 | message: source.to_string(), 99 | } 100 | } 101 | } 102 | 103 | impl From for CustomError { 104 | fn from(source: mongodb::bson::ser::Error) -> Self { 105 | Self::MongoDbError { 106 | message: source.to_string(), 107 | } 108 | } 109 | } 110 | 111 | impl From for CustomError { 112 | fn from(source: mongodb::bson::oid::Error) -> Self { 113 | Self::NotFound { 114 | message: source.to_string(), 115 | } 116 | } 117 | } 118 | 119 | impl From for CustomError { 120 | fn from(source: RedisError) -> Self { 121 | Self::RedisError { 122 | message: source.to_string(), 123 | } 124 | } 125 | } 126 | 127 | impl From for CustomError { 128 | fn from(_source: serde_json::Error) -> Self { 129 | Self::InternalError 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /mongodb-redis/src/handlers.rs: -------------------------------------------------------------------------------- 1 | use actix_web::http::header::{self, ContentType}; 2 | use actix_web::http::StatusCode; 3 | use actix_web::{web, HttpRequest, HttpResponse}; 4 | use prometheus::{Encoder, TextEncoder}; 5 | use serde::Deserialize; 6 | 7 | use crate::broadcaster::Broadcaster; 8 | use crate::dto::PlanetDto; 9 | use crate::errors::CustomError; 10 | use crate::model::PlanetType; 11 | use crate::services::{PlanetService, RateLimitingService}; 12 | use std::sync::Mutex; 13 | 14 | #[derive(Debug, Deserialize)] 15 | pub struct GetPlanetsQueryParams { 16 | r#type: Option, 17 | } 18 | 19 | pub async fn get_planets( 20 | req: HttpRequest, 21 | web::Query(query_params): web::Query, 22 | rate_limit_service: web::Data, 23 | planet_service: web::Data, 24 | ) -> Result { 25 | // can be moved to actix middleware 26 | rate_limit_service 27 | .assert_rate_limit_not_exceeded(get_ip_addr(&req)?) 28 | .await?; 29 | 30 | let planets = planet_service.get_planets(query_params.r#type).await?; 31 | Ok(HttpResponse::Ok().json(planets.into_iter().map(PlanetDto::from).collect::>())) 32 | } 33 | 34 | pub async fn create_planet( 35 | planet_dto: web::Json, 36 | planet_service: web::Data, 37 | ) -> Result { 38 | let planet = planet_service 39 | .create_planet(planet_dto.into_inner().into()) 40 | .await?; 41 | 42 | Ok(HttpResponse::Ok().json(PlanetDto::from(planet))) 43 | } 44 | 45 | pub async fn get_planet( 46 | planet_id: web::Path, 47 | planet_service: web::Data, 48 | ) -> Result { 49 | let planet = planet_service.get_planet(&planet_id.into_inner()).await?; 50 | Ok(HttpResponse::Ok().json(PlanetDto::from(planet))) 51 | } 52 | 53 | pub async fn update_planet( 54 | planet_id: web::Path, 55 | planet_dto: web::Json, 56 | planet_service: web::Data, 57 | ) -> Result { 58 | let planet = planet_service 59 | .update_planet(&planet_id.into_inner(), planet_dto.into_inner().into()) 60 | .await?; 61 | 62 | Ok(HttpResponse::Ok().json(PlanetDto::from(planet))) 63 | } 64 | 65 | pub async fn delete_planet( 66 | planet_id: web::Path, 67 | planet_service: web::Data, 68 | ) -> Result { 69 | planet_service 70 | .delete_planet(&planet_id.into_inner()) 71 | .await?; 72 | 73 | Ok(HttpResponse::Ok().finish()) 74 | } 75 | 76 | pub async fn get_image_of_planet( 77 | planet_id: web::Path, 78 | planet_service: web::Data, 79 | ) -> Result { 80 | let image = planet_service 81 | .get_image_of_planet(&planet_id.into_inner()) 82 | .await?; 83 | 84 | Ok(HttpResponse::Ok() 85 | .content_type(ContentType::png()) 86 | .body(image)) 87 | } 88 | 89 | pub async fn sse(broadcaster: web::Data>) -> Result { 90 | let rx = broadcaster 91 | .lock() 92 | .expect("Can't lock broadcaster") 93 | .new_client() 94 | .await; 95 | let response_stream = tokio_stream::wrappers::ReceiverStream::new(rx); 96 | 97 | Ok(HttpResponse::build(StatusCode::OK) 98 | .insert_header(header::ContentType(mime::TEXT_EVENT_STREAM)) 99 | .streaming(response_stream)) 100 | } 101 | 102 | pub async fn index() -> Result { 103 | let content = include_str!("index.html"); 104 | 105 | Ok(HttpResponse::Ok() 106 | .insert_header(header::ContentType(mime::TEXT_HTML)) 107 | .body(content)) 108 | } 109 | 110 | pub async fn metrics() -> Result { 111 | let encoder = TextEncoder::new(); 112 | let mut buffer = vec![]; 113 | encoder 114 | .encode(&prometheus::gather(), &mut buffer) 115 | .expect("Failed to encode metrics"); 116 | 117 | let response = String::from_utf8(buffer.clone()).expect("Failed to convert bytes to string"); 118 | buffer.clear(); 119 | 120 | Ok(HttpResponse::Ok() 121 | .insert_header(header::ContentType(mime::TEXT_PLAIN)) 122 | .body(response)) 123 | } 124 | 125 | fn get_ip_addr(req: &HttpRequest) -> Result { 126 | Ok(req 127 | .peer_addr() 128 | .ok_or(CustomError::InternalError)? 129 | .ip() 130 | .to_string()) 131 | } 132 | -------------------------------------------------------------------------------- /mongodb-redis/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Server-sent events 7 | 14 | 15 | 16 |
17 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /mongodb-redis/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | use actix_web::dev::Service; 4 | use actix_web::web::Data; 5 | use actix_web::{web, App, HttpServer}; 6 | use log::info; 7 | 8 | use crate::broadcaster::Broadcaster; 9 | use crate::db::MongoDbClient; 10 | use crate::services::{PlanetService, RateLimitingService}; 11 | use prometheus::HistogramTimer; 12 | 13 | mod broadcaster; 14 | mod db; 15 | mod dto; 16 | mod errors; 17 | mod handlers; 18 | mod metrics; 19 | mod model; 20 | mod redis; 21 | mod services; 22 | 23 | #[actix_web::main] 24 | async fn main() -> std::io::Result<()> { 25 | dotenv::from_filename(".env.local").ok(); 26 | env_logger::init(); 27 | 28 | info!("Starting MongoDB & Redis demo server"); 29 | 30 | let mongodb_uri = env::var("MONGODB_URI").expect("MONGODB_URI env var should be specified"); 31 | let mongodb_client = MongoDbClient::new(mongodb_uri).await; 32 | 33 | let redis_uri = env::var("REDIS_URI").expect("REDIS_URI env var should be specified"); 34 | let redis_client = redis::create_client(redis_uri) 35 | .await 36 | .expect("Can't create Redis client"); 37 | let redis_connection_manager = redis_client 38 | .get_tokio_connection_manager() 39 | .await 40 | .expect("Can't create Redis connection manager"); 41 | 42 | let broadcaster = Broadcaster::create(); 43 | 44 | redis::start_pubsub(&redis_client, broadcaster.clone()) 45 | .await 46 | .expect("Can't start Redis Pub/Sub"); 47 | 48 | let planet_service = Data::new(PlanetService::new( 49 | mongodb_client, 50 | redis_client, 51 | redis_connection_manager.clone(), 52 | )); 53 | 54 | let rate_limiting_service = Data::new(RateLimitingService::new(redis_connection_manager)); 55 | 56 | let enable_writing_handlers = env::var("ENABLE_WRITING_HANDLERS") 57 | .expect("ENABLE_WRITING_HANDLERS env var should be specified") 58 | .parse::() 59 | .expect("Can't parse ENABLE_WRITING_HANDLERS"); 60 | 61 | HttpServer::new(move || { 62 | let mut app = App::new() 63 | .wrap_fn(|req, srv| { 64 | let mut histogram_timer: Option = None; 65 | let request_path = req.path(); 66 | let is_registered_resource = req.resource_map().has_resource(request_path); 67 | // this check prevents possible DoS attacks that can be done by flooding the application 68 | // using requests to different unregistered paths. That can cause high memory consumption 69 | // of the application and Prometheus server and also overflow Prometheus's TSDB 70 | if is_registered_resource { 71 | let request_method = req.method().to_string(); 72 | histogram_timer = Some( 73 | metrics::HTTP_RESPONSE_TIME_SECONDS 74 | .with_label_values(&[&request_method, request_path]) 75 | .start_timer(), 76 | ); 77 | metrics::HTTP_REQUESTS_TOTAL 78 | .with_label_values(&[&request_method, request_path]) 79 | .inc(); 80 | } 81 | 82 | let fut = srv.call(req); 83 | 84 | async { 85 | let res = fut.await?; 86 | if let Some(histogram_timer) = histogram_timer { 87 | histogram_timer.observe_duration(); 88 | }; 89 | Ok(res) 90 | } 91 | }) 92 | .route("/planets", web::get().to(handlers::get_planets)) 93 | .route("/planets/{planet_id}", web::get().to(handlers::get_planet)) 94 | .route( 95 | "/planets/{planet_id}/image", 96 | web::get().to(handlers::get_image_of_planet), 97 | ) 98 | .route("/events", web::get().to(handlers::sse)) 99 | .route("/", web::get().to(handlers::index)) 100 | .route("/metrics", web::get().to(handlers::metrics)) 101 | .app_data(planet_service.clone()) 102 | .app_data(rate_limiting_service.clone()) 103 | .app_data(broadcaster.clone()); 104 | 105 | if enable_writing_handlers { 106 | app = app 107 | .route("/planets", web::post().to(handlers::create_planet)) 108 | .route( 109 | "/planets/{planet_id}", 110 | web::put().to(handlers::update_planet), 111 | ) 112 | .route( 113 | "/planets/{planet_id}", 114 | web::delete().to(handlers::delete_planet), 115 | ); 116 | } 117 | 118 | app 119 | }) 120 | .bind("0.0.0.0:9000")? 121 | .run() 122 | .await 123 | } 124 | -------------------------------------------------------------------------------- /mongodb-redis/src/metrics.rs: -------------------------------------------------------------------------------- 1 | use lazy_static::lazy_static; 2 | use prometheus::{opts, register_histogram_vec, register_int_counter_vec, register_int_gauge}; 3 | use prometheus::{HistogramVec, IntCounterVec, IntGauge}; 4 | 5 | const HTTP_RESPONSE_TIME_CUSTOM_BUCKETS: &[f64; 14] = &[ 6 | 0.0005, 0.0008, 0.00085, 0.0009, 0.00095, 0.001, 0.00105, 0.0011, 0.00115, 0.0012, 0.0015, 7 | 0.002, 0.003, 1.0, 8 | ]; 9 | 10 | lazy_static! { 11 | pub static ref HTTP_REQUESTS_TOTAL: IntCounterVec = register_int_counter_vec!( 12 | opts!("http_requests_total", "HTTP requests total"), 13 | &["method", "path"] 14 | ) 15 | .expect("Can't create a metric"); 16 | pub static ref HTTP_CONNECTED_SSE_CLIENTS: IntGauge = 17 | register_int_gauge!(opts!("http_connected_sse_clients", "Connected SSE clients")) 18 | .expect("Can't create a metric"); 19 | pub static ref HTTP_RESPONSE_TIME_SECONDS: HistogramVec = register_histogram_vec!( 20 | "http_response_time_seconds", 21 | "HTTP response times", 22 | &["method", "path"], 23 | HTTP_RESPONSE_TIME_CUSTOM_BUCKETS.to_vec() 24 | ) 25 | .expect("Can't create a metric"); 26 | } 27 | -------------------------------------------------------------------------------- /mongodb-redis/src/model.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | use crate::dto::{PlanetDto, SatelliteDto}; 4 | use chrono::Utc; 5 | use mongodb::bson::{self, doc, oid::ObjectId, Document}; 6 | use serde::{Deserialize, Serialize}; 7 | use std::fmt; 8 | 9 | #[derive(Serialize, Deserialize, Debug)] 10 | pub struct Planet { 11 | #[serde(rename = "_id", skip_serializing_if = "Option::is_none")] 12 | pub id: Option, 13 | pub name: String, 14 | pub r#type: PlanetType, 15 | pub mean_radius: f32, 16 | pub satellites: Option>, 17 | } 18 | 19 | #[derive(Copy, Clone, Eq, PartialEq, Serialize, Deserialize, Debug)] 20 | pub enum PlanetType { 21 | TerrestrialPlanet, 22 | GasGiant, 23 | IceGiant, 24 | DwarfPlanet, 25 | } 26 | 27 | #[derive(Serialize, Deserialize, Debug)] 28 | pub struct Satellite { 29 | pub name: String, 30 | pub first_spacecraft_landing_date: Option, 31 | } 32 | 33 | impl From<&Planet> for Document { 34 | fn from(source: &Planet) -> Self { 35 | bson::to_document(source).expect("Can't convert a planet to Document") 36 | } 37 | } 38 | 39 | impl From for Planet { 40 | fn from(source: PlanetDto) -> Self { 41 | Planet { 42 | id: source 43 | .id 44 | .map(|id| ObjectId::from_str(id.as_str()).expect("Can't convert to ObjectId")), 45 | name: source.name, 46 | r#type: source.r#type, 47 | mean_radius: source.mean_radius, 48 | satellites: source 49 | .satellites 50 | .map(|satellites| satellites.into_iter().map(Satellite::from).collect()), 51 | } 52 | } 53 | } 54 | 55 | impl From for Satellite { 56 | fn from(source: SatelliteDto) -> Self { 57 | Satellite { 58 | name: source.name, 59 | first_spacecraft_landing_date: source.first_spacecraft_landing_date.map(|d| { 60 | mongodb::bson::DateTime::from_millis( 61 | chrono::Date::::from_utc(d, Utc) 62 | .and_hms(0, 0, 0) 63 | .timestamp_millis(), 64 | ) 65 | }), 66 | } 67 | } 68 | } 69 | 70 | impl fmt::Display for PlanetType { 71 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 72 | write!(f, "{:?}", self) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /mongodb-redis/src/redis.rs: -------------------------------------------------------------------------------- 1 | use actix_web::web::{Bytes, Data}; 2 | use redis::{Client, FromRedisValue, RedisError}; 3 | use redis::{RedisWrite, ToRedisArgs}; 4 | use tokio_stream::StreamExt; 5 | 6 | use crate::broadcaster::Broadcaster; 7 | use crate::errors::CustomError; 8 | use crate::model::Planet; 9 | use crate::services::NEW_PLANETS_CHANNEL_NAME; 10 | use std::sync::Mutex; 11 | 12 | pub async fn create_client(redis_uri: String) -> Result { 13 | Ok(Client::open(redis_uri)?) 14 | } 15 | 16 | pub async fn start_pubsub( 17 | redis_client: &Client, 18 | broadcaster: Data>, 19 | ) -> Result<(), CustomError> { 20 | let mut pubsub_con = redis_client.get_async_connection().await?.into_pubsub(); 21 | pubsub_con.subscribe(NEW_PLANETS_CHANNEL_NAME).await?; 22 | 23 | tokio::spawn(async move { 24 | while let Some(msg) = pubsub_con.on_message().next().await { 25 | let payload = msg.get_payload().expect("Can't get payload of message"); 26 | let payload: String = 27 | FromRedisValue::from_redis_value(&payload).expect("Can't convert from Redis value"); 28 | let msg = Bytes::from(format!("data: Planet created: {:?}\n\n", payload)); 29 | broadcaster 30 | .lock() 31 | .expect("Can't lock broadcaster") 32 | .send(msg); 33 | } 34 | }); 35 | 36 | Ok(()) 37 | } 38 | 39 | impl ToRedisArgs for &Planet { 40 | fn write_redis_args(&self, out: &mut W) 41 | where 42 | W: ?Sized + RedisWrite, 43 | { 44 | out.write_arg_fmt(serde_json::to_string(self).expect("Can't serialize Planet as string")) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /mongodb-redis/src/services.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | use chrono::{Timelike, Utc}; 4 | use log::debug; 5 | use mongodb::bson::oid::ObjectId; 6 | use redis::aio::ConnectionManager; 7 | use redis::{AsyncCommands, Client, Value}; 8 | 9 | use crate::db::MongoDbClient; 10 | use crate::dto::PlanetMessage; 11 | use crate::errors::CustomError; 12 | use crate::errors::CustomError::{RedisError, TooManyRequests}; 13 | use crate::model::{Planet, PlanetType}; 14 | 15 | const PLANET_KEY_PREFIX: &str = "planet"; 16 | const IMAGE_KEY_PREFIX: &str = "image"; 17 | const RATE_LIMIT_KEY_PREFIX: &str = "rate_limit"; 18 | const MAX_REQUESTS_PER_MINUTE: u64 = 10; 19 | pub const NEW_PLANETS_CHANNEL_NAME: &str = "new_planets"; 20 | 21 | #[derive(Clone)] 22 | pub struct PlanetService { 23 | mongodb_client: MongoDbClient, 24 | redis_client: Client, 25 | redis_connection_manager: ConnectionManager, 26 | } 27 | 28 | impl PlanetService { 29 | pub fn new( 30 | mongodb_client: MongoDbClient, 31 | redis_client: Client, 32 | redis_connection_manager: ConnectionManager, 33 | ) -> Self { 34 | PlanetService { 35 | mongodb_client, 36 | redis_client, 37 | redis_connection_manager, 38 | } 39 | } 40 | 41 | pub async fn get_planets( 42 | &self, 43 | planet_type: Option, 44 | ) -> Result, CustomError> { 45 | self.mongodb_client.get_planets(planet_type).await 46 | } 47 | 48 | pub async fn create_planet(&self, planet: Planet) -> Result { 49 | let planet = self.mongodb_client.create_planet(planet).await?; 50 | self.redis_connection_manager 51 | .clone() 52 | .publish( 53 | NEW_PLANETS_CHANNEL_NAME, 54 | serde_json::to_string(&PlanetMessage::from(&planet))?, 55 | ) 56 | .await?; 57 | Ok(planet) 58 | } 59 | 60 | pub async fn get_planet(&self, planet_id: &str) -> Result { 61 | let cache_key = self.get_planet_cache_key(planet_id); 62 | let mut con = self.redis_client.get_async_connection().await?; 63 | 64 | let cached_planet = con.get(&cache_key).await?; 65 | match cached_planet { 66 | Value::Nil => { 67 | debug!("Use database to retrieve a planet by id: {}", &planet_id); 68 | let result: Planet = self 69 | .mongodb_client 70 | .get_planet(ObjectId::from_str(planet_id)?) 71 | .await?; 72 | 73 | let _: () = redis::pipe() 74 | .atomic() 75 | .set(&cache_key, &result) 76 | .expire(&cache_key, 60) 77 | .query_async(&mut con) 78 | .await?; 79 | 80 | Ok(result) 81 | } 82 | Value::Data(val) => { 83 | debug!("Use cache to retrieve a planet by id: {}", planet_id); 84 | Ok(serde_json::from_slice(&val)?) 85 | } 86 | _ => Err(RedisError { 87 | message: "Unexpected response from Redis".to_string(), 88 | }), 89 | } 90 | } 91 | 92 | pub async fn update_planet( 93 | &self, 94 | planet_id: &str, 95 | planet: Planet, 96 | ) -> Result { 97 | let updated_planet = self 98 | .mongodb_client 99 | .update_planet(ObjectId::from_str(planet_id)?, planet) 100 | .await?; 101 | 102 | let cache_key = self.get_planet_cache_key(planet_id); 103 | self.redis_connection_manager.clone().del(cache_key).await?; 104 | 105 | Ok(updated_planet) 106 | } 107 | 108 | pub async fn delete_planet(&self, planet_id: &str) -> Result<(), CustomError> { 109 | self.mongodb_client 110 | .delete_planet(ObjectId::from_str(planet_id)?) 111 | .await?; 112 | 113 | let cache_key = self.get_planet_cache_key(planet_id); 114 | self.redis_connection_manager.clone().del(cache_key).await?; 115 | 116 | Ok(()) 117 | } 118 | 119 | pub async fn get_image_of_planet(&self, planet_id: &str) -> Result, CustomError> { 120 | let cache_key = self.get_image_cache_key(planet_id); 121 | let mut redis_connection_manager = self.redis_connection_manager.clone(); 122 | 123 | let cached_image = redis_connection_manager.get(&cache_key).await?; 124 | match cached_image { 125 | Value::Nil => { 126 | debug!( 127 | "Use database to retrieve an image of a planet by id: {}", 128 | &planet_id 129 | ); 130 | let planet = self 131 | .mongodb_client 132 | .get_planet(ObjectId::from_str(planet_id)?) 133 | .await?; 134 | let result = crate::db::get_image_of_planet(&planet.name).await; 135 | 136 | let _: () = redis::pipe() 137 | .atomic() 138 | .set(&cache_key, result.clone()) 139 | .expire(&cache_key, 60) 140 | .query_async(&mut redis_connection_manager) 141 | .await?; 142 | 143 | Ok(result) 144 | } 145 | Value::Data(val) => { 146 | debug!( 147 | "Use cache to retrieve an image of a planet by id: {}", 148 | &planet_id 149 | ); 150 | Ok(val) 151 | } 152 | _ => Err(RedisError { 153 | message: "Unexpected response from Redis".to_string(), 154 | }), 155 | } 156 | } 157 | 158 | fn get_planet_cache_key(&self, planet_id: &str) -> String { 159 | format!("{}:{}", PLANET_KEY_PREFIX, planet_id) 160 | } 161 | 162 | fn get_image_cache_key(&self, planet_id: &str) -> String { 163 | format!("{}:{}:{}", PLANET_KEY_PREFIX, planet_id, IMAGE_KEY_PREFIX) 164 | } 165 | } 166 | 167 | #[derive(Clone)] 168 | pub struct RateLimitingService { 169 | redis_connection_manager: ConnectionManager, 170 | } 171 | 172 | impl RateLimitingService { 173 | pub fn new(redis_connection_manager: ConnectionManager) -> Self { 174 | RateLimitingService { 175 | redis_connection_manager, 176 | } 177 | } 178 | 179 | pub async fn assert_rate_limit_not_exceeded(&self, ip_addr: String) -> Result<(), CustomError> { 180 | let current_minute = Utc::now().minute(); 181 | let rate_limit_key = format!("{}:{}:{}", RATE_LIMIT_KEY_PREFIX, ip_addr, current_minute); 182 | 183 | let (count, _): (u64, u64) = redis::pipe() 184 | .atomic() 185 | .incr(&rate_limit_key, 1) 186 | .expire(&rate_limit_key, 60) 187 | .query_async(&mut self.redis_connection_manager.clone()) 188 | .await?; 189 | 190 | if count > MAX_REQUESTS_PER_MINUTE { 191 | Err(TooManyRequests { 192 | actual_count: count, 193 | permitted_count: MAX_REQUESTS_PER_MINUTE, 194 | }) 195 | } else { 196 | Ok(()) 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /readme.adoc: -------------------------------------------------------------------------------- 1 | = Exploring Rust ecosystem 2 | 3 | A set of examples that demonstrate how to create a backend application using Rust ecosystem: 4 | 5 | * https://github.com/rkudryashov/exploring-rust-ecosystem/tree/master/grpc-telegram-bot[gRPC + Telegram bot] 6 | + 7 | image:https://github.com/rkudryashov/exploring-rust-ecosystem/actions/workflows/grpc-telegram-bot.yml/badge.svg[CI/CD,link=https://github.com/rkudryashov/exploring-rust-ecosystem/actions] 8 | * https://github.com/rkudryashov/exploring-rust-ecosystem/tree/master/mongodb-redis[MongoDB + Redis] 9 | + 10 | image:https://github.com/rkudryashov/exploring-rust-ecosystem/actions/workflows/mongodb-redis.yml/badge.svg[CI/CD,link=https://github.com/rkudryashov/exploring-rust-ecosystem/actions] 11 | --------------------------------------------------------------------------------