From 6bbd238d25bb1756f8b3a6523e9c4cf389481f68 Mon Sep 17 00:00:00 2001 From: Sylvia Ji Date: Tue, 14 Apr 2020 00:53:45 +0000 Subject: [PATCH 01/20] Translated using Weblate (Chinese (Simplified)) Currently translated at 69.1% (164 of 237 strings) Translation: Lemmy/lemmy Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/zh_Hans/ --- ui/translations/zh.json | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/ui/translations/zh.json b/ui/translations/zh.json index 6cf8a6698..6eb6f201c 100644 --- a/ui/translations/zh.json +++ b/ui/translations/zh.json @@ -110,22 +110,19 @@ "setup_admin": "设置管理员", "your_site": "你的站点", "modified": "修改", - "sponsors": "发起人", - "sponsors_of_lemmy": "Lemmy 的发起人", - "sponsor_message": - "Lemmy is free, <1>open-source software, meaning no advertising, monetizing, or venture capital, ever. Your donations directly support full-time development of the project. Thank you to the following people:", - "support_on_patreon": "在 Patreon 赞助", - "support_on_liberapay": "在 on 赞助", - "general_sponsors": - "General Sponsors are those that pledged $10 to $39 to Lemmy.", - "crypto": "加密", + "sponsors": "赞助者", + "sponsors_of_lemmy": "Lemmy 的赞助者", + "sponsor_message": "Lemmy 是自由的<1>开源 软件, 也就是说,决不会有广告、盈利行为和风险投资介入。您的捐款将被直接用于支持项目的开发。感谢以下诸位:", + "support_on_patreon": "在 Patreon 支持", + "support_on_liberapay": "在 Liberapay支持", + "general_sponsors": "General Sponsors are those that pledged $10 to $39 to Lemmy.", + "crypto": "加密货币", "bitcoin": "比特币", "ethereum": "以太币", "code": "代码", "joined": "已加入", "powered_by": "保留所有权利", - "landing_0": - "Lemmy is a <1>link aggregator / reddit alternative, intended to work in the <2>fediverse.<3>It's self-hostable, has live-updating comment threads, and is tiny (<4>~80kB). Federation into the ActivityPub network is on the roadmap. <5>This is a <6>very early beta version, and a lot of features are currently broken or missing. <7>Suggest new features or report bugs <8>here.<9>Made with <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.", + "landing_0": "Lemmy is a <1>link aggregator / reddit alternative, intended to work in the <2>fediverse.<3>It's self-hostable, has live-updating comment threads, and is tiny (<4>~80kB). Federation into the ActivityPub network is on the roadmap. <5>This is a <6>very early beta version, and a lot of features are currently broken or missing. <7>Suggest new features or report bugs <8>here.<9>Made with <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.", "not_logged_in": "未登录.", "community_ban": "你被此节点禁止.", "site_ban": "你被此站点禁止", @@ -158,5 +155,17 @@ "admin_already_created": "抱歉,管理员已存在.", "user_already_exists": "用户已存在.", "couldnt_update_user": "不可以更新用户.", - "system_err_login": "系统错误. 尝试注销再登录" + "system_err_login": "系统错误. 尝试注销再登录", + "nsfw": "工作场所不宜", + "show_nsfw": "显示工作场所不宜内容", + "theme": "主题", + "from": "来自", + "donate_to_lemmy": "向Lemmy捐赠", + "donate": "捐赠", + "monero": "门罗币", + "to": "致", + "are_you_sure": "你确定吗?", + "yes": "是", + "no": "否", + "logged_in": "已登陆" } From abbce23b189e180b7f3dcb2614cd7efd79b2f821 Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Tue, 14 Apr 2020 00:53:45 +0000 Subject: [PATCH 02/20] Translated using Weblate (Japanese) Currently translated at 85.6% (203 of 237 strings) Translation: Lemmy/lemmy Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/ja/ --- ui/translations/ja.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/translations/ja.json b/ui/translations/ja.json index d2e48a4f6..4cb9a5d9f 100644 --- a/ui/translations/ja.json +++ b/ui/translations/ja.json @@ -200,5 +200,6 @@ "sorting_help": "並び順のヘルプ", "block_leaving": "このページから離れてもよろしいですか?", "number_of_upvotes": "{{count}} 票の賛成", - "number_of_downvotes": "{{count}} 票の反対" + "number_of_downvotes": "{{count}} 票の反対", + "show_context": "前後を表示" } From be6a7876b49e8f963506f0b05e12495f119afc10 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Tue, 14 Apr 2020 19:38:20 -0400 Subject: [PATCH 03/20] Version v0.6.50 --- ansible/VERSION | 2 +- docker/prod/docker-compose.yml | 2 +- server/src/version.rs | 2 +- ui/src/version.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ansible/VERSION b/ansible/VERSION index 25690dbe8..2102553df 100644 --- a/ansible/VERSION +++ b/ansible/VERSION @@ -1 +1 @@ -v0.6.49 +v0.6.50 diff --git a/docker/prod/docker-compose.yml b/docker/prod/docker-compose.yml index 905514653..23f0583a5 100644 --- a/docker/prod/docker-compose.yml +++ b/docker/prod/docker-compose.yml @@ -12,7 +12,7 @@ services: restart: always lemmy: - image: dessalines/lemmy:v0.6.49 + image: dessalines/lemmy:v0.6.50 ports: - "127.0.0.1:8536:8536" restart: always diff --git a/server/src/version.rs b/server/src/version.rs index f02e9ae5f..2fb90238c 100644 --- a/server/src/version.rs +++ b/server/src/version.rs @@ -1 +1 @@ -pub const VERSION: &str = "v0.6.49"; +pub const VERSION: &str = "v0.6.50"; diff --git a/ui/src/version.ts b/ui/src/version.ts index 8a3563532..c3abebdeb 100644 --- a/ui/src/version.ts +++ b/ui/src/version.ts @@ -1 +1 @@ -export const version: string = 'v0.6.49'; +export const version: string = 'v0.6.50'; From 09836bffef81df8e703df994d868cb6509be9bb8 Mon Sep 17 00:00:00 2001 From: charleshthomasiii Date: Wed, 15 Apr 2020 13:52:46 -0400 Subject: [PATCH 04/20] Add apache benchmark installation check --- server/query_testing/apache_bench_report.sh | 6 ++++++ server/query_testing/api_benchmark.sh | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/server/query_testing/apache_bench_report.sh b/server/query_testing/apache_bench_report.sh index 62b3e8636..06ceb4843 100755 --- a/server/query_testing/apache_bench_report.sh +++ b/server/query_testing/apache_bench_report.sh @@ -11,6 +11,12 @@ declare -a arr=( "https://torrents-csv.ml/service/search?q=wheel&page=1&type_=torrent" ) +## check if ab installed +if ! [ -x "$(command -v ab)" ]; then + echo 'Error: ab (Apache Bench) is not installed. https://httpd.apache.org/docs/2.4/programs/ab.html' >&2 + exit 1 +fi + ## now loop through the above array for i in "${arr[@]}" do diff --git a/server/query_testing/api_benchmark.sh b/server/query_testing/api_benchmark.sh index 8f8c65f1c..9f06580a7 100755 --- a/server/query_testing/api_benchmark.sh +++ b/server/query_testing/api_benchmark.sh @@ -15,6 +15,12 @@ declare -a arr=( "/api/v1/post/list?sort=Hot&type_=All" ) +## check if ab installed +if ! [ -x "$(command -v ab)" ]; then + echo 'Error: ab (Apache Bench) is not installed. https://httpd.apache.org/docs/2.4/programs/ab.html' >&2 + exit 1 +fi + ## now loop through the above array for path in "${arr[@]}" do From f300c67a4d9674eef05d180a787cc8352092903d Mon Sep 17 00:00:00 2001 From: Dessalines Date: Sun, 19 Apr 2020 18:08:25 -0400 Subject: [PATCH 05/20] Adding websocket notification system. - HTTP and APUB clients can now send live updating messages to websocket clients - Rate limiting now affects both HTTP and websockets - Rate limiting / Websocket logic is now moved into the API Perform functions. - TODO This broke getting current online users, but that will have to wait for the perform trait to be made async. - Fixes #446 --- server/Cargo.lock | 776 ++++++++--------- server/src/api/comment.rs | 153 +++- server/src/api/community.rs | 231 ++++- server/src/api/mod.rs | 25 +- server/src/api/post.rs | 176 +++- server/src/api/site.rs | 187 +++- server/src/api/user.rs | 325 ++++++- server/src/lib.rs | 22 +- server/src/main.rs | 20 +- server/src/rate_limit/mod.rs | 18 + server/src/rate_limit/rate_limiter.rs | 131 +++ server/src/routes/api.rs | 53 +- server/src/routes/federation.rs | 2 +- server/src/routes/feeds.rs | 10 - server/src/routes/index.rs | 4 +- server/src/routes/mod.rs | 29 + server/src/routes/nodeinfo.rs | 10 +- server/src/routes/webfinger.rs | 10 +- server/src/routes/websocket.rs | 18 +- server/src/websocket/mod.rs | 23 +- server/src/websocket/server.rs | 1151 ++++++++++++++----------- 21 files changed, 2309 insertions(+), 1065 deletions(-) create mode 100644 server/src/rate_limit/mod.rs create mode 100644 server/src/rate_limit/rate_limiter.rs diff --git a/server/Cargo.lock b/server/Cargo.lock index 2a3bc0330..a33211ddc 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -8,9 +8,9 @@ dependencies = [ "activitystreams-derive 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "activitystreams-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "activitystreams-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -18,9 +18,9 @@ name = "activitystreams-derive" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -28,9 +28,9 @@ name = "activitystreams-traits" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -42,9 +42,9 @@ dependencies = [ "activitystreams-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -53,19 +53,19 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "actix-http 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "actix-rt 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "actix-rt 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "actix_derive 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-channel 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "derive_more 0.99.3 (registry+https://github.com/rust-lang/crates.io-index)", + "derive_more 0.99.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "trust-dns-proto 0.18.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)", "trust-dns-resolver 0.18.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -81,7 +81,7 @@ dependencies = [ "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -91,13 +91,13 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "actix-codec 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "actix-rt 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "actix-rt 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "actix-service 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "actix-utils 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "derive_more 0.99.3 (registry+https://github.com/rust-lang/crates.io-index)", + "derive_more 0.99.5 (registry+https://github.com/rust-lang/crates.io-index)", "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "trust-dns-proto 0.18.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)", "trust-dns-resolver 0.18.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -113,7 +113,7 @@ dependencies = [ "actix-web 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "derive_more 0.99.3 (registry+https://github.com/rust-lang/crates.io-index)", + "derive_more 0.99.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", @@ -129,7 +129,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "actix-codec 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "actix-connect 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "actix-rt 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "actix-rt 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "actix-service 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "actix-threadpool 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "actix-utils 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -139,17 +139,17 @@ dependencies = [ "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "copyless 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "derive_more 0.99.3 (registry+https://github.com/rust-lang/crates.io-index)", + "derive_more 0.99.5 (registry+https://github.com/rust-lang/crates.io-index)", "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "encoding_rs 0.8.22 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "flate2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "flate2 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", "futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "h2 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "h2 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -157,11 +157,11 @@ dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)", "serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -174,7 +174,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -182,23 +182,24 @@ name = "actix-router" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytestring 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bytestring 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "actix-rt" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "actix-macros 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "actix-threadpool 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "copyless 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -207,7 +208,7 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "actix-codec 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "actix-rt 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "actix-rt 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "actix-service 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "actix-utils 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -215,7 +216,7 @@ dependencies = [ "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -225,7 +226,7 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -234,7 +235,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "actix-macros 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "actix-rt 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "actix-rt 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "actix-server 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "actix-service 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -247,12 +248,12 @@ name = "actix-threadpool" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "derive_more 0.99.3 (registry+https://github.com/rust-lang/crates.io-index)", + "derive_more 0.99.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -262,10 +263,10 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "actix-codec 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "actix-rt 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "actix-rt 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "actix-service 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "actix-utils 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "derive_more 0.99.3 (registry+https://github.com/rust-lang/crates.io-index)", + "derive_more 0.99.5 (registry+https://github.com/rust-lang/crates.io-index)", "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -277,14 +278,14 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "actix-codec 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "actix-rt 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "actix-rt 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "actix-service 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -297,7 +298,7 @@ dependencies = [ "actix-http 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "actix-macros 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "actix-router 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "actix-rt 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "actix-rt 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "actix-server 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "actix-service 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "actix-testing 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -307,17 +308,17 @@ dependencies = [ "actix-web-codegen 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "awc 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "derive_more 0.99.3 (registry+https://github.com/rust-lang/crates.io-index)", + "derive_more 0.99.5 (registry+https://github.com/rust-lang/crates.io-index)", "encoding_rs 0.8.22 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)", "serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -334,7 +335,7 @@ dependencies = [ "actix-web 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -342,9 +343,9 @@ name = "actix-web-codegen" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -352,9 +353,9 @@ name = "actix_derive" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -372,7 +373,7 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -388,7 +389,7 @@ dependencies = [ [[package]] name = "arc-swap" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -406,12 +407,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "async-trait" -version = "0.1.24" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -419,8 +420,8 @@ name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "hermit-abi 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "hermit-abi 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -441,48 +442,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "actix-codec 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "actix-http 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "actix-rt 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "actix-rt 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "actix-service 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "derive_more 0.99.3 (registry+https://github.com/rust-lang/crates.io-index)", + "derive_more 0.99.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)", "serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "backtrace" -version = "0.3.45" +version = "0.3.46" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace-sys 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace-sys 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "backtrace-sys" -version = "0.1.33" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "base64" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "safemem 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -505,7 +497,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bcrypt" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -563,7 +555,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -572,7 +564,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "brotli-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -582,7 +574,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bumpalo" -version = "3.2.0" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -602,20 +594,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bytestring" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "c2-chacha" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "cc" version = "1.0.50" @@ -633,7 +617,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -669,7 +653,7 @@ dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "pest 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "twoway 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "typed-arena 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicode_categories 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -683,9 +667,9 @@ dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "nom 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rust-ini 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", "serde-hjson 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -701,7 +685,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -738,28 +722,28 @@ dependencies = [ [[package]] name = "curl" -version = "0.4.26" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "curl-sys 0.4.28+curl-7.69.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "curl-sys 0.4.30+curl-7.69.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.54 (registry+https://github.com/rust-lang/crates.io-index)", - "schannel 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.55 (registry+https://github.com/rust-lang/crates.io-index)", + "schannel 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "socket2 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "curl-sys" -version = "0.4.28+curl-7.69.0" +version = "0.4.30+curl-7.69.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", "libnghttp2-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.54 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.55 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -781,10 +765,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -794,7 +778,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "darling_core 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -804,9 +788,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "darling 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "derive_builder_core 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -815,24 +799,24 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "darling 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "derive_more" -version = "0.99.3" +version = "0.99.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "diesel" -version = "1.4.3" +version = "1.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -848,9 +832,9 @@ name = "diesel_derives" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -858,8 +842,8 @@ name = "diesel_migrations" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "migrations_internals 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "migrations_macros 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "migrations_internals 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "migrations_macros 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -903,16 +887,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "email" -version = "0.0.20" +version = "0.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -991,9 +974,9 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1004,7 +987,7 @@ dependencies = [ "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", "humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1013,7 +996,7 @@ name = "failure" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.46 (registry+https://github.com/rust-lang/crates.io-index)", "failure_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1022,9 +1005,9 @@ name = "failure_derive" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1043,12 +1026,12 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", "miniz_oxide 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1137,10 +1120,10 @@ name = "futures-macro" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.15 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1166,8 +1149,8 @@ dependencies = [ "futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.15 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-nested 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1184,7 +1167,7 @@ name = "generic-array" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1193,13 +1176,13 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", "wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "h2" -version = "0.2.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1207,12 +1190,12 @@ dependencies = [ "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1225,10 +1208,10 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.8" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1247,7 +1230,7 @@ name = "hostname" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", "winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1256,7 +1239,7 @@ name = "hostname" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", "match_cfg 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1268,7 +1251,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "http" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1317,7 +1300,7 @@ name = "iovec" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1325,7 +1308,7 @@ name = "ipconfig" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", + "socket2 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", "widestring 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1339,13 +1322,13 @@ dependencies = [ "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-channel 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "curl 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", - "curl-sys 0.4.28+curl-7.69.0 (registry+https://github.com/rust-lang/crates.io-index)", + "curl 0.4.28 (registry+https://github.com/rust-lang/crates.io-index)", + "curl-sys 0.4.30+curl-7.69.1 (registry+https://github.com/rust-lang/crates.io-index)", "encoding_rs 0.8.22 (registry+https://github.com/rust-lang/crates.io-index)", "futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "futures-io 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1365,10 +1348,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "js-sys" -version = "0.3.36" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "wasm-bindgen 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1378,9 +1361,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "pem 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.16.11 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "ring 0.16.12 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)", "simple_asn1 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1415,14 +1398,14 @@ dependencies = [ "activitypub 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "actix 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "actix-files 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "actix-rt 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "actix-rt 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "actix-web 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "actix-web-actors 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bcrypt 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bcrypt 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "comrak 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "config 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "diesel 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "diesel 1.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "diesel_migrations 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "dotenv 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1432,15 +1415,15 @@ dependencies = [ "isahc 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "jsonwebtoken 7.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lettre 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lettre_email 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lettre 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lettre_email 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "rss 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "strum 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", "strum_macros 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1448,7 +1431,7 @@ dependencies = [ [[package]] name = "lettre" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1458,19 +1441,19 @@ dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "lettre_email" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "email 0.0.20 (registry+https://github.com/rust-lang/crates.io-index)", - "lettre 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "email 0.0.21 (registry+https://github.com/rust-lang/crates.io-index)", + "lettre 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1484,13 +1467,13 @@ dependencies = [ "arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "static_assertions 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "libc" -version = "0.2.67" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1499,7 +1482,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1508,7 +1491,7 @@ version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1529,7 +1512,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "lock_api" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1576,7 +1559,7 @@ name = "memchr" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1586,21 +1569,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "migrations_internals" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "diesel 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "diesel 1.4.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "migrations_macros" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "migrations_internals 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "migrations_internals 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1635,7 +1618,7 @@ dependencies = [ "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1649,7 +1632,7 @@ version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1670,14 +1653,14 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl 0.10.28 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.10.29 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.54 (registry+https://github.com/rust-lang/crates.io-index)", - "schannel 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework-sys 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.55 (registry+https://github.com/rust-lang/crates.io-index)", + "schannel 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1687,7 +1670,7 @@ version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1752,11 +1735,11 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "hermit-abi 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "hermit-abi 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1766,15 +1749,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "openssl" -version = "0.10.28" +version = "0.10.29" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.54 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.55 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1784,35 +1767,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "openssl-sys" -version = "0.9.54" +version = "0.9.55" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "parking_lot" -version = "0.10.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lock_api 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "parking_lot_core" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1823,7 +1806,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1855,9 +1838,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "pest 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "pest_meta 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1872,20 +1855,20 @@ dependencies = [ [[package]] name = "pin-project" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "pin-project-internal 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project-internal 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "pin-project-internal" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1918,22 +1901,17 @@ dependencies = [ [[package]] name = "proc-macro-hack" -version = "0.5.11" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "proc-macro-nested" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "proc-macro2" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1958,7 +1936,7 @@ name = "quote" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1967,20 +1945,8 @@ version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "scheduled-thread-pool 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", + "scheduled-thread-pool 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1989,7 +1955,7 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2007,8 +1973,8 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2024,10 +1990,10 @@ dependencies = [ [[package]] name = "rand_chacha" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2081,7 +2047,7 @@ name = "rand_jitter" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2093,7 +2059,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2143,10 +2109,10 @@ dependencies = [ [[package]] name = "regex" -version = "1.3.5" +version = "1.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "aho-corasick 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "regex-syntax 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2181,15 +2147,15 @@ dependencies = [ [[package]] name = "ring" -version = "0.16.11" +version = "0.16.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "untrusted 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "web-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)", + "web-sys 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2227,17 +2193,12 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "safemem" -version = "0.3.3" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "schannel" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2246,10 +2207,10 @@ dependencies = [ [[package]] name = "scheduled-thread-pool" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2259,22 +2220,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "security-framework" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework-sys 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "security-framework-sys" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2297,10 +2259,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.105" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde_derive 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2311,7 +2273,7 @@ dependencies = [ "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2323,18 +2285,18 @@ dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive" -version = "1.0.105" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2350,13 +2312,13 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.48" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2374,7 +2336,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2410,8 +2372,8 @@ name = "signal-hook-registry" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "arc-swap 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "arc-swap 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2442,16 +2404,16 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "socket2" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2492,17 +2454,17 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "syn" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2512,9 +2474,9 @@ name = "synstructure" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2524,7 +2486,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2536,7 +2498,7 @@ name = "termcolor" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2549,20 +2511,20 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.11" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "thiserror-impl 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror-impl 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "thiserror-impl" -version = "1.0.11" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2571,7 +2533,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2595,7 +2557,7 @@ name = "threadpool" version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2603,14 +2565,14 @@ name = "time" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio" -version = "0.2.13" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2618,7 +2580,7 @@ dependencies = [ "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2638,7 +2600,20 @@ dependencies = [ "futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "pin-project-lite 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-util" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project-lite 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2646,7 +2621,7 @@ name = "toml" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2654,7 +2629,7 @@ name = "trust-dns-proto" version = "0.18.0-alpha.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "async-trait 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", + "async-trait 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)", "enum-as-inner 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2662,9 +2637,9 @@ dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "socket2 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2681,8 +2656,8 @@ dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "resolv-conf 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", "trust-dns-proto 0.18.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2702,7 +2677,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "typenum" -version = "1.11.2" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -2736,7 +2711,7 @@ name = "unicode-normalization" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2801,9 +2776,9 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2842,60 +2817,60 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "wasm-bindgen" -version = "0.2.59" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-macro 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-macro 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.59" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bumpalo 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bumpalo 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-shared 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-shared 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.59" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-macro-support 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-macro-support 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.59" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-backend 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-shared 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-backend 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-shared 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.59" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "web-sys" -version = "0.3.36" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "js-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2929,7 +2904,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "winapi-util" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2985,7 +2960,7 @@ dependencies = [ "checksum actix-http 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c16664cc4fdea8030837ad5a845eb231fb93fc3c5c171edfefb52fad92ce9019" "checksum actix-macros 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "21705adc76bbe4bc98434890e73a89cd00c6015e5704a60bb6eea6c3b72316b6" "checksum actix-router 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9d7a10ca4d94e8c8e7a87c5173aba1b97ba9a6563ca02b0e1cd23531093d3ec8" -"checksum actix-rt 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6a0a55507046441a496b2f0d26a84a65e67c8cafffe279072412f624b5fb6d" +"checksum actix-rt 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "20066d9200ef8d441ac156c76dd36c3f1e9a15976c34e69ae97f7f570b331882" "checksum actix-server 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "582a7173c281a4f46b5aa168a11e7f37183dcb71177a39312cc2264da7a632c9" "checksum actix-service 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d3e4fc95dfa7e24171b2d0bb46b85f8ab0e8499e4e3caec691fc4ea65c287564" "checksum actix-testing 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "48494745b72d0ea8ff0cf874aaf9b622a3ee03d7081ee0c04edea4f26d32c911" @@ -2998,23 +2973,22 @@ dependencies = [ "checksum actix_derive 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b95aceadaf327f18f0df5962fedc1bde2f870566a0b9f65c89508a3b1f79334c" "checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" "checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66" -"checksum aho-corasick 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)" = "d5e63fd144e18ba274ae7095c0197a870a7b9468abc801dd62f190d80817d2ec" +"checksum aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -"checksum arc-swap 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d7b8a9123b8027467bce0099fe556c628a53c8d83df0507084c31e9ba2e39aff" +"checksum arc-swap 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d663a8e9a99154b5fb793032533f6328da35e23aac63d5c152279aa8ba356825" "checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" "checksum ascii_utils 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "71938f30533e4d95a6d17aa530939da3842c2ab6f4f84b9dae68447e4129f74a" -"checksum async-trait 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "750b1c38a1dfadd108da0f01c08f4cdc7ff1bb39b325f9c82cc972361780a6e1" +"checksum async-trait 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)" = "da71fef07bc806586090247e971229289f64c210a278ee5ae419314eb386b31d" "checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" "checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" "checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" "checksum awc 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d7601d4d1d7ef2335d6597a41b5fe069f6ab799b85f53565ab390e7b7065aac5" -"checksum backtrace 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)" = "ad235dabf00f36301792cfe82499880ba54c6486be094d1047b02bacb67c14e8" -"checksum backtrace-sys 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)" = "e17b52e737c40a7d75abca20b29a19a0eb7ba9fc72c5a72dd282a0a3c2c0dc35" +"checksum backtrace 0.3.46 (registry+https://github.com/rust-lang/crates.io-index)" = "b1e692897359247cc6bb902933361652380af0f1b7651ae5c5013407f30e109e" +"checksum backtrace-sys 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "7de8aba10a69c8e8d7622c5710229485ec32e9d55fdad160ea559c086fdcd118" "checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" "checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" "checksum base64 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d5ca2cd0adc3f48f9e9ea5a6bbdf9ccc0bfade884847e484d452414c7ccffb3" -"checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" -"checksum bcrypt 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c158d9cf14495dbd2ac5ce1f31d75b3253f75c54cc865dd7e5fc864eb5fb8633" +"checksum bcrypt 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f055c8591efe08e03f534ee632672ea3643c3932e485f422107ec6cc1157b0ea" "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" "checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" "checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" @@ -3023,12 +2997,11 @@ dependencies = [ "checksum brotli-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4445dea95f4c2b41cde57cc9fee236ae4dbae88d8fcbdb4750fc1bb5d86aaecd" "checksum brotli2 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0cb036c3eade309815c15ddbacec5b22c4d1f3983a774ab2eac2e3e9ea85568e" "checksum bufstream 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "40e38929add23cdf8a366df9b0e088953150724bcbe5fc330b0d8eb3b328eec8" -"checksum bumpalo 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1f359dc14ff8911330a51ef78022d376f25ed00248912803b58f00cb1c27f742" +"checksum bumpalo 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "12ae9db68ad7fac5fe51304d20f016c911539251075a214f8e663babefa35187" "checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" "checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" "checksum bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" -"checksum bytestring 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "fc267467f58ef6cc8874064c62a0423eb0d099362c8a23edd1c6d044f46eead4" -"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" +"checksum bytestring 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fc7c05fa5172da78a62d9949d662d2ac89d4cc7355d7b49adee5163f1fb3f363" "checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" @@ -3042,15 +3015,15 @@ dependencies = [ "checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" "checksum crossbeam-channel 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cced8691919c02aac3cb0a1bc2e9b73d89e832bf9a06fc579d4e71b68a2da061" "checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" -"checksum curl 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)" = "ecb534fed9060d04bccaa8b8e1e2d3d5a0d7a9ec6d9c667691c80a3c6b7d19ef" -"checksum curl-sys 0.4.28+curl-7.69.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2c6b7fa5d36aa192e410788b77af65f339af24c8786419e8b48173689a484bf" +"checksum curl 0.4.28 (registry+https://github.com/rust-lang/crates.io-index)" = "eda1c0c03cacf3365d84818a40293f0e3f3953db8759c9c565a3b434edf0b52e" +"checksum curl-sys 0.4.30+curl-7.69.1 (registry+https://github.com/rust-lang/crates.io-index)" = "923b38e423a8f47a4058e96f2a1fa2865a6231097ee860debd678d244277d50c" "checksum darling 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" "checksum darling_core 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" "checksum darling_macro 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" "checksum derive_builder 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a2658621297f2cf68762a6f7dc0bb7e1ff2cfd6583daef8ee0fed6f7ec468ec0" "checksum derive_builder_core 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2791ea3e372c8495c0bc2033991d76b512cd799d07491fbd6890124db9458bef" -"checksum derive_more 0.99.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a806e96c59a76a5ba6e18735b6cf833344671e61e7863f2edb5c518ea2cac95c" -"checksum diesel 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9d7cc03b910de9935007861dce440881f69102aaaedfd4bc5a6f40340ca5840c" +"checksum derive_more 0.99.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e2323f3f47db9a0e77ce7a300605d8d2098597fc451ed1a97bb1f6411bb550a7" +"checksum diesel 1.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "33d7ca63eb2efea87a7f56a283acc49e2ce4b2bd54adf7465dc1d81fef13d8fc" "checksum diesel_derives 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3" "checksum diesel_migrations 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3cde8413353dc7f5d72fa8ce0b99a560a359d2c5ef1e5817ca731cd9008f4c" "checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" @@ -3059,7 +3032,7 @@ dependencies = [ "checksum dtoa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0dd841b58510c9618291ffa448da2e4e0f699d984d436122372f446dae62263d" "checksum dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3" "checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" -"checksum email 0.0.20 (registry+https://github.com/rust-lang/crates.io-index)" = "91549a51bb0241165f13d57fc4c72cef063b4088fb078b019ecbf464a45f22e4" +"checksum email 0.0.21 (registry+https://github.com/rust-lang/crates.io-index)" = "65360503b8831670969621be3d3814c4c7be44c642de11f8c0f5aaa01f057b3e" "checksum encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec" "checksum encoding-index-japanese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91" "checksum encoding-index-korean 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81" @@ -3075,7 +3048,7 @@ dependencies = [ "checksum failure_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "030a733c8287d6213886dd487564ff5c8f6aae10278b3588ed177f9d18f8d231" "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" "checksum fast_chemail 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)" = "495a39d30d624c2caabe6312bfead73e7717692b44e0b32df168c275a2e8e9e4" -"checksum flate2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6bd6d6f4752952feb71363cffc9ebac9411b75b87c6ab6058c40c8900cf43c0f" +"checksum flate2 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "2cfff41391129e0a856d6d822600b8d71179d46879e310417eb9c762eb178b42" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" "checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" @@ -3094,14 +3067,14 @@ dependencies = [ "checksum fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" "checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" "checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" -"checksum h2 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9d5c295d1c0c68e4e42003d75f908f5e16a1edd1cbe0b0d02e4dc2006a384f47" +"checksum h2 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "377038bf3c89d18d6ca1431e7a5027194fbd724ca10592b9487ede5e8e144f42" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" -"checksum hermit-abi 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8" +"checksum hermit-abi 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8a0d737e0f947a1864e93d33fdef4af8445a00d1ed8dc0c8ddb73139ea6abf15" "checksum hjson 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0849d73a64ec77d1c8354aff489cf31943c4b4d3716de1eabfba572c70fde530" "checksum hostname 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "21ceb46a83a85e824ef93669c8b390009623863b5c195d1ba747292c0c72f94e" "checksum hostname 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" "checksum htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e9025058dae765dee5070ec375f591e2ba14638c63feff74f13805a72e523163" -"checksum http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b708cc7f06493459026f53b9a61a7a121a5d1ec6238dee58ea4941132b30156b" +"checksum http 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9" "checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" "checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" "checksum ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" @@ -3112,21 +3085,21 @@ dependencies = [ "checksum isahc 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "50bdb3bdcbf6d534daaad1a686eda0d0dc1946818fa71e3edd3124d001adfdc2" "checksum itoa 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae3088ea4baeceb0284ee9eea42f591226e6beaecf65373e41b38d95a1b8e7a1" "checksum itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" -"checksum js-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)" = "1cb931d43e71f560c81badb0191596562bafad2be06a3f9025b845c847c60df5" +"checksum js-sys 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)" = "6a27d435371a2fa5b6d2b028a74bbdb1234f308da363226a2854ca3ff8ba7055" "checksum jsonwebtoken 7.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d11f9e80a85927748a334df8e4f6782a04033517bb28f3863a563daad882da7f" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" "checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -"checksum lettre 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c66afaa5dfadbb81d4e00fd1d1ab057c7cd4c799c5a44e0009386d553587e728" -"checksum lettre_email 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bbb68ca999042d965476e47bbdbacd52db0927348b6f8062c44dd04a3b1fd43b" +"checksum lettre 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bf43f3202a879fbdab4ecafec3349b0139f81d31c626246d53bcbb546253ffaa" +"checksum lettre_email 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b1c03656b85bea745db40d9e82a8a0c7236e0171dcdf1b98e95916956ec993f1" "checksum lexical-core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d7043aa5c05dd34fb73b47acb8c3708eac428de4545ea3682ed2f11293ebd890" -"checksum libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)" = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018" +"checksum libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)" = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" "checksum libnghttp2-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b359f5ec8106bc297694c9a562ace312be2cfd17a5fc68dc12249845aa144b11" "checksum libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "2eb5e43362e38e2bca2fd5f5134c4d4564a23a5c28e9b95411652021a8675ebe" "checksum linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6d262045c5b87c0861b3f004610afd0e2c851e2908d08b6c870cbb9d5f494ecd" "checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" -"checksum lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b" +"checksum lock_api 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" "checksum maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" @@ -3135,8 +3108,8 @@ dependencies = [ "checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" "checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20" "checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" -"checksum migrations_internals 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8089920229070f914b9ce9b07ef60e175b2b9bc2d35c3edd8bf4433604e863b9" -"checksum migrations_macros 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "719ef0bc7f531428764c9b70661c14abd50a7f3d21f355752d9985aa21251c9e" +"checksum migrations_internals 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2b4fc84e4af020b837029e017966f86a1c2d5e83e64b589963d5047525995860" +"checksum migrations_macros 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9753f12909fd8d923f75ae5c3258cae1ed3c8ec052e1b38c93c21a6d157f789c" "checksum mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" "checksum mime_guess 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212" "checksum miniz_oxide 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aa679ff6578b1cddee93d7e82e263b94a575e0bfced07284eb0c037c1d2416a5" @@ -3152,38 +3125,37 @@ dependencies = [ "checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" "checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" "checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" -"checksum num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6" +"checksum num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" "checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" -"checksum openssl 0.10.28 (registry+https://github.com/rust-lang/crates.io-index)" = "973293749822d7dd6370d6da1e523b0d1db19f06c459134c658b2a4261378b52" +"checksum openssl 0.10.29 (registry+https://github.com/rust-lang/crates.io-index)" = "cee6d85f4cb4c4f59a6a85d5b68a233d280c82e29e822913b9c8b129fbf20bdd" "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" -"checksum openssl-sys 0.9.54 (registry+https://github.com/rust-lang/crates.io-index)" = "1024c0a59774200a555087a6da3f253a9095a5f344e353b212ac4c8b8e450986" -"checksum parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc" -"checksum parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1" +"checksum openssl-sys 0.9.55 (registry+https://github.com/rust-lang/crates.io-index)" = "7717097d810a0f2e2323f9e5d11e71608355e24828410b55b9d4f18aa5f9a5d8" +"checksum parking_lot 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" +"checksum parking_lot_core 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e136c1904604defe99ce5fd71a28d473fa60a12255d511aa78a9ddf11237aeb" "checksum pem 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a1581760c757a756a41f0ee3ff01256227bdf64cb752839779b95ffb01c59793" "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" "checksum pest 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" "checksum pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" "checksum pest_generator 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" "checksum pest_meta 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" -"checksum pin-project 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7804a463a8d9572f13453c516a5faea534a2403d7ced2f0c7e100eeff072772c" -"checksum pin-project-internal 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "385322a45f2ecf3410c68d2a549a4a2685e8051d0f278e39743ff4e451cb9b3f" +"checksum pin-project 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6f6a7f5eee6292c559c793430c55c00aea9d3b3d1905e855806ca4d7253426a2" +"checksum pin-project-internal 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "8988430ce790d8682672117bc06dda364c0be32d3abd738234f19f3240bad99a" "checksum pin-project-lite 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae" "checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" "checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" "checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" "checksum pq-sys 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6ac25eee5a0582f45a67e837e350d784e7003bd29a5f460796772061ca49ffda" -"checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" -"checksum proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e" -"checksum proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" +"checksum proc-macro-hack 0.5.15 (registry+https://github.com/rust-lang/crates.io-index)" = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63" +"checksum proc-macro-nested 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8e946095f9d3ed29ec38de908c22f95d9ac008e424c7bcae54c75a79c527c694" +"checksum proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" "checksum quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" "checksum quick-xml 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fe1e430bdcf30c9fdc25053b9c459bb1a4672af4617b6c783d7d91dc17c6bbb0" "checksum quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" "checksum r2d2 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1497e40855348e4a8a40767d8e55174bce1e445a3ac9254ad44ad468ee0485af" -"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" "checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" +"checksum rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" "checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" "checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" @@ -3197,33 +3169,32 @@ dependencies = [ "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" "checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f" -"checksum regex 1.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8900ebc1363efa7ea1c399ccc32daed870b4002651e0bed86e72d501ebbe0048" +"checksum regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692" "checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957" "checksum regex-syntax 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)" = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" "checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" "checksum resolv-conf 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "11834e137f3b14e309437a8276714eed3a80d1ef894869e510f2c0c0b98b9f4a" -"checksum ring 0.16.11 (registry+https://github.com/rust-lang/crates.io-index)" = "741ba1704ae21999c00942f9f5944f801e977f54302af346b596287599ad1862" +"checksum ring 0.16.12 (registry+https://github.com/rust-lang/crates.io-index)" = "1ba5a8ec64ee89a76c98c549af81ff14813df09c3e6dc4766c3856da48597a0c" "checksum rss 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "99979205510c60f80a119dedbabd0b8426517384edf205322f8bcd51796bcef9" "checksum rust-ini 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2" "checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" -"checksum safemem 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" -"checksum schannel 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "507a9e6e8ffe0a4e0ebb9a10293e62fdf7657c06f1b8bb07a8fcf697d2abf295" -"checksum scheduled-thread-pool 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f5de7bc31f28f8e6c28df5e1bf3d10610f5fdc14cc95f272853512c70a2bd779" +"checksum ryu 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76" +"checksum schannel 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "039c25b130bd8c1321ee2d7de7fde2659fa9c2744e4bb29711cfc852ea53cd19" +"checksum scheduled-thread-pool 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0988d7fdf88d5e5fcf5923a0f1e8ab345f3e98ab4bc6bc45a2d5ff7f7458fbf6" "checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -"checksum security-framework 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "97bbedbe81904398b6ebb054b3e912f99d55807125790f3198ac990d98def5b0" -"checksum security-framework-sys 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "06fd2f23e31ef68dd2328cc383bd493142e46107a3a0e24f7d734e3f3b80fe4c" +"checksum security-framework 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "572dfa3a0785509e7a44b5b4bebcf94d41ba34e9ed9eb9df722545c3b3c4144a" +"checksum security-framework-sys 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8ddb15a5fec93b7021b8a9e96009c5d8d51c15673569f7c0f6b7204e5b7b404f" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" "checksum serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" -"checksum serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)" = "e707fbbf255b8fc8c3b99abb91e7257a622caeb20a9818cbadbeeede4e0932ff" +"checksum serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)" = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" "checksum serde-hjson 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0b833c5ad67d52ced5f5938b2980f32a9c1c5ef047f0b4fb3127e7a423c76153" "checksum serde-hjson 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6a3a4e0ea8a88553209f6cc6cfe8724ecad22e1acf372793c27d995290fe74f8" -"checksum serde_derive 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)" = "ac5d00fc561ba2724df6758a17de23df5914f20e41cb00f94d5b7ae42fffaff8" +"checksum serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)" = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" "checksum serde_json 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)" = "67f7d2e9edc3523a9c8ec8cd6ec481b3a27810aafee3e625d311febd3e656b4c" -"checksum serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)" = "9371ade75d4c2d6cb154141b9752cf3781ec9c05e0e5cf35060e1e70ee7b9c25" +"checksum serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)" = "da07b57ee2623368351e9a0488bb0b261322a15a6e0ae53e243cbdc0f4208da9" "checksum serde_test 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)" = "110b3dbdf8607ec493c22d5d947753282f3bae73c0f56d322af1e8c78e4c23d5" "checksum serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" "checksum sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" @@ -3233,8 +3204,8 @@ dependencies = [ "checksum simple_asn1 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2b25ecba7165254f0c97d6c22a64b1122a03634b18d20a34daf21e18f892e618" "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" "checksum sluice 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fed13b7cb46f13a15db2c4740f087a848acc8b31af89f95844d40137451f89b1" -"checksum smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc" -"checksum socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "e8b74de517221a2cb01a53349cf54182acdc31a074727d3079068448c0676d85" +"checksum smallvec 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05720e22615919e4734f6a99ceae50d00226c3c5aca406e102ebc33298214e0a" +"checksum socket2 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)" = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" "checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" "checksum static_assertions 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3" "checksum strsim 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "67f84c44fbb2f91db7fef94554e6b2ac05909c9c0b0bc23bb98d3a1aebfe7f7c" @@ -3242,26 +3213,27 @@ dependencies = [ "checksum strsim 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" "checksum strum 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57bd81eb48f4c437cadc685403cad539345bf703d78e63707418431cecd4522b" "checksum strum_macros 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c" -"checksum syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859" +"checksum syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" "checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" "checksum termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -"checksum thiserror 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ee14bf8e6767ab4c687c9e8bc003879e042a96fd67a3ba5934eadb6536bef4db" -"checksum thiserror-impl 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "a7b51e1fbc44b5a0840be594fbc0f960be09050f2617e61e6aa43bef97cd3ef4" +"checksum thiserror 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "54b3d3d2ff68104100ab257bb6bb0cb26c901abe4bd4ba15961f3bf867924012" +"checksum thiserror-impl 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972988113b7715266f91250ddb98070d033c62a011fa0fcc57434a649310dd" "checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03" "checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5" "checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" "checksum threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e2f0c90a5f3459330ac8bc0d2f879c693bb7a2f59689c1083fc4ef83834da865" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" -"checksum tokio 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "0fa5e81d6bc4e67fe889d5783bd2a128ab2e0cfa487e0be16b6a8d177b101616" +"checksum tokio 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "34ef16d072d2b6dc8b4a56c70f5c5ced1a37752116f8e7c1e80c659aa7cb6713" "checksum tokio-util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "571da51182ec208780505a32528fc5512a8fe1443ab960b3f2f3ef093cd16930" +"checksum tokio-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" "checksum toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" "checksum trust-dns-proto 0.18.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2a7f3a2ab8a919f5eca52a468866a67ed7d3efa265d48a652a9a3452272b413f" "checksum trust-dns-resolver 0.18.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6f90b1502b226f8b2514c6d5b37bafa8c200d7ca4102d57dc36ee0f3b7a04a2f" "checksum twoway 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6b40075910de3a912adbd80b5d8bad6ad10a23eeb1f5bf9d4006839e899ba5bc" "checksum typed-arena 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9b2228007eba4120145f785df0f6c92ea538f5a3635a612ecf4e334c8c1446d" -"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" +"checksum typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" "checksum ucd-trie 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" "checksum unchecked-index 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eeba86d422ce181a719445e51872fa30f1f7413b62becb52e95ec91aa262d85c" "checksum unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" @@ -3283,18 +3255,18 @@ dependencies = [ "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" "checksum version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" "checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" -"checksum wasm-bindgen 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "3557c397ab5a8e347d434782bcd31fc1483d927a6826804cec05cc792ee2519d" -"checksum wasm-bindgen-backend 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "e0da9c9a19850d3af6df1cb9574970b566d617ecfaf36eb0b706b6f3ef9bd2f8" -"checksum wasm-bindgen-macro 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "0f6fde1d36e75a714b5fe0cffbb78978f222ea6baebb726af13c78869fdb4205" -"checksum wasm-bindgen-macro-support 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "25bda4168030a6412ea8a047e27238cadf56f0e53516e1e83fec0a8b7c786f6d" -"checksum wasm-bindgen-shared 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "fc9f36ad51f25b0219a3d4d13b90eb44cd075dff8b6280cca015775d7acaddd8" -"checksum web-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)" = "721c6263e2c66fd44501cc5efbfa2b7dfa775d13e4ea38c46299646ed1f9c70a" +"checksum wasm-bindgen 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)" = "2cc57ce05287f8376e998cbddfb4c8cb43b84a7ec55cf4551d7c00eef317a47f" +"checksum wasm-bindgen-backend 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)" = "d967d37bf6c16cca2973ca3af071d0a2523392e4a594548155d89a678f4237cd" +"checksum wasm-bindgen-macro 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)" = "8bd151b63e1ea881bb742cd20e1d6127cef28399558f3b5d415289bc41eee3a4" +"checksum wasm-bindgen-macro-support 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)" = "d68a5b36eef1be7868f668632863292e37739656a80fc4b9acec7b0bd35a4931" +"checksum wasm-bindgen-shared 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)" = "daf76fe7d25ac79748a37538b7daeed1c7a6867c92d3245c12c6222e4a20d639" +"checksum web-sys 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)" = "2d6f51648d8c56c366144378a33290049eafdd784071077f6fe37dae64c1c4cb" "checksum widestring 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "effc0e4ff8085673ea7b9b2e3c73f6bd4d118810c9009ed8f1e16bd96c331db6" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4ccfbf554c6ad11084fb7517daca16cfdcaccbdadba4fc336f032a8b12c2ad80" +"checksum winapi-util 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "fa515c5163a99cc82bab70fd3bfdd36d827be85de63737b40fcef2ce084a436e" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" "checksum winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7daf138b6b14196e3830a588acf1e86966c694d3e8fb026fb105b8b5dca07e6e" diff --git a/server/src/api/comment.rs b/server/src/api/comment.rs index 8373a338b..8e398c9ac 100644 --- a/server/src/api/comment.rs +++ b/server/src/api/comment.rs @@ -1,9 +1,4 @@ use super::*; -use crate::send_email; -use crate::settings::Settings; -use diesel::PgConnection; -use log::error; -use std::str::FromStr; #[derive(Serialize, Deserialize)] pub struct CreateComment { @@ -65,7 +60,12 @@ pub struct GetCommentsResponse { } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &CreateComment = &self.data; let claims = match Claims::decode(&data.auth) { @@ -77,6 +77,15 @@ impl Perform for Oper { let hostname = &format!("https://{}", Settings::get().hostname); + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + // Check for a community ban let post = Post::read(&conn, data.post_id)?; if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() { @@ -223,15 +232,34 @@ impl Perform for Oper { let comment_view = CommentView::read(&conn, inserted_comment.id, Some(user_id))?; - Ok(CommentResponse { + let mut res = CommentResponse { comment: comment_view, recipient_ids, - }) + }; + + if let Some(ws) = websocket_info { + ws.chatserver.do_send(SendComment { + op: UserOperation::CreateComment, + comment: res.clone(), + my_id: ws.id, + }); + + // strip out the recipient_ids, so that + // users don't get double notifs + res.recipient_ids = Vec::new(); + } + + Ok(res) } } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &EditComment = &self.data; let claims = match Claims::decode(&data.auth) { @@ -241,6 +269,15 @@ impl Perform for Oper { let user_id = claims.id; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + let orig_comment = CommentView::read(&conn, data.edit_id, None)?; // You are allowed to mark the comment as read even if you're banned. @@ -353,15 +390,34 @@ impl Perform for Oper { let comment_view = CommentView::read(&conn, data.edit_id, Some(user_id))?; - Ok(CommentResponse { + let mut res = CommentResponse { comment: comment_view, recipient_ids, - }) + }; + + if let Some(ws) = websocket_info { + ws.chatserver.do_send(SendComment { + op: UserOperation::EditComment, + comment: res.clone(), + my_id: ws.id, + }); + + // strip out the recipient_ids, so that + // users don't get double notifs + res.recipient_ids = Vec::new(); + } + + Ok(res) } } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + _websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &SaveComment = &self.data; let claims = match Claims::decode(&data.auth) { @@ -376,6 +432,15 @@ impl Perform for Oper { user_id, }; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + if data.save { match CommentSaved::save(&conn, &comment_saved_form) { Ok(comment) => comment, @@ -398,7 +463,12 @@ impl Perform for Oper { } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &CreateCommentLike = &self.data; let claims = match Claims::decode(&data.auth) { @@ -410,6 +480,15 @@ impl Perform for Oper { let mut recipient_ids = Vec::new(); + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + // Don't do a downvote if site has downvotes disabled if data.score == -1 { let site = SiteView::read(&conn)?; @@ -467,15 +546,34 @@ impl Perform for Oper { // Have to refetch the comment to get the current state let liked_comment = CommentView::read(&conn, data.comment_id, Some(user_id))?; - Ok(CommentResponse { + let mut res = CommentResponse { comment: liked_comment, recipient_ids, - }) + }; + + if let Some(ws) = websocket_info { + ws.chatserver.do_send(SendComment { + op: UserOperation::CreateCommentLike, + comment: res.clone(), + my_id: ws.id, + }); + + // strip out the recipient_ids, so that + // users don't get double notifs + res.recipient_ids = Vec::new(); + } + + Ok(res) } } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &GetComments = &self.data; let user_claims: Option = match &data.auth { @@ -494,6 +592,15 @@ impl Perform for Oper { let type_ = ListingType::from_str(&data.type_)?; let sort = SortType::from_str(&data.sort)?; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + let comments = match CommentQueryBuilder::create(&conn) .listing_type(type_) .sort(&sort) @@ -507,6 +614,20 @@ impl Perform for Oper { Err(_e) => return Err(APIError::err("couldnt_get_comments").into()), }; + if let Some(ws) = websocket_info { + // You don't need to join the specific community room, bc this is already handled by + // GetCommunity + if data.community_id.is_none() { + if let Some(id) = ws.id { + // 0 is the "all" community + ws.chatserver.do_send(JoinCommunityRoom { + community_id: 0, + id, + }); + } + } + } + Ok(GetCommentsResponse { comments }) } } diff --git a/server/src/api/community.rs b/server/src/api/community.rs index 936e54cda..0f4376939 100644 --- a/server/src/api/community.rs +++ b/server/src/api/community.rs @@ -1,6 +1,4 @@ use super::*; -use diesel::PgConnection; -use std::str::FromStr; #[derive(Serialize, Deserialize)] pub struct GetCommunity { @@ -55,7 +53,7 @@ pub struct BanFromCommunity { auth: String, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Clone)] pub struct BanFromCommunityResponse { user: UserView, banned: bool, @@ -69,7 +67,7 @@ pub struct AddModToCommunity { auth: String, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Clone)] pub struct AddModToCommunityResponse { moderators: Vec, } @@ -114,7 +112,12 @@ pub struct TransferCommunity { } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &GetCommunity = &self.data; let user_id: Option = match &data.auth { @@ -128,6 +131,15 @@ impl Perform for Oper { None => None, }; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + let community_id = match data.id { Some(id) => id, None => { @@ -157,18 +169,41 @@ impl Perform for Oper { let creator_user = admins.remove(creator_index); admins.insert(0, creator_user); - // Return the jwt - Ok(GetCommunityResponse { + let online = if let Some(ws) = websocket_info { + if let Some(id) = ws.id { + ws.chatserver + .do_send(JoinCommunityRoom { community_id, id }); + } + + // TODO + 1 + // let fut = async { + // ws.chatserver.send(GetCommunityUsersOnline {community_id}).await.unwrap() + // }; + // Runtime::new().unwrap().block_on(fut) + } else { + 0 + }; + + let res = GetCommunityResponse { community: community_view, moderators, admins, - online: 0, - }) + online, + }; + + // Return the jwt + Ok(res) } } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + _websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &CreateCommunity = &self.data; let claims = match Claims::decode(&data.auth) { @@ -192,6 +227,15 @@ impl Perform for Oper { let user_id = claims.id; + if let Some(rl) = &rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_register(&rl.ip, true)?; + } + + let conn = pool.get()?; + // Check for a site ban if UserView::read(&conn, user_id)?.banned { return Err(APIError::err("site_ban").into()); @@ -239,6 +283,13 @@ impl Perform for Oper { let community_view = CommunityView::read(&conn, inserted_community.id, Some(user_id))?; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_register(&rl.ip, false)?; + } + Ok(CommunityResponse { community: community_view, }) @@ -246,7 +297,12 @@ impl Perform for Oper { } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &EditCommunity = &self.data; if let Err(slurs) = slur_check(&data.name) { @@ -270,6 +326,15 @@ impl Perform for Oper { let user_id = claims.id; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + // Check for a site ban if UserView::read(&conn, user_id)?.banned { return Err(APIError::err("site_ban").into()); @@ -323,14 +388,35 @@ impl Perform for Oper { let community_view = CommunityView::read(&conn, data.edit_id, Some(user_id))?; - Ok(CommunityResponse { + let res = CommunityResponse { community: community_view, - }) + }; + + if let Some(ws) = websocket_info { + // Strip out the user id and subscribed when sending to others + let mut res_sent = res.clone(); + res_sent.community.user_id = None; + res_sent.community.subscribed = None; + + ws.chatserver.do_send(SendCommunityRoomMessage { + op: UserOperation::EditCommunity, + response: res_sent, + community_id: data.edit_id, + my_id: ws.id, + }); + } + + Ok(res) } } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + _websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &ListCommunities = &self.data; let user_claims: Option = match &data.auth { @@ -353,6 +439,15 @@ impl Perform for Oper { let sort = SortType::from_str(&data.sort)?; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + let communities = CommunityQueryBuilder::create(&conn) .sort(&sort) .for_user(user_id) @@ -367,7 +462,12 @@ impl Perform for Oper { } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + _websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &FollowCommunity = &self.data; let claims = match Claims::decode(&data.auth) { @@ -382,6 +482,15 @@ impl Perform for Oper { user_id, }; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + if data.follow { match CommunityFollower::follow(&conn, &community_follower_form) { Ok(user) => user, @@ -403,7 +512,12 @@ impl Perform for Oper { } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + _websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &GetFollowedCommunities = &self.data; let claims = match Claims::decode(&data.auth) { @@ -413,6 +527,15 @@ impl Perform for Oper { let user_id = claims.id; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + let communities: Vec = match CommunityFollowerView::for_user(&conn, user_id) { Ok(communities) => communities, @@ -425,7 +548,12 @@ impl Perform for Oper { } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &BanFromCommunity = &self.data; let claims = match Claims::decode(&data.auth) { @@ -440,6 +568,15 @@ impl Perform for Oper { user_id: data.user_id, }; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + if data.ban { match CommunityUserBan::ban(&conn, &community_user_ban_form) { Ok(user) => user, @@ -470,15 +607,31 @@ impl Perform for Oper { let user_view = UserView::read(&conn, data.user_id)?; - Ok(BanFromCommunityResponse { + let res = BanFromCommunityResponse { user: user_view, banned: data.ban, - }) + }; + + if let Some(ws) = websocket_info { + ws.chatserver.do_send(SendCommunityRoomMessage { + op: UserOperation::BanFromCommunity, + response: res.clone(), + community_id: data.community_id, + my_id: ws.id, + }); + } + + Ok(res) } } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &AddModToCommunity = &self.data; let claims = match Claims::decode(&data.auth) { @@ -493,6 +646,15 @@ impl Perform for Oper { user_id: data.user_id, }; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + if data.added { match CommunityModerator::join(&conn, &community_moderator_form) { Ok(user) => user, @@ -516,12 +678,28 @@ impl Perform for Oper { let moderators = CommunityModeratorView::for_community(&conn, data.community_id)?; - Ok(AddModToCommunityResponse { moderators }) + let res = AddModToCommunityResponse { moderators }; + + if let Some(ws) = websocket_info { + ws.chatserver.do_send(SendCommunityRoomMessage { + op: UserOperation::AddModToCommunity, + response: res.clone(), + community_id: data.community_id, + my_id: ws.id, + }); + } + + Ok(res) } } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + _websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &TransferCommunity = &self.data; let claims = match Claims::decode(&data.auth) { @@ -531,6 +709,15 @@ impl Perform for Oper { let user_id = claims.id; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + let read_community = Community::read(&conn, data.community_id)?; let site_creator_id = Site::read(&conn, 1)?.creator_id; diff --git a/server/src/api/mod.rs b/server/src/api/mod.rs index e4fdfee61..e40d122c8 100644 --- a/server/src/api/mod.rs +++ b/server/src/api/mod.rs @@ -18,12 +18,26 @@ use crate::db::user_mention_view::*; use crate::db::user_view::*; use crate::db::*; use crate::{ - extract_usernames, fetch_iframely_and_pictshare_data, naive_from_unix, naive_now, remove_slurs, - slur_check, slurs_vec_to_str, + extract_usernames, fetch_iframely_and_pictshare_data, generate_random_string, naive_from_unix, + naive_now, remove_slurs, send_email, slur_check, slurs_vec_to_str, }; + +use crate::rate_limit::RateLimitInfo; +use crate::settings::Settings; +use crate::websocket::UserOperation; +use crate::websocket::{ + server::{ + JoinCommunityRoom, JoinPostRoom, JoinUserRoom, SendAllMessage, SendComment, + SendCommunityRoomMessage, SendPost, SendUserRoomMessage, + }, + WebsocketInfo, +}; +use diesel::r2d2::{ConnectionManager, Pool}; use diesel::PgConnection; use failure::Error; +use log::{error, info}; use serde::{Deserialize, Serialize}; +use std::str::FromStr; pub mod comment; pub mod community; @@ -56,7 +70,12 @@ impl Oper { } pub trait Perform { - fn perform(&self, conn: &PgConnection) -> Result + fn perform( + &self, + pool: Pool>, + websocket_info: Option, + rate_limit_info: Option, + ) -> Result where T: Sized; } diff --git a/server/src/api/post.rs b/server/src/api/post.rs index fb0225891..19f160149 100644 --- a/server/src/api/post.rs +++ b/server/src/api/post.rs @@ -1,6 +1,4 @@ use super::*; -use diesel::PgConnection; -use std::str::FromStr; #[derive(Serialize, Deserialize)] pub struct CreatePost { @@ -80,7 +78,12 @@ pub struct SavePost { } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &CreatePost = &self.data; let claims = match Claims::decode(&data.auth) { @@ -100,6 +103,15 @@ impl Perform for Oper { let user_id = claims.id; + if let Some(rl) = &rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_post(&rl.ip, true)?; + } + + let conn = pool.get()?; + // Check for a community ban if CommunityUserBanView::get(&conn, user_id, data.community_id).is_ok() { return Err(APIError::err("community_ban").into()); @@ -164,12 +176,34 @@ impl Perform for Oper { Err(_e) => return Err(APIError::err("couldnt_find_post").into()), }; - Ok(PostResponse { post: post_view }) + if let Some(rl) = &rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_post(&rl.ip, false)?; + } + + let res = PostResponse { post: post_view }; + + if let Some(ws) = websocket_info { + ws.chatserver.do_send(SendPost { + op: UserOperation::CreatePost, + post: res.clone(), + my_id: ws.id, + }); + } + + Ok(res) } } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &GetPost = &self.data; let user_id: Option = match &data.auth { @@ -183,6 +217,15 @@ impl Perform for Oper { None => None, }; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + let post_view = match PostView::read(&conn, data.id, user_id) { Ok(post) => post, Err(_e) => return Err(APIError::err("couldnt_find_post").into()), @@ -204,6 +247,24 @@ impl Perform for Oper { let creator_user = admins.remove(creator_index); admins.insert(0, creator_user); + let online = if let Some(ws) = websocket_info { + if let Some(id) = ws.id { + ws.chatserver.do_send(JoinPostRoom { + post_id: data.id, + id, + }); + } + + // TODO + 1 + // let fut = async { + // ws.chatserver.send(GetPostUsersOnline {post_id: data.id}).await.unwrap() + // }; + // Runtime::new().unwrap().block_on(fut) + } else { + 0 + }; + // Return the jwt Ok(GetPostResponse { post: post_view, @@ -211,13 +272,18 @@ impl Perform for Oper { community, moderators, admins, - online: 0, + online, }) } } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &GetPosts = &self.data; let user_claims: Option = match &data.auth { @@ -241,6 +307,15 @@ impl Perform for Oper { let type_ = ListingType::from_str(&data.type_)?; let sort = SortType::from_str(&data.sort)?; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + let posts = match PostQueryBuilder::create(&conn) .listing_type(type_) .sort(&sort) @@ -255,12 +330,31 @@ impl Perform for Oper { Err(_e) => return Err(APIError::err("couldnt_get_posts").into()), }; + if let Some(ws) = websocket_info { + // You don't need to join the specific community room, bc this is already handled by + // GetCommunity + if data.community_id.is_none() { + if let Some(id) = ws.id { + // 0 is the "all" community + ws.chatserver.do_send(JoinCommunityRoom { + community_id: 0, + id, + }); + } + } + } + Ok(GetPostsResponse { posts }) } } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &CreatePostLike = &self.data; let claims = match Claims::decode(&data.auth) { @@ -270,6 +364,15 @@ impl Perform for Oper { let user_id = claims.id; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + // Don't do a downvote if site has downvotes disabled if data.score == -1 { let site = SiteView::read(&conn)?; @@ -312,13 +415,27 @@ impl Perform for Oper { Err(_e) => return Err(APIError::err("couldnt_find_post").into()), }; - // just output the score - Ok(PostResponse { post: post_view }) + let res = PostResponse { post: post_view }; + + if let Some(ws) = websocket_info { + ws.chatserver.do_send(SendPost { + op: UserOperation::CreatePostLike, + post: res.clone(), + my_id: ws.id, + }); + } + + Ok(res) } } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &EditPost = &self.data; if let Err(slurs) = slur_check(&data.name) { @@ -338,6 +455,15 @@ impl Perform for Oper { let user_id = claims.id; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + // Verify its the creator or a mod or admin let mut editors: Vec = vec![data.creator_id]; editors.append( @@ -427,12 +553,27 @@ impl Perform for Oper { let post_view = PostView::read(&conn, data.edit_id, Some(user_id))?; - Ok(PostResponse { post: post_view }) + let res = PostResponse { post: post_view }; + + if let Some(ws) = websocket_info { + ws.chatserver.do_send(SendPost { + op: UserOperation::EditPost, + post: res.clone(), + my_id: ws.id, + }); + } + + Ok(res) } } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + _websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &SavePost = &self.data; let claims = match Claims::decode(&data.auth) { @@ -447,6 +588,15 @@ impl Perform for Oper { user_id, }; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + if data.save { match PostSaved::save(&conn, &post_saved_form) { Ok(post) => post, diff --git a/server/src/api/site.rs b/server/src/api/site.rs index 3720a2c4c..891f52a48 100644 --- a/server/src/api/site.rs +++ b/server/src/api/site.rs @@ -1,10 +1,5 @@ +use super::user::Register; use super::*; -use crate::api::user::Register; -use crate::api::{Oper, Perform}; -use crate::settings::Settings; -use diesel::PgConnection; -use log::info; -use std::str::FromStr; #[derive(Serialize, Deserialize)] pub struct ListCategories {} @@ -78,7 +73,7 @@ pub struct EditSite { #[derive(Serialize, Deserialize)] pub struct GetSite {} -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Clone)] pub struct SiteResponse { site: SiteView, } @@ -114,9 +109,23 @@ pub struct SaveSiteConfig { } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + _websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let _data: &ListCategories = &self.data; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + let categories: Vec = Category::list_all(&conn)?; // Return the jwt @@ -125,9 +134,23 @@ impl Perform for Oper { } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + _websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &GetModlog = &self.data; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + let removed_posts = ModRemovePostView::list( &conn, data.community_id, @@ -198,7 +221,12 @@ impl Perform for Oper { } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + _websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &CreateSite = &self.data; let claims = match Claims::decode(&data.auth) { @@ -218,6 +246,15 @@ impl Perform for Oper { let user_id = claims.id; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + // Make sure user is an admin if !UserView::read(&conn, user_id)?.admin { return Err(APIError::err("not_an_admin").into()); @@ -245,7 +282,12 @@ impl Perform for Oper { } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &EditSite = &self.data; let claims = match Claims::decode(&data.auth) { @@ -265,6 +307,15 @@ impl Perform for Oper { let user_id = claims.id; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + // Make sure user is an admin if !UserView::read(&conn, user_id)?.admin { return Err(APIError::err("not_an_admin").into()); @@ -289,14 +340,39 @@ impl Perform for Oper { let site_view = SiteView::read(&conn)?; - Ok(SiteResponse { site: site_view }) + let res = SiteResponse { site: site_view }; + + if let Some(ws) = websocket_info { + ws.chatserver.do_send(SendAllMessage { + op: UserOperation::EditSite, + response: res.clone(), + my_id: ws.id, + }); + } + + Ok(res) } } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let _data: &GetSite = &self.data; + if let Some(rl) = &rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + + // TODO refactor this a little let site = Site::read(&conn, 1); let site_view = if site.is_ok() { Some(SiteView::read(&conn)?) @@ -309,7 +385,11 @@ impl Perform for Oper { admin: true, show_nsfw: true, }; - let login_response = Oper::new(register).perform(&conn)?; + let login_response = Oper::new(register).perform( + pool.clone(), + websocket_info.clone(), + rate_limit_info.clone(), + )?; info!("Admin {} created", setup.admin_username); let create_site = CreateSite { @@ -320,7 +400,7 @@ impl Perform for Oper { enable_nsfw: false, auth: login_response.jwt, }; - Oper::new(create_site).perform(&conn)?; + Oper::new(create_site).perform(pool, websocket_info.clone(), rate_limit_info)?; info!("Site {} created", setup.site_name); Some(SiteView::read(&conn)?) } else { @@ -337,17 +417,33 @@ impl Perform for Oper { let banned = UserView::banned(&conn)?; + let online = if let Some(_ws) = websocket_info { + // TODO + 1 + // let fut = async { + // ws.chatserver.send(GetUsersOnline).await.unwrap() + // }; + // Runtime::new().unwrap().block_on(fut) + } else { + 0 + }; + Ok(GetSiteResponse { site: site_view, admins, banned, - online: 0, + online, }) } } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + _websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &Search = &self.data; let user_id: Option = match &data.auth { @@ -371,6 +467,15 @@ impl Perform for Oper { // TODO no clean / non-nsfw searching rn + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + match type_ { SearchType::Posts => { posts = PostQueryBuilder::create(&conn) @@ -465,7 +570,12 @@ impl Perform for Oper { } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + _websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &TransferSite = &self.data; let claims = match Claims::decode(&data.auth) { @@ -475,6 +585,15 @@ impl Perform for Oper { let user_id = claims.id; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + let read_site = Site::read(&conn, 1)?; // Make sure user is the creator @@ -528,7 +647,12 @@ impl Perform for Oper { } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + _websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &GetSiteConfig = &self.data; let claims = match Claims::decode(&data.auth) { @@ -538,6 +662,15 @@ impl Perform for Oper { let user_id = claims.id; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + // Only let admins read this let admins = UserView::admins(&conn)?; let admin_ids: Vec = admins.into_iter().map(|m| m.id).collect(); @@ -553,7 +686,12 @@ impl Perform for Oper { } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + _websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &SaveSiteConfig = &self.data; let claims = match Claims::decode(&data.auth) { @@ -563,6 +701,15 @@ impl Perform for Oper { let user_id = claims.id; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + // Only let admins read this let admins = UserView::admins(&conn)?; let admin_ids: Vec = admins.into_iter().map(|m| m.id).collect(); diff --git a/server/src/api/user.rs b/server/src/api/user.rs index 40e099694..31a0a4e78 100644 --- a/server/src/api/user.rs +++ b/server/src/api/user.rs @@ -1,10 +1,5 @@ use super::*; -use crate::settings::Settings; -use crate::{generate_random_string, send_email}; use bcrypt::verify; -use diesel::PgConnection; -use log::error; -use std::str::FromStr; #[derive(Serialize, Deserialize, Debug)] pub struct Login { @@ -89,7 +84,7 @@ pub struct AddAdmin { auth: String, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Clone)] pub struct AddAdminResponse { admins: Vec, } @@ -103,7 +98,7 @@ pub struct BanUser { auth: String, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Clone)] pub struct BanUserResponse { user: UserView, banned: bool, @@ -205,9 +200,23 @@ pub struct UserJoinResponse { } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + _websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &Login = &self.data; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + // Fetch that username / email let user: User_ = match User_::find_by_email_or_username(&conn, &data.username_or_email) { Ok(user) => user, @@ -226,9 +235,23 @@ impl Perform for Oper { } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + _websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &Register = &self.data; + if let Some(rl) = &rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_register(&rl.ip, true)?; + } + + let conn = pool.get()?; + // Make sure site has open registration if let Ok(site) = SiteView::read(&conn) { if !site.open_registration { @@ -332,6 +355,13 @@ impl Perform for Oper { }; } + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_register(&rl.ip, false)?; + } + // Return the jwt Ok(LoginResponse { jwt: inserted_user.jwt(), @@ -340,7 +370,12 @@ impl Perform for Oper { } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + _websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &SaveUserSettings = &self.data; let claims = match Claims::decode(&data.auth) { @@ -350,6 +385,15 @@ impl Perform for Oper { let user_id = claims.id; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + let read_user = User_::read(&conn, user_id)?; let email = match &data.email { @@ -428,9 +472,23 @@ impl Perform for Oper { } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + _websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &GetUserDetails = &self.data; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + let user_claims: Option = match &data.auth { Some(auth) => match Claims::decode(&auth) { Ok(claims) => Some(claims.claims), @@ -525,7 +583,12 @@ impl Perform for Oper { } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &AddAdmin = &self.data; let claims = match Claims::decode(&data.auth) { @@ -535,6 +598,15 @@ impl Perform for Oper { let user_id = claims.id; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + // Make sure user is an admin if !UserView::read(&conn, user_id)?.admin { return Err(APIError::err("not_an_admin").into()); @@ -583,12 +655,27 @@ impl Perform for Oper { let creator_user = admins.remove(creator_index); admins.insert(0, creator_user); - Ok(AddAdminResponse { admins }) + let res = AddAdminResponse { admins }; + + if let Some(ws) = websocket_info { + ws.chatserver.do_send(SendAllMessage { + op: UserOperation::AddAdmin, + response: res.clone(), + my_id: ws.id, + }); + } + + Ok(res) } } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &BanUser = &self.data; let claims = match Claims::decode(&data.auth) { @@ -598,6 +685,15 @@ impl Perform for Oper { let user_id = claims.id; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + // Make sure user is an admin if !UserView::read(&conn, user_id)?.admin { return Err(APIError::err("not_an_admin").into()); @@ -649,15 +745,30 @@ impl Perform for Oper { let user_view = UserView::read(&conn, data.user_id)?; - Ok(BanUserResponse { + let res = BanUserResponse { user: user_view, banned: data.ban, - }) + }; + + if let Some(ws) = websocket_info { + ws.chatserver.do_send(SendAllMessage { + op: UserOperation::BanUser, + response: res.clone(), + my_id: ws.id, + }); + } + + Ok(res) } } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + _websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &GetReplies = &self.data; let claims = match Claims::decode(&data.auth) { @@ -669,6 +780,15 @@ impl Perform for Oper { let sort = SortType::from_str(&data.sort)?; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + let replies = ReplyQueryBuilder::create(&conn, user_id) .sort(&sort) .unread_only(data.unread_only) @@ -681,7 +801,12 @@ impl Perform for Oper { } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + _websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &GetUserMentions = &self.data; let claims = match Claims::decode(&data.auth) { @@ -693,6 +818,15 @@ impl Perform for Oper { let sort = SortType::from_str(&data.sort)?; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + let mentions = UserMentionQueryBuilder::create(&conn, user_id) .sort(&sort) .unread_only(data.unread_only) @@ -705,7 +839,12 @@ impl Perform for Oper { } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + _websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &EditUserMention = &self.data; let claims = match Claims::decode(&data.auth) { @@ -715,6 +854,15 @@ impl Perform for Oper { let user_id = claims.id; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + let user_mention = UserMention::read(&conn, data.user_mention_id)?; let user_mention_form = UserMentionForm { @@ -738,7 +886,12 @@ impl Perform for Oper { } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + _websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &MarkAllAsRead = &self.data; let claims = match Claims::decode(&data.auth) { @@ -748,6 +901,15 @@ impl Perform for Oper { let user_id = claims.id; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + let replies = ReplyQueryBuilder::create(&conn, user_id) .unread_only(true) .page(1) @@ -822,7 +984,12 @@ impl Perform for Oper { } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + _websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &DeleteAccount = &self.data; let claims = match Claims::decode(&data.auth) { @@ -832,6 +999,15 @@ impl Perform for Oper { let user_id = claims.id; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + let user: User_ = User_::read(&conn, user_id)?; // Verify the password @@ -903,9 +1079,23 @@ impl Perform for Oper { } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + _websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &PasswordReset = &self.data; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + // Fetch that email let user: User_ = match User_::find_by_email(&conn, &data.email) { Ok(user) => user, @@ -934,9 +1124,23 @@ impl Perform for Oper { } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + _websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &PasswordChange = &self.data; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + // Fetch the user_id from the token let user_id = PasswordResetRequest::read_from_token(&conn, &data.token)?.user_id; @@ -959,7 +1163,12 @@ impl Perform for Oper { } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &CreatePrivateMessage = &self.data; let claims = match Claims::decode(&data.auth) { @@ -971,6 +1180,15 @@ impl Perform for Oper { let hostname = &format!("https://{}", Settings::get().hostname); + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + // Check for a site ban if UserView::read(&conn, user_id)?.banned { return Err(APIError::err("site_ban").into()); @@ -1016,12 +1234,28 @@ impl Perform for Oper { let message = PrivateMessageView::read(&conn, inserted_private_message.id)?; - Ok(PrivateMessageResponse { message }) + let res = PrivateMessageResponse { message }; + + if let Some(ws) = websocket_info { + ws.chatserver.do_send(SendUserRoomMessage { + op: UserOperation::CreatePrivateMessage, + response: res.clone(), + recipient_id: recipient_user.id, + my_id: ws.id, + }); + } + + Ok(res) } } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + _websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &EditPrivateMessage = &self.data; let claims = match Claims::decode(&data.auth) { @@ -1031,6 +1265,15 @@ impl Perform for Oper { let user_id = claims.id; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + let orig_private_message = PrivateMessage::read(&conn, data.edit_id)?; // Check for a site ban @@ -1076,7 +1319,12 @@ impl Perform for Oper { } impl Perform for Oper { - fn perform(&self, conn: &PgConnection) -> Result { + fn perform( + &self, + pool: Pool>, + _websocket_info: Option, + rate_limit_info: Option, + ) -> Result { let data: &GetPrivateMessages = &self.data; let claims = match Claims::decode(&data.auth) { @@ -1086,6 +1334,15 @@ impl Perform for Oper { let user_id = claims.id; + if let Some(rl) = rate_limit_info { + rl.rate_limiter + .lock() + .unwrap() + .check_rate_limit_message(&rl.ip, false)?; + } + + let conn = pool.get()?; + let messages = PrivateMessageQueryBuilder::create(&conn, user_id) .page(data.page) .limit(data.limit) @@ -1097,7 +1354,12 @@ impl Perform for Oper { } impl Perform for Oper { - fn perform(&self, _conn: &PgConnection) -> Result { + fn perform( + &self, + _pool: Pool>, + websocket_info: Option, + _rate_limit_info: Option, + ) -> Result { let data: &UserJoin = &self.data; let claims = match Claims::decode(&data.auth) { @@ -1106,6 +1368,13 @@ impl Perform for Oper { }; let user_id = claims.id; + + if let Some(ws) = websocket_info { + if let Some(id) = ws.id { + ws.chatserver.do_send(JoinUserRoom { user_id, id }); + } + } + Ok(UserJoinResponse { user_id }) } } diff --git a/server/src/lib.rs b/server/src/lib.rs index 9bbfe251a..2d0ecc3a3 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -27,13 +27,14 @@ pub extern crate strum; pub mod api; pub mod apub; pub mod db; +pub mod rate_limit; pub mod routes; pub mod schema; pub mod settings; pub mod version; pub mod websocket; -use crate::settings::Settings; +use actix_web::HttpRequest; use chrono::{DateTime, NaiveDateTime, Utc}; use isahc::prelude::*; use lettre::smtp::authentication::{Credentials, Mechanism}; @@ -48,6 +49,14 @@ use rand::{thread_rng, Rng}; use regex::{Regex, RegexBuilder}; use serde::Deserialize; +use crate::settings::Settings; + +pub type ConnectionId = usize; +pub type PostId = i32; +pub type CommunityId = i32; +pub type UserId = i32; +pub type IPAddr = String; + pub fn to_datetime_utc(ndt: NaiveDateTime) -> DateTime { DateTime::::from_utc(ndt, Utc) } @@ -224,6 +233,17 @@ pub fn markdown_to_html(text: &str) -> String { comrak::markdown_to_html(text, &comrak::ComrakOptions::default()) } +pub fn get_ip(req: &HttpRequest) -> String { + req + .connection_info() + .remote() + .unwrap_or("127.0.0.1:12345") + .split(':') + .next() + .unwrap_or("127.0.0.1") + .to_string() +} + #[cfg(test)] mod tests { use crate::{extract_usernames, is_email_regex, remove_slurs, slur_check, slurs_vec_to_str}; diff --git a/server/src/main.rs b/server/src/main.rs index f38875275..eb4ba0e94 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -6,10 +6,16 @@ use actix::prelude::*; use actix_web::*; use diesel::r2d2::{ConnectionManager, Pool}; use diesel::PgConnection; -use lemmy_server::routes::{api, federation, feeds, index, nodeinfo, webfinger, websocket}; -use lemmy_server::settings::Settings; -use lemmy_server::websocket::server::*; -use std::io; +use lemmy_server::{ + rate_limit::rate_limiter::RateLimiter, + routes::{api, federation, feeds, index, nodeinfo, webfinger, websocket}, + settings::Settings, + websocket::server::*, +}; +use std::{ + io, + sync::{Arc, Mutex}, +}; embed_migrations!(); @@ -29,8 +35,11 @@ async fn main() -> io::Result<()> { let conn = pool.get().unwrap(); embedded_migrations::run(&conn).unwrap(); + // Set up the rate limiter + let rate_limiter = Arc::new(Mutex::new(RateLimiter::default())); + // Set up websocket server - let server = ChatServer::startup(pool.clone()).start(); + let server = ChatServer::startup(pool.clone(), rate_limiter.clone()).start(); println!( "Starting http server at {}:{}", @@ -44,6 +53,7 @@ async fn main() -> io::Result<()> { .wrap(middleware::Logger::default()) .data(pool.clone()) .data(server.clone()) + .data(rate_limiter.clone()) // The routes .configure(api::config) .configure(federation::config) diff --git a/server/src/rate_limit/mod.rs b/server/src/rate_limit/mod.rs new file mode 100644 index 000000000..29a3a9e14 --- /dev/null +++ b/server/src/rate_limit/mod.rs @@ -0,0 +1,18 @@ +pub mod rate_limiter; + +use super::{IPAddr, Settings}; +use crate::api::APIError; +use failure::Error; +use log::warn; +use rate_limiter::RateLimiter; +use std::collections::HashMap; +use std::sync::Arc; +use std::sync::Mutex; +use std::time::SystemTime; +use strum::IntoEnumIterator; + +#[derive(Debug, Clone)] +pub struct RateLimitInfo { + pub rate_limiter: Arc>, + pub ip: IPAddr, +} diff --git a/server/src/rate_limit/rate_limiter.rs b/server/src/rate_limit/rate_limiter.rs new file mode 100644 index 000000000..6b01a75b2 --- /dev/null +++ b/server/src/rate_limit/rate_limiter.rs @@ -0,0 +1,131 @@ +use super::*; + +#[derive(Debug, Clone)] +pub struct RateLimitBucket { + last_checked: SystemTime, + allowance: f64, +} + +#[derive(Eq, PartialEq, Hash, Debug, EnumIter, Copy, Clone)] +pub enum RateLimitType { + Message, + Register, + Post, +} + +/// Rate limiting based on rate type and IP addr +#[derive(Debug, Clone)] +pub struct RateLimiter { + pub buckets: HashMap>, +} + +impl Default for RateLimiter { + fn default() -> Self { + Self { + buckets: HashMap::new(), + } + } +} + +impl RateLimiter { + fn insert_ip(&mut self, ip: &str) { + for rate_limit_type in RateLimitType::iter() { + if self.buckets.get(&rate_limit_type).is_none() { + self.buckets.insert(rate_limit_type, HashMap::new()); + } + + if let Some(bucket) = self.buckets.get_mut(&rate_limit_type) { + if bucket.get(ip).is_none() { + bucket.insert( + ip.to_string(), + RateLimitBucket { + last_checked: SystemTime::now(), + allowance: -2f64, + }, + ); + } + } + } + } + + pub fn check_rate_limit_register(&mut self, ip: &str, check_only: bool) -> Result<(), Error> { + self.check_rate_limit_full( + RateLimitType::Register, + ip, + Settings::get().rate_limit.register, + Settings::get().rate_limit.register_per_second, + check_only, + ) + } + + pub fn check_rate_limit_post(&mut self, ip: &str, check_only: bool) -> Result<(), Error> { + self.check_rate_limit_full( + RateLimitType::Post, + ip, + Settings::get().rate_limit.post, + Settings::get().rate_limit.post_per_second, + check_only, + ) + } + + pub fn check_rate_limit_message(&mut self, ip: &str, check_only: bool) -> Result<(), Error> { + self.check_rate_limit_full( + RateLimitType::Message, + ip, + Settings::get().rate_limit.message, + Settings::get().rate_limit.message_per_second, + check_only, + ) + } + + #[allow(clippy::float_cmp)] + fn check_rate_limit_full( + &mut self, + type_: RateLimitType, + ip: &str, + rate: i32, + per: i32, + check_only: bool, + ) -> Result<(), Error> { + self.insert_ip(ip); + if let Some(bucket) = self.buckets.get_mut(&type_) { + if let Some(rate_limit) = bucket.get_mut(ip) { + let current = SystemTime::now(); + let time_passed = current.duration_since(rate_limit.last_checked)?.as_secs() as f64; + + // The initial value + if rate_limit.allowance == -2f64 { + rate_limit.allowance = rate as f64; + }; + + rate_limit.last_checked = current; + rate_limit.allowance += time_passed * (rate as f64 / per as f64); + if !check_only && rate_limit.allowance > rate as f64 { + rate_limit.allowance = rate as f64; + } + + if rate_limit.allowance < 1.0 { + warn!( + "Rate limited IP: {}, time_passed: {}, allowance: {}", + ip, time_passed, rate_limit.allowance + ); + Err( + APIError { + message: format!("Too many requests. {} per {} seconds", rate, per), + } + .into(), + ) + } else { + if !check_only { + rate_limit.allowance -= 1.0; + } + Ok(()) + } + } else { + Ok(()) + } + } else { + Ok(()) + } + } +} diff --git a/server/src/routes/api.rs b/server/src/routes/api.rs index 36a55f960..0ac1a8a53 100644 --- a/server/src/routes/api.rs +++ b/server/src/routes/api.rs @@ -1,16 +1,9 @@ +use super::*; use crate::api::comment::*; use crate::api::community::*; use crate::api::post::*; use crate::api::site::*; use crate::api::user::*; -use crate::api::{Oper, Perform}; -use actix_web::{web, HttpResponse}; -use diesel::r2d2::{ConnectionManager, Pool}; -use diesel::PgConnection; -use failure::Error; -use serde::Serialize; - -type DbParam = web::Data>>; #[rustfmt::skip] pub fn config(cfg: &mut web::ServiceConfig) { @@ -66,40 +59,64 @@ pub fn config(cfg: &mut web::ServiceConfig) { .route("/api/v1/user/save_user_settings", web::put().to(route_post::)); } -fn perform(data: Request, db: DbParam) -> Result +fn perform( + data: Request, + db: DbPoolParam, + rate_limit_param: RateLimitParam, + chat_server: ChatServerParam, + req: HttpRequest, +) -> Result where Response: Serialize, Oper: Perform, { - let conn = match db.get() { - Ok(c) => c, - Err(e) => return Err(format_err!("{}", e)), + let ws_info = WebsocketInfo { + chatserver: chat_server.get_ref().to_owned(), + id: None, }; + + let rate_limit_info = RateLimitInfo { + rate_limiter: rate_limit_param.get_ref().to_owned(), + ip: get_ip(&req), + }; + let oper: Oper = Oper::new(data); - let response = oper.perform(&conn); - Ok(HttpResponse::Ok().json(response?)) + + let res = oper.perform( + db.get_ref().to_owned(), + Some(ws_info), + Some(rate_limit_info), + ); + + Ok(HttpResponse::Ok().json(res?)) } async fn route_get( data: web::Query, - db: DbParam, + db: DbPoolParam, + rate_limit_param: RateLimitParam, + chat_server: ChatServerParam, + req: HttpRequest, ) -> Result where Data: Serialize, Response: Serialize, Oper: Perform, { - perform::(data.0, db) + perform::(data.0, db, rate_limit_param, chat_server, req) } async fn route_post( data: web::Json, - db: DbParam, + db: DbPoolParam, + rate_limit_param: RateLimitParam, + chat_server: ChatServerParam, + req: HttpRequest, ) -> Result where Data: Serialize, Response: Serialize, Oper: Perform, { - perform::(data.0, db) + perform::(data.0, db, rate_limit_param, chat_server, req) } diff --git a/server/src/routes/federation.rs b/server/src/routes/federation.rs index ea6039d6b..bc627cb0e 100644 --- a/server/src/routes/federation.rs +++ b/server/src/routes/federation.rs @@ -1,5 +1,5 @@ +use super::*; use crate::apub; -use actix_web::web; pub fn config(cfg: &mut web::ServiceConfig) { cfg diff --git a/server/src/routes/feeds.rs b/server/src/routes/feeds.rs index 6826cf5cb..8eefe02a0 100644 --- a/server/src/routes/feeds.rs +++ b/server/src/routes/feeds.rs @@ -6,16 +6,6 @@ use crate::db::site_view::SiteView; use crate::db::user::{Claims, User_}; use crate::db::user_mention_view::{UserMentionQueryBuilder, UserMentionView}; use crate::db::{ListingType, SortType}; -use crate::{markdown_to_html, Settings}; -use actix_web::{web, HttpResponse, Result}; -use chrono::{DateTime, NaiveDateTime, Utc}; -use diesel::r2d2::{ConnectionManager, Pool}; -use diesel::PgConnection; -use failure::Error; -use rss::{CategoryBuilder, ChannelBuilder, GuidBuilder, Item, ItemBuilder}; -use serde::Deserialize; -use std::str::FromStr; -use strum::ParseError; #[derive(Deserialize)] pub struct Params { diff --git a/server/src/routes/index.rs b/server/src/routes/index.rs index 2e192df41..9f18b64d8 100644 --- a/server/src/routes/index.rs +++ b/server/src/routes/index.rs @@ -1,6 +1,4 @@ -use crate::settings::Settings; -use actix_files::NamedFile; -use actix_web::web; +use super::*; pub fn config(cfg: &mut web::ServiceConfig) { cfg diff --git a/server/src/routes/mod.rs b/server/src/routes/mod.rs index 27d9ea1be..4d018db49 100644 --- a/server/src/routes/mod.rs +++ b/server/src/routes/mod.rs @@ -1,3 +1,32 @@ +use crate::api::{Oper, Perform}; +use crate::db::site_view::SiteView; +use crate::rate_limit::{rate_limiter::RateLimiter, RateLimitInfo}; +use crate::websocket::{server::ChatServer, WebsocketInfo}; +use crate::{get_ip, markdown_to_html, version, Settings}; +use actix::prelude::*; +use actix_files::NamedFile; +use actix_web::{body::Body, web::Query, *}; +use actix_web_actors::ws; +use chrono::{DateTime, NaiveDateTime, Utc}; +use diesel::{ + r2d2::{ConnectionManager, Pool}, + PgConnection, +}; +use failure::Error; +use log::{error, info}; +use regex::Regex; +use rss::{CategoryBuilder, ChannelBuilder, GuidBuilder, Item, ItemBuilder}; +use serde::{Deserialize, Serialize}; +use serde_json::json; +use std::str::FromStr; +use std::sync::{Arc, Mutex}; +use std::time::{Duration, Instant}; +use strum::ParseError; + +pub type DbPoolParam = web::Data>>; +pub type RateLimitParam = web::Data>>; +pub type ChatServerParam = web::Data>; + pub mod api; pub mod federation; pub mod feeds; diff --git a/server/src/routes/nodeinfo.rs b/server/src/routes/nodeinfo.rs index 1263cef84..829d56b1f 100644 --- a/server/src/routes/nodeinfo.rs +++ b/server/src/routes/nodeinfo.rs @@ -1,12 +1,4 @@ -use crate::db::site_view::SiteView; -use crate::version; -use crate::Settings; -use actix_web::body::Body; -use actix_web::web; -use actix_web::HttpResponse; -use diesel::r2d2::{ConnectionManager, Pool}; -use diesel::PgConnection; -use serde::Serialize; +use super::*; pub fn config(cfg: &mut web::ServiceConfig) { cfg diff --git a/server/src/routes/webfinger.rs b/server/src/routes/webfinger.rs index 20f76a9ad..b35ddbf79 100644 --- a/server/src/routes/webfinger.rs +++ b/server/src/routes/webfinger.rs @@ -1,13 +1,5 @@ +use super::*; use crate::db::community::Community; -use crate::Settings; -use actix_web::web; -use actix_web::web::Query; -use actix_web::HttpResponse; -use diesel::r2d2::{ConnectionManager, Pool}; -use diesel::PgConnection; -use regex::Regex; -use serde::Deserialize; -use serde_json::json; #[derive(Deserialize)] pub struct Params { diff --git a/server/src/routes/websocket.rs b/server/src/routes/websocket.rs index 6c4326fd3..045858eca 100644 --- a/server/src/routes/websocket.rs +++ b/server/src/routes/websocket.rs @@ -1,10 +1,6 @@ +use super::*; use crate::websocket::server::*; -use actix::prelude::*; -use actix_web::web; -use actix_web::*; -use actix_web_actors::ws; -use log::{error, info}; -use std::time::{Duration, Instant}; +use actix_web::{Error, Result}; pub fn config(cfg: &mut web::ServiceConfig) { cfg.service(web::resource("/api/v1/ws").to(chat_route)); @@ -21,20 +17,12 @@ async fn chat_route( stream: web::Payload, chat_server: web::Data>, ) -> Result { - // TODO not sure if the blocking should be here or not ws::start( WSSession { cs_addr: chat_server.get_ref().to_owned(), id: 0, hb: Instant::now(), - ip: req - .connection_info() - .remote() - .unwrap_or("127.0.0.1:12345") - .split(':') - .next() - .unwrap_or("127.0.0.1") - .to_string(), + ip: get_ip(&req), }, &req, stream, diff --git a/server/src/websocket/mod.rs b/server/src/websocket/mod.rs index c7136423c..05d021d75 100644 --- a/server/src/websocket/mod.rs +++ b/server/src/websocket/mod.rs @@ -1,6 +1,21 @@ pub mod server; -#[derive(EnumString, ToString, Debug)] +use crate::ConnectionId; +use actix::prelude::*; +use diesel::r2d2::{ConnectionManager, Pool}; +use diesel::PgConnection; +use failure::Error; +use log::{error, info}; +use rand::{rngs::ThreadRng, Rng}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use server::ChatServer; +use std::collections::{HashMap, HashSet}; +use std::str::FromStr; +use std::sync::Arc; +use std::sync::Mutex; + +#[derive(EnumString, ToString, Debug, Clone)] pub enum UserOperation { Login, Register, @@ -49,3 +64,9 @@ pub enum UserOperation { GetSiteConfig, SaveSiteConfig, } + +#[derive(Clone)] +pub struct WebsocketInfo { + pub chatserver: Addr, + pub id: Option, +} diff --git a/server/src/websocket/server.rs b/server/src/websocket/server.rs index 0f2d2d26f..ab3bddf02 100644 --- a/server/src/websocket/server.rs +++ b/server/src/websocket/server.rs @@ -2,33 +2,16 @@ //! And manages available rooms. Peers send messages to other peers in same //! room through `ChatServer`. -use actix::prelude::*; -use diesel::r2d2::{ConnectionManager, Pool, PooledConnection}; -use diesel::PgConnection; -use failure::Error; -use log::{error, info, warn}; -use rand::{rngs::ThreadRng, Rng}; -use serde::{Deserialize, Serialize}; -use serde_json::Value; -use std::collections::{HashMap, HashSet}; -use std::str::FromStr; -use std::time::SystemTime; -use strum::IntoEnumIterator; - +use super::*; use crate::api::comment::*; use crate::api::community::*; use crate::api::post::*; use crate::api::site::*; use crate::api::user::*; use crate::api::*; +use crate::rate_limit::{rate_limiter::RateLimiter, RateLimitInfo}; use crate::websocket::UserOperation; -use crate::Settings; - -type ConnectionId = usize; -type PostId = i32; -type CommunityId = i32; -type UserId = i32; -type IPAddr = String; +use crate::{CommunityId, ConnectionId, IPAddr, PostId, UserId}; /// Chat server sends this messages to session #[derive(Message)] @@ -53,6 +36,7 @@ pub struct Disconnect { pub ip: IPAddr, } +/// The messages sent to websocket clients #[derive(Serialize, Deserialize, Message)] #[rtype(String)] pub struct StandardMessage { @@ -62,10 +46,83 @@ pub struct StandardMessage { pub msg: String, } -#[derive(Debug)] -pub struct RateLimitBucket { - last_checked: SystemTime, - allowance: f64, +#[derive(Message)] +#[rtype(result = "()")] +pub struct SendAllMessage { + pub op: UserOperation, + pub response: Response, + pub my_id: Option, +} + +#[derive(Message)] +#[rtype(result = "()")] +pub struct SendUserRoomMessage { + pub op: UserOperation, + pub response: Response, + pub recipient_id: UserId, + pub my_id: Option, +} + +#[derive(Message)] +#[rtype(result = "()")] +pub struct SendCommunityRoomMessage { + pub op: UserOperation, + pub response: Response, + pub community_id: CommunityId, + pub my_id: Option, +} + +#[derive(Message)] +#[rtype(result = "()")] +pub struct SendPost { + pub op: UserOperation, + pub post: PostResponse, + pub my_id: Option, +} + +#[derive(Message)] +#[rtype(result = "()")] +pub struct SendComment { + pub op: UserOperation, + pub comment: CommentResponse, + pub my_id: Option, +} + +#[derive(Message)] +#[rtype(result = "()")] +pub struct JoinUserRoom { + pub user_id: UserId, + pub id: ConnectionId, +} + +#[derive(Message)] +#[rtype(result = "()")] +pub struct JoinCommunityRoom { + pub community_id: CommunityId, + pub id: ConnectionId, +} + +#[derive(Message)] +#[rtype(result = "()")] +pub struct JoinPostRoom { + pub post_id: PostId, + pub id: ConnectionId, +} + +#[derive(Message)] +#[rtype(usize)] +pub struct GetUsersOnline; + +#[derive(Message)] +#[rtype(usize)] +pub struct GetPostUsersOnline { + pub post_id: PostId, +} + +#[derive(Message)] +#[rtype(usize)] +pub struct GetCommunityUsersOnline { + pub community_id: CommunityId, } pub struct SessionInfo { @@ -73,50 +130,48 @@ pub struct SessionInfo { pub ip: IPAddr, } -#[derive(Eq, PartialEq, Hash, Debug, EnumIter, Copy, Clone)] -pub enum RateLimitType { - Message, - Register, - Post, -} - /// `ChatServer` manages chat rooms and responsible for coordinating chat /// session. pub struct ChatServer { /// A map from generated random ID to session addr - sessions: HashMap, + pub sessions: HashMap, /// A map from post_id to set of connectionIDs - post_rooms: HashMap>, + pub post_rooms: HashMap>, /// A map from community to set of connectionIDs - community_rooms: HashMap>, + pub community_rooms: HashMap>, /// A map from user id to its connection ID for joined users. Remember a user can have multiple /// sessions (IE clients) user_rooms: HashMap>, - /// Rate limiting based on rate type and IP addr - rate_limit_buckets: HashMap>, - rng: ThreadRng, - db: Pool>, + + /// The DB Pool + pool: Pool>, + + /// Rate limiting based on rate type and IP addr + rate_limiter: Arc>, } impl ChatServer { - pub fn startup(db: Pool>) -> ChatServer { + pub fn startup( + pool: Pool>, + rate_limiter: Arc>, + ) -> ChatServer { ChatServer { sessions: HashMap::new(), - rate_limit_buckets: HashMap::new(), post_rooms: HashMap::new(), community_rooms: HashMap::new(), user_rooms: HashMap::new(), rng: rand::thread_rng(), - db, + pool, + rate_limiter, } } - fn join_community_room(&mut self, community_id: CommunityId, id: ConnectionId) { + pub fn join_community_room(&mut self, community_id: CommunityId, id: ConnectionId) { // remove session from all rooms for sessions in self.community_rooms.values_mut() { sessions.remove(&id); @@ -140,7 +195,7 @@ impl ChatServer { .insert(id); } - fn join_post_room(&mut self, post_id: PostId, id: ConnectionId) { + pub fn join_post_room(&mut self, post_id: PostId, id: ConnectionId) { // remove session from all rooms for sessions in self.post_rooms.values_mut() { sessions.remove(&id); @@ -160,7 +215,7 @@ impl ChatServer { self.post_rooms.get_mut(&post_id).unwrap().insert(id); } - fn join_user_room(&mut self, user_id: UserId, id: ConnectionId) { + pub fn join_user_room(&mut self, user_id: UserId, id: ConnectionId) { // remove session from all rooms for sessions in self.user_rooms.values_mut() { sessions.remove(&id); @@ -174,200 +229,508 @@ impl ChatServer { self.user_rooms.get_mut(&user_id).unwrap().insert(id); } - fn send_post_room_message(&self, post_id: PostId, message: &str, skip_id: ConnectionId) { + fn send_post_room_message( + &self, + op: &UserOperation, + response: &Response, + post_id: PostId, + my_id: Option, + ) -> Result<(), Error> + where + Response: Serialize, + { + let res_str = &to_json_string(op, response)?; if let Some(sessions) = self.post_rooms.get(&post_id) { for id in sessions { - if *id != skip_id { - if let Some(info) = self.sessions.get(id) { - let _ = info.addr.do_send(WSMessage(message.to_owned())); + if let Some(my_id) = my_id { + if *id == my_id { + continue; } } + self.sendit(res_str, *id); } } + Ok(()) } - fn send_community_room_message( + pub fn send_community_room_message( &self, + op: &UserOperation, + response: &Response, community_id: CommunityId, - message: &str, - skip_id: ConnectionId, - ) { + my_id: Option, + ) -> Result<(), Error> + where + Response: Serialize, + { + let res_str = &to_json_string(op, response)?; if let Some(sessions) = self.community_rooms.get(&community_id) { for id in sessions { - if *id != skip_id { - if let Some(info) = self.sessions.get(id) { - let _ = info.addr.do_send(WSMessage(message.to_owned())); + if let Some(my_id) = my_id { + if *id == my_id { + continue; } } + self.sendit(res_str, *id); } } + Ok(()) } - fn send_user_room_message(&self, user_id: UserId, message: &str, skip_id: ConnectionId) { - if let Some(sessions) = self.user_rooms.get(&user_id) { - for id in sessions { - if *id != skip_id { - if let Some(info) = self.sessions.get(id) { - let _ = info.addr.do_send(WSMessage(message.to_owned())); - } - } - } - } - } - - fn send_all_message(&self, message: &str, skip_id: ConnectionId) { - for id in self.sessions.keys() { - if *id != skip_id { - if let Some(info) = self.sessions.get(id) { - let _ = info.addr.do_send(WSMessage(message.to_owned())); - } - } - } - } - - fn comment_sends( + pub fn send_all_message( &self, - user_operation: UserOperation, - comment: CommentResponse, - id: ConnectionId, - ) -> Result { + op: &UserOperation, + response: &Response, + my_id: Option, + ) -> Result<(), Error> + where + Response: Serialize, + { + let res_str = &to_json_string(op, response)?; + for id in self.sessions.keys() { + if let Some(my_id) = my_id { + if *id == my_id { + continue; + } + } + self.sendit(res_str, *id); + } + Ok(()) + } + + pub fn send_user_room_message( + &self, + op: &UserOperation, + response: &Response, + recipient_id: UserId, + my_id: Option, + ) -> Result<(), Error> + where + Response: Serialize, + { + let res_str = &to_json_string(op, response)?; + if let Some(sessions) = self.user_rooms.get(&recipient_id) { + for id in sessions { + if let Some(my_id) = my_id { + if *id == my_id { + continue; + } + } + self.sendit(res_str, *id); + } + } + Ok(()) + } + + pub fn send_comment( + &self, + user_operation: &UserOperation, + comment: &CommentResponse, + my_id: Option, + ) -> Result<(), Error> { let mut comment_reply_sent = comment.clone(); comment_reply_sent.comment.my_vote = None; comment_reply_sent.comment.user_id = None; - // For the post room ones, and the directs back to the user - // strip out the recipient_ids, so that - // users don't get double notifs - let mut comment_user_sent = comment.clone(); - comment_user_sent.recipient_ids = Vec::new(); - let mut comment_post_sent = comment_reply_sent.clone(); comment_post_sent.recipient_ids = Vec::new(); - let comment_reply_sent_str = to_json_string(&user_operation, &comment_reply_sent)?; - let comment_post_sent_str = to_json_string(&user_operation, &comment_post_sent)?; - let comment_user_sent_str = to_json_string(&user_operation, &comment_user_sent)?; - // Send it to the post room - self.send_post_room_message(comment.comment.post_id, &comment_post_sent_str, id); + self.send_post_room_message( + user_operation, + &comment_post_sent, + comment_post_sent.comment.post_id, + my_id, + )?; // Send it to the recipient(s) including the mentioned users - for recipient_id in comment_reply_sent.recipient_ids { - self.send_user_room_message(recipient_id, &comment_reply_sent_str, id); + for recipient_id in &comment_reply_sent.recipient_ids { + self.send_user_room_message(user_operation, &comment_reply_sent, *recipient_id, my_id)?; } // Send it to the community too - self.send_community_room_message(0, &comment_post_sent_str, id); - self.send_community_room_message(comment.comment.community_id, &comment_post_sent_str, id); + self.send_community_room_message(user_operation, &comment_post_sent, 0, my_id)?; + self.send_community_room_message( + user_operation, + &comment_post_sent, + comment.comment.community_id, + my_id, + )?; - Ok(comment_user_sent_str) + Ok(()) } - fn post_sends( + pub fn send_post( &self, - user_operation: UserOperation, - post: PostResponse, - id: ConnectionId, - ) -> Result { + user_operation: &UserOperation, + post: &PostResponse, + my_id: Option, + ) -> Result<(), Error> { let community_id = post.post.community_id; // Don't send my data with it let mut post_sent = post.clone(); post_sent.post.my_vote = None; post_sent.post.user_id = None; - let post_sent_str = to_json_string(&user_operation, &post_sent)?; // Send it to /c/all and that community - self.send_community_room_message(0, &post_sent_str, id); - self.send_community_room_message(community_id, &post_sent_str, id); + self.send_community_room_message(user_operation, &post_sent, 0, my_id)?; + self.send_community_room_message(user_operation, &post_sent, community_id, my_id)?; // Send it to the post room - self.send_post_room_message(post_sent.post.id, &post_sent_str, id); + self.send_post_room_message(user_operation, &post_sent, post.post.id, my_id)?; - to_json_string(&user_operation, post) + Ok(()) } - fn check_rate_limit_register(&mut self, id: usize, check_only: bool) -> Result<(), Error> { - self.check_rate_limit_full( - RateLimitType::Register, - id, - Settings::get().rate_limit.register, - Settings::get().rate_limit.register_per_second, - check_only, - ) - } - - fn check_rate_limit_post(&mut self, id: usize, check_only: bool) -> Result<(), Error> { - self.check_rate_limit_full( - RateLimitType::Post, - id, - Settings::get().rate_limit.post, - Settings::get().rate_limit.post_per_second, - check_only, - ) - } - - fn check_rate_limit_message(&mut self, id: usize, check_only: bool) -> Result<(), Error> { - self.check_rate_limit_full( - RateLimitType::Message, - id, - Settings::get().rate_limit.message, - Settings::get().rate_limit.message_per_second, - check_only, - ) - } - - #[allow(clippy::float_cmp)] - fn check_rate_limit_full( - &mut self, - type_: RateLimitType, - id: usize, - rate: i32, - per: i32, - check_only: bool, - ) -> Result<(), Error> { + fn sendit(&self, message: &str, id: ConnectionId) { if let Some(info) = self.sessions.get(&id) { - if let Some(bucket) = self.rate_limit_buckets.get_mut(&type_) { - if let Some(rate_limit) = bucket.get_mut(&info.ip) { - let current = SystemTime::now(); - let time_passed = current.duration_since(rate_limit.last_checked)?.as_secs() as f64; + let _ = info.addr.do_send(WSMessage(message.to_owned())); + } + } - // The initial value - if rate_limit.allowance == -2f64 { - rate_limit.allowance = rate as f64; - }; + fn do_user_operation<'a, Data, Response>( + &self, + id: ConnectionId, + ip: IPAddr, + op: UserOperation, + data: &str, + ctx: &mut Context, + ) -> Result + where + for<'de> Data: Deserialize<'de> + 'a, + Response: Serialize, + Oper: Perform, + { + let parsed_data: Data = serde_json::from_str(data)?; - rate_limit.last_checked = current; - rate_limit.allowance += time_passed * (rate as f64 / per as f64); - if !check_only && rate_limit.allowance > rate as f64 { - rate_limit.allowance = rate as f64; - } + let ws_info = WebsocketInfo { + chatserver: ctx.address(), + id: Some(id), + }; - if rate_limit.allowance < 1.0 { - warn!( - "Rate limited IP: {}, time_passed: {}, allowance: {}", - &info.ip, time_passed, rate_limit.allowance - ); - Err( - APIError { - message: format!("Too many requests. {} per {} seconds", rate, per), - } - .into(), - ) - } else { - if !check_only { - rate_limit.allowance -= 1.0; - } - Ok(()) - } - } else { - Ok(()) - } - } else { - Ok(()) + let rate_limit_info = RateLimitInfo { + rate_limiter: self.rate_limiter.clone(), + ip, + }; + + let new_pool = self.pool.clone(); + let res = Oper::new(parsed_data).perform(new_pool, Some(ws_info), Some(rate_limit_info))?; + to_json_string(&op, &res) + } + + fn parse_json_message( + &mut self, + msg: StandardMessage, + ctx: &mut Context, + ) -> Result { + let json: Value = serde_json::from_str(&msg.msg)?; + let data = &json["data"].to_string(); + let op = &json["op"].as_str().ok_or(APIError { + message: "Unknown op type".to_string(), + })?; + + let user_operation: UserOperation = UserOperation::from_str(&op)?; + + let ip: IPAddr = match self.sessions.get(&msg.id) { + Some(info) => info.ip.to_owned(), + None => "blank_ip".to_string(), + }; + + match user_operation { + // User ops + UserOperation::Login => { + self.do_user_operation::(msg.id, ip, user_operation, data, ctx) } - } else { - Ok(()) + UserOperation::Register => { + self.do_user_operation::(msg.id, ip, user_operation, data, ctx) + } + UserOperation::GetUserDetails => self + .do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), + UserOperation::GetReplies => self.do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), + UserOperation::AddAdmin => { + self.do_user_operation::(msg.id, ip, user_operation, data, ctx) + } + UserOperation::BanUser => { + self.do_user_operation::(msg.id, ip, user_operation, data, ctx) + } + UserOperation::GetUserMentions => self + .do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), + UserOperation::EditUserMention => self + .do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), + UserOperation::MarkAllAsRead => self.do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), + UserOperation::DeleteAccount => self.do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), + UserOperation::PasswordReset => self + .do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), + UserOperation::PasswordChange => self.do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), + UserOperation::CreatePrivateMessage => self + .do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), + UserOperation::EditPrivateMessage => self + .do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), + UserOperation::GetPrivateMessages => self + .do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), + UserOperation::UserJoin => { + self.do_user_operation::(msg.id, ip, user_operation, data, ctx) + } + UserOperation::SaveUserSettings => self.do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), + + // Site ops + UserOperation::GetModlog => self.do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), + UserOperation::CreateSite => { + self.do_user_operation::(msg.id, ip, user_operation, data, ctx) + } + UserOperation::EditSite => { + self.do_user_operation::(msg.id, ip, user_operation, data, ctx) + } + UserOperation::GetSite => { + self.do_user_operation::(msg.id, ip, user_operation, data, ctx) + } + UserOperation::GetSiteConfig => self + .do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), + UserOperation::SaveSiteConfig => self + .do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), + UserOperation::Search => { + self.do_user_operation::(msg.id, ip, user_operation, data, ctx) + } + UserOperation::TransferCommunity => self + .do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), + UserOperation::TransferSite => self.do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), + UserOperation::ListCategories => self + .do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), + + // Community ops + UserOperation::GetCommunity => self.do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), + UserOperation::ListCommunities => self + .do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), + UserOperation::CreateCommunity => self + .do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), + UserOperation::EditCommunity => self.do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), + UserOperation::FollowCommunity => self + .do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), + UserOperation::GetFollowedCommunities => self + .do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), + UserOperation::BanFromCommunity => self + .do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), + UserOperation::AddModToCommunity => self + .do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), + + // Post ops + UserOperation::CreatePost => { + self.do_user_operation::(msg.id, ip, user_operation, data, ctx) + } + UserOperation::GetPost => { + self.do_user_operation::(msg.id, ip, user_operation, data, ctx) + } + UserOperation::GetPosts => { + self.do_user_operation::(msg.id, ip, user_operation, data, ctx) + } + UserOperation::EditPost => { + self.do_user_operation::(msg.id, ip, user_operation, data, ctx) + } + UserOperation::CreatePostLike => self.do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), + UserOperation::SavePost => { + self.do_user_operation::(msg.id, ip, user_operation, data, ctx) + } + + // Comment ops + UserOperation::CreateComment => self.do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), + UserOperation::EditComment => self.do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), + UserOperation::SaveComment => self.do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), + UserOperation::GetComments => self.do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), + UserOperation::CreateCommentLike => self + .do_user_operation::( + msg.id, + ip, + user_operation, + data, + ctx, + ), } } } @@ -398,26 +761,6 @@ impl Handler for ChatServer { }, ); - for rate_limit_type in RateLimitType::iter() { - if self.rate_limit_buckets.get(&rate_limit_type).is_none() { - self - .rate_limit_buckets - .insert(rate_limit_type, HashMap::new()); - } - - if let Some(bucket) = self.rate_limit_buckets.get_mut(&rate_limit_type) { - if bucket.get(&msg.ip).is_none() { - bucket.insert( - msg.ip.to_owned(), - RateLimitBucket { - last_checked: SystemTime::now(), - allowance: -2f64, - }, - ); - } - } - } - id } } @@ -448,8 +791,8 @@ impl Handler for ChatServer { impl Handler for ChatServer { type Result = MessageResult; - fn handle(&mut self, msg: StandardMessage, _: &mut Context) -> Self::Result { - match parse_json_message(self, msg) { + fn handle(&mut self, msg: StandardMessage, ctx: &mut Context) -> Self::Result { + match self.parse_json_message(msg, ctx) { Ok(m) => { info!("Message Sent: {}", m); MessageResult(m) @@ -462,15 +805,126 @@ impl Handler for ChatServer { } } +impl Handler> for ChatServer +where + Response: Serialize, +{ + type Result = (); + + fn handle(&mut self, msg: SendAllMessage, _: &mut Context) { + self + .send_all_message(&msg.op, &msg.response, msg.my_id) + .unwrap(); + } +} + +impl Handler> for ChatServer +where + Response: Serialize, +{ + type Result = (); + + fn handle(&mut self, msg: SendUserRoomMessage, _: &mut Context) { + self + .send_user_room_message(&msg.op, &msg.response, msg.recipient_id, msg.my_id) + .unwrap(); + } +} + +impl Handler> for ChatServer +where + Response: Serialize, +{ + type Result = (); + + fn handle(&mut self, msg: SendCommunityRoomMessage, _: &mut Context) { + self + .send_community_room_message(&msg.op, &msg.response, msg.community_id, msg.my_id) + .unwrap(); + } +} + +impl Handler for ChatServer { + type Result = (); + + fn handle(&mut self, msg: SendPost, _: &mut Context) { + self.send_post(&msg.op, &msg.post, msg.my_id).unwrap(); + } +} + +impl Handler for ChatServer { + type Result = (); + + fn handle(&mut self, msg: SendComment, _: &mut Context) { + self.send_comment(&msg.op, &msg.comment, msg.my_id).unwrap(); + } +} + +impl Handler for ChatServer { + type Result = (); + + fn handle(&mut self, msg: JoinUserRoom, _: &mut Context) { + self.join_user_room(msg.user_id, msg.id); + } +} + +impl Handler for ChatServer { + type Result = (); + + fn handle(&mut self, msg: JoinCommunityRoom, _: &mut Context) { + self.join_community_room(msg.community_id, msg.id); + } +} + +impl Handler for ChatServer { + type Result = (); + + fn handle(&mut self, msg: JoinPostRoom, _: &mut Context) { + self.join_post_room(msg.post_id, msg.id); + } +} + +impl Handler for ChatServer { + type Result = usize; + + fn handle(&mut self, _msg: GetUsersOnline, _: &mut Context) -> Self::Result { + self.sessions.len() + } +} + +impl Handler for ChatServer { + type Result = usize; + + fn handle(&mut self, msg: GetPostUsersOnline, _: &mut Context) -> Self::Result { + if let Some(users) = self.post_rooms.get(&msg.post_id) { + users.len() + } else { + 0 + } + } +} + +impl Handler for ChatServer { + type Result = usize; + + fn handle(&mut self, msg: GetCommunityUsersOnline, _: &mut Context) -> Self::Result { + if let Some(users) = self.community_rooms.get(&msg.community_id) { + users.len() + } else { + 0 + } + } +} + #[derive(Serialize)] struct WebsocketResponse { op: String, data: T, } -fn to_json_string(op: &UserOperation, data: T) -> Result +fn to_json_string(op: &UserOperation, data: &Response) -> Result where - T: Serialize, + Response: Serialize, { let response = WebsocketResponse { op: op.to_string(), @@ -478,284 +932,3 @@ where }; Ok(serde_json::to_string(&response)?) } - -fn do_user_operation<'a, Data, Response>( - op: UserOperation, - data: &str, - conn: &PooledConnection>, -) -> Result -where - for<'de> Data: Deserialize<'de> + 'a, - Response: Serialize, - Oper: Perform, -{ - let parsed_data: Data = serde_json::from_str(data)?; - let res = Oper::new(parsed_data).perform(&conn)?; - to_json_string(&op, &res) -} - -fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result { - let json: Value = serde_json::from_str(&msg.msg)?; - let data = &json["data"].to_string(); - let op = &json["op"].as_str().ok_or(APIError { - message: "Unknown op type".to_string(), - })?; - - let conn = chat.db.get()?; - - let user_operation: UserOperation = UserOperation::from_str(&op)?; - - // TODO: none of the chat messages are going to work if stuff is submitted via http api, - // need to move that handling elsewhere - - // A DDOS check - chat.check_rate_limit_message(msg.id, false)?; - - match user_operation { - UserOperation::Login => do_user_operation::(user_operation, data, &conn), - UserOperation::Register => { - chat.check_rate_limit_register(msg.id, true)?; - let register: Register = serde_json::from_str(data)?; - let res = Oper::new(register).perform(&conn)?; - chat.check_rate_limit_register(msg.id, false)?; - to_json_string(&user_operation, &res) - } - UserOperation::GetUserDetails => { - do_user_operation::(user_operation, data, &conn) - } - UserOperation::SaveUserSettings => { - do_user_operation::(user_operation, data, &conn) - } - UserOperation::AddAdmin => { - let add_admin: AddAdmin = serde_json::from_str(data)?; - let res = Oper::new(add_admin).perform(&conn)?; - let res_str = to_json_string(&user_operation, &res)?; - chat.send_all_message(&res_str, msg.id); - Ok(res_str) - } - UserOperation::BanUser => { - let ban_user: BanUser = serde_json::from_str(data)?; - let res = Oper::new(ban_user).perform(&conn)?; - let res_str = to_json_string(&user_operation, &res)?; - chat.send_all_message(&res_str, msg.id); - Ok(res_str) - } - UserOperation::GetReplies => { - do_user_operation::(user_operation, data, &conn) - } - UserOperation::GetUserMentions => { - do_user_operation::(user_operation, data, &conn) - } - UserOperation::EditUserMention => { - do_user_operation::(user_operation, data, &conn) - } - UserOperation::MarkAllAsRead => { - do_user_operation::(user_operation, data, &conn) - } - UserOperation::GetCommunity => { - let get_community: GetCommunity = serde_json::from_str(data)?; - let mut res = Oper::new(get_community).perform(&conn)?; - let community_id = res.community.id; - - chat.join_community_room(community_id, msg.id); - - res.online = if let Some(community_users) = chat.community_rooms.get(&community_id) { - community_users.len() - } else { - 0 - }; - - to_json_string(&user_operation, &res) - } - UserOperation::ListCommunities => { - do_user_operation::(user_operation, data, &conn) - } - UserOperation::CreateCommunity => { - chat.check_rate_limit_register(msg.id, true)?; - let create_community: CreateCommunity = serde_json::from_str(data)?; - let res = Oper::new(create_community).perform(&conn)?; - chat.check_rate_limit_register(msg.id, false)?; - to_json_string(&user_operation, &res) - } - UserOperation::EditCommunity => { - let edit_community: EditCommunity = serde_json::from_str(data)?; - let res = Oper::new(edit_community).perform(&conn)?; - let mut community_sent: CommunityResponse = res.clone(); - community_sent.community.user_id = None; - community_sent.community.subscribed = None; - let community_sent_str = to_json_string(&user_operation, &community_sent)?; - chat.send_community_room_message(community_sent.community.id, &community_sent_str, msg.id); - to_json_string(&user_operation, &res) - } - UserOperation::FollowCommunity => { - do_user_operation::(user_operation, data, &conn) - } - UserOperation::GetFollowedCommunities => do_user_operation::< - GetFollowedCommunities, - GetFollowedCommunitiesResponse, - >(user_operation, data, &conn), - UserOperation::BanFromCommunity => { - let ban_from_community: BanFromCommunity = serde_json::from_str(data)?; - let community_id = ban_from_community.community_id; - let res = Oper::new(ban_from_community).perform(&conn)?; - let res_str = to_json_string(&user_operation, &res)?; - chat.send_community_room_message(community_id, &res_str, msg.id); - Ok(res_str) - } - UserOperation::AddModToCommunity => { - let mod_add_to_community: AddModToCommunity = serde_json::from_str(data)?; - let community_id = mod_add_to_community.community_id; - let res = Oper::new(mod_add_to_community).perform(&conn)?; - let res_str = to_json_string(&user_operation, &res)?; - chat.send_community_room_message(community_id, &res_str, msg.id); - Ok(res_str) - } - UserOperation::ListCategories => { - do_user_operation::(user_operation, data, &conn) - } - UserOperation::GetPost => { - let get_post: GetPost = serde_json::from_str(data)?; - let post_id = get_post.id; - chat.join_post_room(post_id, msg.id); - let mut res = Oper::new(get_post).perform(&conn)?; - - res.online = if let Some(post_users) = chat.post_rooms.get(&post_id) { - post_users.len() - } else { - 0 - }; - - to_json_string(&user_operation, &res) - } - UserOperation::GetPosts => { - let get_posts: GetPosts = serde_json::from_str(data)?; - if get_posts.community_id.is_none() { - // 0 is the "all" community - chat.join_community_room(0, msg.id); - } - let res = Oper::new(get_posts).perform(&conn)?; - to_json_string(&user_operation, &res) - } - UserOperation::GetComments => { - let get_comments: GetComments = serde_json::from_str(data)?; - if get_comments.community_id.is_none() { - // 0 is the "all" community - chat.join_community_room(0, msg.id); - } - let res = Oper::new(get_comments).perform(&conn)?; - to_json_string(&user_operation, &res) - } - UserOperation::CreatePost => { - chat.check_rate_limit_post(msg.id, true)?; - let create_post: CreatePost = serde_json::from_str(data)?; - let res = Oper::new(create_post).perform(&conn)?; - chat.check_rate_limit_post(msg.id, false)?; - - chat.post_sends(UserOperation::CreatePost, res, msg.id) - } - UserOperation::CreatePostLike => { - let create_post_like: CreatePostLike = serde_json::from_str(data)?; - let res = Oper::new(create_post_like).perform(&conn)?; - - chat.post_sends(UserOperation::CreatePostLike, res, msg.id) - } - UserOperation::EditPost => { - let edit_post: EditPost = serde_json::from_str(data)?; - let res = Oper::new(edit_post).perform(&conn)?; - - chat.post_sends(UserOperation::EditPost, res, msg.id) - } - UserOperation::SavePost => { - do_user_operation::(user_operation, data, &conn) - } - UserOperation::CreateComment => { - let create_comment: CreateComment = serde_json::from_str(data)?; - let res = Oper::new(create_comment).perform(&conn)?; - - chat.comment_sends(UserOperation::CreateComment, res, msg.id) - } - UserOperation::EditComment => { - let edit_comment: EditComment = serde_json::from_str(data)?; - let res = Oper::new(edit_comment).perform(&conn)?; - - chat.comment_sends(UserOperation::EditComment, res, msg.id) - } - UserOperation::SaveComment => { - do_user_operation::(user_operation, data, &conn) - } - UserOperation::CreateCommentLike => { - let create_comment_like: CreateCommentLike = serde_json::from_str(data)?; - let res = Oper::new(create_comment_like).perform(&conn)?; - - chat.comment_sends(UserOperation::CreateCommentLike, res, msg.id) - } - UserOperation::GetModlog => { - do_user_operation::(user_operation, data, &conn) - } - UserOperation::CreateSite => { - do_user_operation::(user_operation, data, &conn) - } - UserOperation::EditSite => { - let edit_site: EditSite = serde_json::from_str(data)?; - let res = Oper::new(edit_site).perform(&conn)?; - let res_str = to_json_string(&user_operation, &res)?; - chat.send_all_message(&res_str, msg.id); - Ok(res_str) - } - UserOperation::GetSite => { - let get_site: GetSite = serde_json::from_str(data)?; - let mut res = Oper::new(get_site).perform(&conn)?; - res.online = chat.sessions.len(); - to_json_string(&user_operation, &res) - } - UserOperation::GetSiteConfig => { - let get_site_config: GetSiteConfig = serde_json::from_str(data)?; - let res = Oper::new(get_site_config).perform(&conn)?; - to_json_string(&user_operation, &res) - } - UserOperation::SaveSiteConfig => { - let save_site_config: SaveSiteConfig = serde_json::from_str(data)?; - let res = Oper::new(save_site_config).perform(&conn)?; - to_json_string(&user_operation, &res) - } - UserOperation::Search => { - do_user_operation::(user_operation, data, &conn) - } - UserOperation::TransferCommunity => { - do_user_operation::(user_operation, data, &conn) - } - UserOperation::TransferSite => { - do_user_operation::(user_operation, data, &conn) - } - UserOperation::DeleteAccount => { - do_user_operation::(user_operation, data, &conn) - } - UserOperation::PasswordReset => { - do_user_operation::(user_operation, data, &conn) - } - UserOperation::PasswordChange => { - do_user_operation::(user_operation, data, &conn) - } - UserOperation::CreatePrivateMessage => { - let create_private_message: CreatePrivateMessage = serde_json::from_str(data)?; - let recipient_id = create_private_message.recipient_id; - let res = Oper::new(create_private_message).perform(&conn)?; - let res_str = to_json_string(&user_operation, &res)?; - - chat.send_user_room_message(recipient_id, &res_str, msg.id); - Ok(res_str) - } - UserOperation::EditPrivateMessage => { - do_user_operation::(user_operation, data, &conn) - } - UserOperation::GetPrivateMessages => { - do_user_operation::(user_operation, data, &conn) - } - UserOperation::UserJoin => { - let user_join: UserJoin = serde_json::from_str(data)?; - let res = Oper::new(user_join).perform(&conn)?; - chat.join_user_room(res.user_id, msg.id); - to_json_string(&user_operation, &res) - } - } -} From fa8fcb0f8b4ab65f995794e78a42b95a872e5a62 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Sun, 19 Apr 2020 20:01:58 -0400 Subject: [PATCH 06/20] Adding to sponsors list. --- ui/src/components/sponsors.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/components/sponsors.tsx b/ui/src/components/sponsors.tsx index cd7e14c5b..cdd7bdad3 100644 --- a/ui/src/components/sponsors.tsx +++ b/ui/src/components/sponsors.tsx @@ -10,7 +10,7 @@ let general = [ 'Andre Vallestero', 'NotTooHighToHack', ]; -let highlighted = ['Alex Benishek']; +let highlighted = ['Oskenso Kashi', 'Alex Benishek']; // let silver = []; // let gold = []; // let latinum = []; From ac43b86b6063ef1ac876122c390de83d6b34a8e6 Mon Sep 17 00:00:00 2001 From: asonix Date: Sun, 19 Apr 2020 22:59:07 -0500 Subject: [PATCH 07/20] Change RateLimit to act as a middleware --- server/Cargo.lock | 2 + server/Cargo.toml | 2 + server/src/api/comment.rs | 60 +- server/src/api/community.rs | 115 +--- server/src/api/mod.rs | 10 +- server/src/api/post.rs | 79 +-- server/src/api/site.rs | 115 +--- server/src/api/user.rs | 204 ++---- server/src/main.rs | 14 +- server/src/rate_limit/mod.rs | 173 +++++- server/src/rate_limit/rate_limiter.rs | 2 +- server/src/routes/api.rs | 195 +++--- server/src/routes/mod.rs | 2 +- server/src/routes/websocket.rs | 7 +- server/src/websocket/mod.rs | 2 - server/src/websocket/server.rs | 861 ++++++++++++++++---------- 16 files changed, 962 insertions(+), 881 deletions(-) diff --git a/server/Cargo.lock b/server/Cargo.lock index a33211ddc..a83f65935 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -1410,6 +1410,7 @@ dependencies = [ "dotenv 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "hjson 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "isahc 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1427,6 +1428,7 @@ dependencies = [ "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "strum 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", "strum_macros 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/server/Cargo.toml b/server/Cargo.toml index 5a4fdcece..e15e90bf2 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -37,3 +37,5 @@ hjson = "0.8.2" percent-encoding = "2.1.0" isahc = "0.9" comrak = "0.7" +tokio = "0.2.18" +futures = "0.3.4" diff --git a/server/src/api/comment.rs b/server/src/api/comment.rs index 8e398c9ac..058c72674 100644 --- a/server/src/api/comment.rs +++ b/server/src/api/comment.rs @@ -59,12 +59,13 @@ pub struct GetCommentsResponse { comments: Vec, } -impl Perform for Oper { +impl Perform for Oper { + type Response = CommentResponse; + fn perform( &self, pool: Pool>, websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &CreateComment = &self.data; @@ -77,13 +78,6 @@ impl Perform for Oper { let hostname = &format!("https://{}", Settings::get().hostname); - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; // Check for a community ban @@ -253,12 +247,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = CommentResponse; + fn perform( &self, pool: Pool>, websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &EditComment = &self.data; @@ -269,13 +264,6 @@ impl Perform for Oper { let user_id = claims.id; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; let orig_comment = CommentView::read(&conn, data.edit_id, None)?; @@ -411,12 +399,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = CommentResponse; + fn perform( &self, pool: Pool>, _websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &SaveComment = &self.data; @@ -432,13 +421,6 @@ impl Perform for Oper { user_id, }; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; if data.save { @@ -462,12 +444,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = CommentResponse; + fn perform( &self, pool: Pool>, websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &CreateCommentLike = &self.data; @@ -480,13 +463,6 @@ impl Perform for Oper { let mut recipient_ids = Vec::new(); - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; // Don't do a downvote if site has downvotes disabled @@ -567,12 +543,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = GetCommentsResponse; + fn perform( &self, pool: Pool>, websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &GetComments = &self.data; @@ -592,13 +569,6 @@ impl Perform for Oper { let type_ = ListingType::from_str(&data.type_)?; let sort = SortType::from_str(&data.sort)?; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; let comments = match CommentQueryBuilder::create(&conn) diff --git a/server/src/api/community.rs b/server/src/api/community.rs index 0f4376939..df03546cf 100644 --- a/server/src/api/community.rs +++ b/server/src/api/community.rs @@ -111,12 +111,13 @@ pub struct TransferCommunity { auth: String, } -impl Perform for Oper { +impl Perform for Oper { + type Response = GetCommunityResponse; + fn perform( &self, pool: Pool>, websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &GetCommunity = &self.data; @@ -131,13 +132,6 @@ impl Perform for Oper { None => None, }; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; let community_id = match data.id { @@ -197,12 +191,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = CommunityResponse; + fn perform( &self, pool: Pool>, _websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &CreateCommunity = &self.data; @@ -227,13 +222,6 @@ impl Perform for Oper { let user_id = claims.id; - if let Some(rl) = &rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_register(&rl.ip, true)?; - } - let conn = pool.get()?; // Check for a site ban @@ -283,25 +271,19 @@ impl Perform for Oper { let community_view = CommunityView::read(&conn, inserted_community.id, Some(user_id))?; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_register(&rl.ip, false)?; - } - Ok(CommunityResponse { community: community_view, }) } } -impl Perform for Oper { +impl Perform for Oper { + type Response = CommunityResponse; + fn perform( &self, pool: Pool>, websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &EditCommunity = &self.data; @@ -326,13 +308,6 @@ impl Perform for Oper { let user_id = claims.id; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; // Check for a site ban @@ -410,12 +385,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = ListCommunitiesResponse; + fn perform( &self, pool: Pool>, _websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &ListCommunities = &self.data; @@ -439,13 +415,6 @@ impl Perform for Oper { let sort = SortType::from_str(&data.sort)?; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; let communities = CommunityQueryBuilder::create(&conn) @@ -461,12 +430,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = CommunityResponse; + fn perform( &self, pool: Pool>, _websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &FollowCommunity = &self.data; @@ -482,13 +452,6 @@ impl Perform for Oper { user_id, }; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; if data.follow { @@ -511,12 +474,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = GetFollowedCommunitiesResponse; + fn perform( &self, pool: Pool>, _websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &GetFollowedCommunities = &self.data; @@ -527,13 +491,6 @@ impl Perform for Oper { let user_id = claims.id; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; let communities: Vec = @@ -547,12 +504,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = BanFromCommunityResponse; + fn perform( &self, pool: Pool>, websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &BanFromCommunity = &self.data; @@ -568,13 +526,6 @@ impl Perform for Oper { user_id: data.user_id, }; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; if data.ban { @@ -625,12 +576,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = AddModToCommunityResponse; + fn perform( &self, pool: Pool>, websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &AddModToCommunity = &self.data; @@ -646,13 +598,6 @@ impl Perform for Oper { user_id: data.user_id, }; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; if data.added { @@ -693,12 +638,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = GetCommunityResponse; + fn perform( &self, pool: Pool>, _websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &TransferCommunity = &self.data; @@ -709,13 +655,6 @@ impl Perform for Oper { let user_id = claims.id; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; let read_community = Community::read(&conn, data.community_id)?; diff --git a/server/src/api/mod.rs b/server/src/api/mod.rs index e40d122c8..aab00c047 100644 --- a/server/src/api/mod.rs +++ b/server/src/api/mod.rs @@ -22,7 +22,6 @@ use crate::{ naive_now, remove_slurs, send_email, slur_check, slurs_vec_to_str, }; -use crate::rate_limit::RateLimitInfo; use crate::settings::Settings; use crate::websocket::UserOperation; use crate::websocket::{ @@ -69,13 +68,12 @@ impl Oper { } } -pub trait Perform { +pub trait Perform { + type Response: serde::ser::Serialize; + fn perform( &self, pool: Pool>, websocket_info: Option, - rate_limit_info: Option, - ) -> Result - where - T: Sized; + ) -> Result; } diff --git a/server/src/api/post.rs b/server/src/api/post.rs index 19f160149..84ef89f16 100644 --- a/server/src/api/post.rs +++ b/server/src/api/post.rs @@ -77,12 +77,13 @@ pub struct SavePost { auth: String, } -impl Perform for Oper { +impl Perform for Oper { + type Response = PostResponse; + fn perform( &self, pool: Pool>, websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &CreatePost = &self.data; @@ -103,13 +104,6 @@ impl Perform for Oper { let user_id = claims.id; - if let Some(rl) = &rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_post(&rl.ip, true)?; - } - let conn = pool.get()?; // Check for a community ban @@ -176,13 +170,6 @@ impl Perform for Oper { Err(_e) => return Err(APIError::err("couldnt_find_post").into()), }; - if let Some(rl) = &rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_post(&rl.ip, false)?; - } - let res = PostResponse { post: post_view }; if let Some(ws) = websocket_info { @@ -197,12 +184,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = GetPostResponse; + fn perform( &self, pool: Pool>, websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &GetPost = &self.data; @@ -217,13 +205,6 @@ impl Perform for Oper { None => None, }; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; let post_view = match PostView::read(&conn, data.id, user_id) { @@ -277,12 +258,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = GetPostsResponse; + fn perform( &self, pool: Pool>, websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &GetPosts = &self.data; @@ -307,13 +289,6 @@ impl Perform for Oper { let type_ = ListingType::from_str(&data.type_)?; let sort = SortType::from_str(&data.sort)?; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; let posts = match PostQueryBuilder::create(&conn) @@ -348,12 +323,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = PostResponse; + fn perform( &self, pool: Pool>, websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &CreatePostLike = &self.data; @@ -364,13 +340,6 @@ impl Perform for Oper { let user_id = claims.id; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; // Don't do a downvote if site has downvotes disabled @@ -429,12 +398,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = PostResponse; + fn perform( &self, pool: Pool>, websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &EditPost = &self.data; @@ -455,13 +425,6 @@ impl Perform for Oper { let user_id = claims.id; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; // Verify its the creator or a mod or admin @@ -567,12 +530,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = PostResponse; + fn perform( &self, pool: Pool>, _websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &SavePost = &self.data; @@ -588,13 +552,6 @@ impl Perform for Oper { user_id, }; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; if data.save { diff --git a/server/src/api/site.rs b/server/src/api/site.rs index 891f52a48..e05487dfb 100644 --- a/server/src/api/site.rs +++ b/server/src/api/site.rs @@ -108,22 +108,16 @@ pub struct SaveSiteConfig { auth: String, } -impl Perform for Oper { +impl Perform for Oper { + type Response = ListCategoriesResponse; + fn perform( &self, pool: Pool>, _websocket_info: Option, - rate_limit_info: Option, ) -> Result { let _data: &ListCategories = &self.data; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; let categories: Vec = Category::list_all(&conn)?; @@ -133,22 +127,16 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = GetModlogResponse; + fn perform( &self, pool: Pool>, _websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &GetModlog = &self.data; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; let removed_posts = ModRemovePostView::list( @@ -220,12 +208,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = SiteResponse; + fn perform( &self, pool: Pool>, _websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &CreateSite = &self.data; @@ -246,13 +235,6 @@ impl Perform for Oper { let user_id = claims.id; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; // Make sure user is an admin @@ -281,12 +263,12 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = SiteResponse; fn perform( &self, pool: Pool>, websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &EditSite = &self.data; @@ -307,13 +289,6 @@ impl Perform for Oper { let user_id = claims.id; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; // Make sure user is an admin @@ -354,22 +329,16 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = GetSiteResponse; + fn perform( &self, pool: Pool>, websocket_info: Option, - rate_limit_info: Option, ) -> Result { let _data: &GetSite = &self.data; - if let Some(rl) = &rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; // TODO refactor this a little @@ -385,11 +354,7 @@ impl Perform for Oper { admin: true, show_nsfw: true, }; - let login_response = Oper::new(register).perform( - pool.clone(), - websocket_info.clone(), - rate_limit_info.clone(), - )?; + let login_response = Oper::new(register).perform(pool.clone(), websocket_info.clone())?; info!("Admin {} created", setup.admin_username); let create_site = CreateSite { @@ -400,7 +365,7 @@ impl Perform for Oper { enable_nsfw: false, auth: login_response.jwt, }; - Oper::new(create_site).perform(pool, websocket_info.clone(), rate_limit_info)?; + Oper::new(create_site).perform(pool, websocket_info.clone())?; info!("Site {} created", setup.site_name); Some(SiteView::read(&conn)?) } else { @@ -437,12 +402,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = SearchResponse; + fn perform( &self, pool: Pool>, _websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &Search = &self.data; @@ -467,13 +433,6 @@ impl Perform for Oper { // TODO no clean / non-nsfw searching rn - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; match type_ { @@ -569,12 +528,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = GetSiteResponse; + fn perform( &self, pool: Pool>, _websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &TransferSite = &self.data; @@ -585,13 +545,6 @@ impl Perform for Oper { let user_id = claims.id; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; let read_site = Site::read(&conn, 1)?; @@ -646,12 +599,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = GetSiteConfigResponse; + fn perform( &self, pool: Pool>, _websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &GetSiteConfig = &self.data; @@ -662,13 +616,6 @@ impl Perform for Oper { let user_id = claims.id; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; // Only let admins read this @@ -685,12 +632,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = GetSiteConfigResponse; + fn perform( &self, pool: Pool>, _websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &SaveSiteConfig = &self.data; @@ -701,13 +649,6 @@ impl Perform for Oper { let user_id = claims.id; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; // Only let admins read this diff --git a/server/src/api/user.rs b/server/src/api/user.rs index 31a0a4e78..c2734f512 100644 --- a/server/src/api/user.rs +++ b/server/src/api/user.rs @@ -199,22 +199,16 @@ pub struct UserJoinResponse { pub user_id: i32, } -impl Perform for Oper { +impl Perform for Oper { + type Response = LoginResponse; + fn perform( &self, pool: Pool>, _websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &Login = &self.data; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; // Fetch that username / email @@ -234,22 +228,16 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = LoginResponse; + fn perform( &self, pool: Pool>, _websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &Register = &self.data; - if let Some(rl) = &rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_register(&rl.ip, true)?; - } - let conn = pool.get()?; // Make sure site has open registration @@ -355,13 +343,6 @@ impl Perform for Oper { }; } - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_register(&rl.ip, false)?; - } - // Return the jwt Ok(LoginResponse { jwt: inserted_user.jwt(), @@ -369,12 +350,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = LoginResponse; + fn perform( &self, pool: Pool>, _websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &SaveUserSettings = &self.data; @@ -385,13 +367,6 @@ impl Perform for Oper { let user_id = claims.id; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; let read_user = User_::read(&conn, user_id)?; @@ -471,22 +446,16 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = GetUserDetailsResponse; + fn perform( &self, pool: Pool>, _websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &GetUserDetails = &self.data; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; let user_claims: Option = match &data.auth { @@ -582,12 +551,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = AddAdminResponse; + fn perform( &self, pool: Pool>, websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &AddAdmin = &self.data; @@ -598,13 +568,6 @@ impl Perform for Oper { let user_id = claims.id; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; // Make sure user is an admin @@ -669,12 +632,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = BanUserResponse; + fn perform( &self, pool: Pool>, websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &BanUser = &self.data; @@ -685,13 +649,6 @@ impl Perform for Oper { let user_id = claims.id; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; // Make sure user is an admin @@ -762,12 +719,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = GetRepliesResponse; + fn perform( &self, pool: Pool>, _websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &GetReplies = &self.data; @@ -780,13 +738,6 @@ impl Perform for Oper { let sort = SortType::from_str(&data.sort)?; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; let replies = ReplyQueryBuilder::create(&conn, user_id) @@ -800,12 +751,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = GetUserMentionsResponse; + fn perform( &self, pool: Pool>, _websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &GetUserMentions = &self.data; @@ -818,13 +770,6 @@ impl Perform for Oper { let sort = SortType::from_str(&data.sort)?; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; let mentions = UserMentionQueryBuilder::create(&conn, user_id) @@ -838,12 +783,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = UserMentionResponse; + fn perform( &self, pool: Pool>, _websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &EditUserMention = &self.data; @@ -854,13 +800,6 @@ impl Perform for Oper { let user_id = claims.id; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; let user_mention = UserMention::read(&conn, data.user_mention_id)?; @@ -885,12 +824,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = GetRepliesResponse; + fn perform( &self, pool: Pool>, _websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &MarkAllAsRead = &self.data; @@ -901,13 +841,6 @@ impl Perform for Oper { let user_id = claims.id; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; let replies = ReplyQueryBuilder::create(&conn, user_id) @@ -983,12 +916,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = LoginResponse; + fn perform( &self, pool: Pool>, _websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &DeleteAccount = &self.data; @@ -999,13 +933,6 @@ impl Perform for Oper { let user_id = claims.id; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; let user: User_ = User_::read(&conn, user_id)?; @@ -1078,22 +1005,16 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = PasswordResetResponse; + fn perform( &self, pool: Pool>, _websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &PasswordReset = &self.data; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; // Fetch that email @@ -1123,22 +1044,16 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = LoginResponse; + fn perform( &self, pool: Pool>, _websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &PasswordChange = &self.data; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; // Fetch the user_id from the token @@ -1162,12 +1077,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = PrivateMessageResponse; + fn perform( &self, pool: Pool>, websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &CreatePrivateMessage = &self.data; @@ -1180,13 +1096,6 @@ impl Perform for Oper { let hostname = &format!("https://{}", Settings::get().hostname); - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; // Check for a site ban @@ -1249,12 +1158,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = PrivateMessageResponse; + fn perform( &self, pool: Pool>, _websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &EditPrivateMessage = &self.data; @@ -1265,13 +1175,6 @@ impl Perform for Oper { let user_id = claims.id; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; let orig_private_message = PrivateMessage::read(&conn, data.edit_id)?; @@ -1318,12 +1221,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = PrivateMessagesResponse; + fn perform( &self, pool: Pool>, _websocket_info: Option, - rate_limit_info: Option, ) -> Result { let data: &GetPrivateMessages = &self.data; @@ -1334,13 +1238,6 @@ impl Perform for Oper { let user_id = claims.id; - if let Some(rl) = rate_limit_info { - rl.rate_limiter - .lock() - .unwrap() - .check_rate_limit_message(&rl.ip, false)?; - } - let conn = pool.get()?; let messages = PrivateMessageQueryBuilder::create(&conn, user_id) @@ -1353,12 +1250,13 @@ impl Perform for Oper { } } -impl Perform for Oper { +impl Perform for Oper { + type Response = UserJoinResponse; + fn perform( &self, _pool: Pool>, websocket_info: Option, - _rate_limit_info: Option, ) -> Result { let data: &UserJoin = &self.data; diff --git a/server/src/main.rs b/server/src/main.rs index eb4ba0e94..6abb22439 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -7,15 +7,13 @@ use actix_web::*; use diesel::r2d2::{ConnectionManager, Pool}; use diesel::PgConnection; use lemmy_server::{ - rate_limit::rate_limiter::RateLimiter, + rate_limit::{rate_limiter::RateLimiter, RateLimit}, routes::{api, federation, feeds, index, nodeinfo, webfinger, websocket}, settings::Settings, websocket::server::*, }; -use std::{ - io, - sync::{Arc, Mutex}, -}; +use std::{io, sync::Arc}; +use tokio::sync::Mutex; embed_migrations!(); @@ -36,7 +34,7 @@ async fn main() -> io::Result<()> { embedded_migrations::run(&conn).unwrap(); // Set up the rate limiter - let rate_limiter = Arc::new(Mutex::new(RateLimiter::default())); + let rate_limiter = RateLimit(Arc::new(Mutex::new(RateLimiter::default()))); // Set up websocket server let server = ChatServer::startup(pool.clone(), rate_limiter.clone()).start(); @@ -49,13 +47,13 @@ async fn main() -> io::Result<()> { // Create Http server with websocket support HttpServer::new(move || { let settings = Settings::get(); + let rate_limiter = rate_limiter.clone(); App::new() .wrap(middleware::Logger::default()) .data(pool.clone()) .data(server.clone()) - .data(rate_limiter.clone()) // The routes - .configure(api::config) + .configure(move |cfg| api::config(cfg, &rate_limiter)) .configure(federation::config) .configure(feeds::config) .configure(index::config) diff --git a/server/src/rate_limit/mod.rs b/server/src/rate_limit/mod.rs index 29a3a9e14..646e3477d 100644 --- a/server/src/rate_limit/mod.rs +++ b/server/src/rate_limit/mod.rs @@ -2,17 +2,180 @@ pub mod rate_limiter; use super::{IPAddr, Settings}; use crate::api::APIError; +use crate::settings::RateLimitConfig; +use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform}; use failure::Error; +use futures::future::{ok, Ready}; use log::warn; -use rate_limiter::RateLimiter; +use rate_limiter::{RateLimitType, RateLimiter}; use std::collections::HashMap; +use std::future::Future; +use std::pin::Pin; use std::sync::Arc; -use std::sync::Mutex; +use std::task::{Context, Poll}; use std::time::SystemTime; use strum::IntoEnumIterator; +use tokio::sync::Mutex; #[derive(Debug, Clone)] -pub struct RateLimitInfo { - pub rate_limiter: Arc>, - pub ip: IPAddr, +pub struct RateLimit(pub Arc>); + +#[derive(Debug, Clone)] +pub struct RateLimited(Arc>, RateLimitType); + +pub struct RateLimitedMiddleware(RateLimited, S); + +impl RateLimit { + pub fn message(&self) -> RateLimited { + self.kind(RateLimitType::Message) + } + + pub fn post(&self) -> RateLimited { + self.kind(RateLimitType::Post) + } + + pub fn register(&self) -> RateLimited { + self.kind(RateLimitType::Register) + } + + fn kind(&self, type_: RateLimitType) -> RateLimited { + RateLimited(self.0.clone(), type_) + } +} + +impl RateLimited { + pub async fn wrap( + self, + ip_addr: String, + fut: impl Future>, + ) -> Result + where + E: From, + { + let rate_limit: RateLimitConfig = actix_web::web::block(move || { + // needs to be in a web::block because the RwLock in settings is from stdlib + Ok(Settings::get().rate_limit.clone()) as Result<_, failure::Error> + }) + .await + .map_err(|e| match e { + actix_web::error::BlockingError::Error(e) => e, + _ => APIError::err("Operation canceled").into(), + })?; + + // before + { + let mut limiter = self.0.lock().await; + + match self.1 { + RateLimitType::Message => { + limiter.check_rate_limit_full( + self.1, + &ip_addr, + rate_limit.message, + rate_limit.message_per_second, + false, + )?; + + return fut.await; + } + RateLimitType::Post => { + limiter.check_rate_limit_full( + self.1.clone(), + &ip_addr, + rate_limit.post, + rate_limit.post_per_second, + true, + )?; + } + RateLimitType::Register => { + limiter.check_rate_limit_full( + self.1, + &ip_addr, + rate_limit.register, + rate_limit.register_per_second, + true, + )?; + } + }; + } + + let res = fut.await; + + // after + { + let mut limiter = self.0.lock().await; + if res.is_ok() { + match self.1 { + RateLimitType::Post => { + limiter.check_rate_limit_full( + self.1, + &ip_addr, + rate_limit.post, + rate_limit.post_per_second, + false, + )?; + } + RateLimitType::Register => { + limiter.check_rate_limit_full( + self.1, + &ip_addr, + rate_limit.register, + rate_limit.register_per_second, + false, + )?; + } + _ => (), + }; + } + } + + res + } +} + +impl Transform for RateLimited +where + S: Service, + S::Future: 'static, +{ + type Request = S::Request; + type Response = S::Response; + type Error = actix_web::Error; + type InitError = (); + type Transform = RateLimitedMiddleware; + type Future = Ready>; + + fn new_transform(&self, service: S) -> Self::Future { + ok(RateLimitedMiddleware(self.clone(), service)) + } +} + +impl Service for RateLimitedMiddleware +where + S: Service, + S::Future: 'static, +{ + type Request = S::Request; + type Response = S::Response; + type Error = actix_web::Error; + type Future = Pin>>>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.1.poll_ready(cx) + } + + fn call(&mut self, req: S::Request) -> Self::Future { + let ip_addr = req + .connection_info() + .remote() + .unwrap_or("127.0.0.1:12345") + .split(':') + .next() + .unwrap_or("127.0.0.1") + .to_string(); + + let fut = self.0.clone().wrap(ip_addr, self.1.call(req)); + + Box::pin(async move { fut.await.map_err(actix_web::Error::from) }) + } } diff --git a/server/src/rate_limit/rate_limiter.rs b/server/src/rate_limit/rate_limiter.rs index 6b01a75b2..8f598c3dd 100644 --- a/server/src/rate_limit/rate_limiter.rs +++ b/server/src/rate_limit/rate_limiter.rs @@ -79,7 +79,7 @@ impl RateLimiter { } #[allow(clippy::float_cmp)] - fn check_rate_limit_full( + pub(super) fn check_rate_limit_full( &mut self, type_: RateLimitType, ip: &str, diff --git a/server/src/routes/api.rs b/server/src/routes/api.rs index 0ac1a8a53..9d5de33c3 100644 --- a/server/src/routes/api.rs +++ b/server/src/routes/api.rs @@ -4,119 +4,158 @@ use crate::api::community::*; use crate::api::post::*; use crate::api::site::*; use crate::api::user::*; +use crate::rate_limit::RateLimit; +use actix_web::guard; #[rustfmt::skip] -pub fn config(cfg: &mut web::ServiceConfig) { - cfg - // Site - .route("/api/v1/site", web::get().to(route_get::)) - .route("/api/v1/categories", web::get().to(route_get::)) - .route("/api/v1/modlog", web::get().to(route_get::)) - .route("/api/v1/search", web::get().to(route_get::)) - // Community - .route("/api/v1/community", web::post().to(route_post::)) - .route("/api/v1/community", web::get().to(route_get::)) - .route("/api/v1/community", web::put().to(route_post::)) - .route("/api/v1/community/list", web::get().to(route_get::)) - .route("/api/v1/community/follow", web::post().to(route_post::)) - // Post - .route("/api/v1/post", web::post().to(route_post::)) - .route("/api/v1/post", web::put().to(route_post::)) - .route("/api/v1/post", web::get().to(route_get::)) - .route("/api/v1/post/list", web::get().to(route_get::)) - .route("/api/v1/post/like", web::post().to(route_post::)) - .route("/api/v1/post/save", web::put().to(route_post::)) - // Comment - .route("/api/v1/comment", web::post().to(route_post::)) - .route("/api/v1/comment", web::put().to(route_post::)) - .route("/api/v1/comment/like", web::post().to(route_post::)) - .route("/api/v1/comment/save", web::put().to(route_post::)) - // User - .route("/api/v1/user", web::get().to(route_get::)) - .route("/api/v1/user/mention", web::get().to(route_get::)) - .route("/api/v1/user/mention", web::put().to(route_post::)) - .route("/api/v1/user/replies", web::get().to(route_get::)) - .route("/api/v1/user/followed_communities", web::get().to(route_get::)) - // Mod actions - .route("/api/v1/community/transfer", web::post().to(route_post::)) - .route("/api/v1/community/ban_user", web::post().to(route_post::)) - .route("/api/v1/community/mod", web::post().to(route_post::)) - // Admin actions - .route("/api/v1/site", web::post().to(route_post::)) - .route("/api/v1/site", web::put().to(route_post::)) - .route("/api/v1/site/transfer", web::post().to(route_post::)) - .route("/api/v1/site/config", web::get().to(route_get::)) - .route("/api/v1/site/config", web::put().to(route_post::)) - .route("/api/v1/admin/add", web::post().to(route_post::)) - .route("/api/v1/user/ban", web::post().to(route_post::)) - // User account actions - .route("/api/v1/user/login", web::post().to(route_post::)) - .route("/api/v1/user/register", web::post().to(route_post::)) - .route("/api/v1/user/delete_account", web::post().to(route_post::)) - .route("/api/v1/user/password_reset", web::post().to(route_post::)) - .route("/api/v1/user/password_change", web::post().to(route_post::)) - .route("/api/v1/user/mark_all_as_read", web::post().to(route_post::)) - .route("/api/v1/user/save_user_settings", web::put().to(route_post::)); +pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimit) { + cfg.service( + web::scope("/api/v1") + // Site + .service( + web::scope("/site") + .wrap(rate_limit.message()) + .route("", web::get().to(route_get::)) + // Admin Actions + .route("", web::post().to(route_post::)) + .route("", web::put().to(route_post::)) + .route("/transfer", web::post().to(route_post::)) + .route("/config", web::get().to(route_get::)) + .route("/config", web::put().to(route_post::)), + ) + .service( + web::resource("/categories") + .wrap(rate_limit.message()) + .route(web::get().to(route_get::)), + ) + .service( + web::resource("/modlog") + .wrap(rate_limit.message()) + .route(web::get().to(route_get::)), + ) + .service( + web::resource("/search") + .wrap(rate_limit.message()) + .route(web::get().to(route_get::)), + ) + // Community + .service( + web::scope("/community") + .wrap(rate_limit.message()) + .route("", web::post().to(route_post::)) + .route("", web::get().to(route_get::)) + .route("", web::put().to(route_post::)) + .route("/list", web::get().to(route_get::)) + .route("/follow", web::post().to(route_post::)) + // Mod Actions + .route("/transfer", web::post().to(route_post::)) + .route("/ban_user", web::post().to(route_post::)) + .route("/mod", web::post().to(route_post::)), + ) + // Post + .service( + // Handle POST to /post separately to add the post() rate limitter + web::resource("/post") + .guard(guard::Post()) + .wrap(rate_limit.post()) + .route(web::post().to(route_post::)), + ) + .service( + web::scope("/post") + .wrap(rate_limit.message()) + .route("", web::get().to(route_get::)) + .route("", web::put().to(route_post::)) + .route("/list", web::get().to(route_get::)) + .route("/like", web::post().to(route_post::)) + .route("/save", web::put().to(route_post::)), + ) + // Comment + .service( + web::scope("/comment") + .wrap(rate_limit.message()) + .route("", web::post().to(route_post::)) + .route("", web::put().to(route_post::)) + .route("/like", web::post().to(route_post::)) + .route("/save", web::put().to(route_post::)), + ) + // User + .service( + // Account action, I don't like that it's in /user maybe /accounts + // Handle /user/register separately to add the register() rate limitter + web::resource("/user/register") + .guard(guard::Post()) + .wrap(rate_limit.register()) + .route(web::post().to(route_post::)), + ) + // User actions + .service( + web::scope("/user") + .wrap(rate_limit.message()) + .route("", web::get().to(route_get::)) + .route("/mention", web::get().to(route_get::)) + .route("/mention", web::put().to(route_post::)) + .route("/replies", web::get().to(route_get::)) + .route("/followed_communities", web::get().to(route_get::)) + // Admin action. I don't like that it's in /user + .route("/ban", web::post().to(route_post::)) + // Account actions. I don't like that they're in /user maybe /accounts + .route("/login", web::post().to(route_post::)) + .route("/delete_account", web::post().to(route_post::)) + .route("/password_reset", web::post().to(route_post::)) + .route("/password_change", web::post().to(route_post::)) + // mark_all_as_read feels off being in this section as well + .route("/mark_all_as_read", web::post().to(route_post::)) + .route("/save_user_settings", web::put().to(route_post::)), + ) + // Admin Actions + .service( + web::resource("/admin/add") + .wrap(rate_limit.message()) + .route(web::post().to(route_post::)), + ), + ); } -fn perform( +fn perform( data: Request, db: DbPoolParam, - rate_limit_param: RateLimitParam, chat_server: ChatServerParam, - req: HttpRequest, ) -> Result where - Response: Serialize, - Oper: Perform, + Oper: Perform, { let ws_info = WebsocketInfo { chatserver: chat_server.get_ref().to_owned(), id: None, }; - let rate_limit_info = RateLimitInfo { - rate_limiter: rate_limit_param.get_ref().to_owned(), - ip: get_ip(&req), - }; - let oper: Oper = Oper::new(data); - let res = oper.perform( - db.get_ref().to_owned(), - Some(ws_info), - Some(rate_limit_info), - ); + let res = oper.perform(db.get_ref().to_owned(), Some(ws_info)); Ok(HttpResponse::Ok().json(res?)) } -async fn route_get( +async fn route_get( data: web::Query, db: DbPoolParam, - rate_limit_param: RateLimitParam, chat_server: ChatServerParam, - req: HttpRequest, ) -> Result where Data: Serialize, - Response: Serialize, - Oper: Perform, + Oper: Perform, { - perform::(data.0, db, rate_limit_param, chat_server, req) + perform::(data.0, db, chat_server) } -async fn route_post( +async fn route_post( data: web::Json, db: DbPoolParam, - rate_limit_param: RateLimitParam, chat_server: ChatServerParam, - req: HttpRequest, ) -> Result where Data: Serialize, - Response: Serialize, - Oper: Perform, + Oper: Perform, { - perform::(data.0, db, rate_limit_param, chat_server, req) + perform::(data.0, db, chat_server) } diff --git a/server/src/routes/mod.rs b/server/src/routes/mod.rs index 4d018db49..b1ea41679 100644 --- a/server/src/routes/mod.rs +++ b/server/src/routes/mod.rs @@ -1,6 +1,6 @@ use crate::api::{Oper, Perform}; use crate::db::site_view::SiteView; -use crate::rate_limit::{rate_limiter::RateLimiter, RateLimitInfo}; +use crate::rate_limit::rate_limiter::RateLimiter; use crate::websocket::{server::ChatServer, WebsocketInfo}; use crate::{get_ip, markdown_to_html, version, Settings}; use actix::prelude::*; diff --git a/server/src/routes/websocket.rs b/server/src/routes/websocket.rs index 045858eca..c6bca9aa0 100644 --- a/server/src/routes/websocket.rs +++ b/server/src/routes/websocket.rs @@ -123,10 +123,9 @@ impl StreamHandler> for WSSession { .into_actor(self) .then(|res, _, ctx| { match res { - Ok(res) => ctx.text(res), - Err(e) => { - error!("{}", &e); - } + Ok(Ok(res)) => ctx.text(res), + Ok(Err(e)) => error!("{}", e), + Err(e) => error!("{}", &e), } actix::fut::ready(()) }) diff --git a/server/src/websocket/mod.rs b/server/src/websocket/mod.rs index 05d021d75..fd200d7d6 100644 --- a/server/src/websocket/mod.rs +++ b/server/src/websocket/mod.rs @@ -12,8 +12,6 @@ use serde_json::Value; use server::ChatServer; use std::collections::{HashMap, HashSet}; use std::str::FromStr; -use std::sync::Arc; -use std::sync::Mutex; #[derive(EnumString, ToString, Debug, Clone)] pub enum UserOperation { diff --git a/server/src/websocket/server.rs b/server/src/websocket/server.rs index ab3bddf02..d16ecf854 100644 --- a/server/src/websocket/server.rs +++ b/server/src/websocket/server.rs @@ -9,7 +9,7 @@ use crate::api::post::*; use crate::api::site::*; use crate::api::user::*; use crate::api::*; -use crate::rate_limit::{rate_limiter::RateLimiter, RateLimitInfo}; +use crate::rate_limit::RateLimit; use crate::websocket::UserOperation; use crate::{CommunityId, ConnectionId, IPAddr, PostId, UserId}; @@ -38,7 +38,7 @@ pub struct Disconnect { /// The messages sent to websocket clients #[derive(Serialize, Deserialize, Message)] -#[rtype(String)] +#[rtype(result = "Result")] pub struct StandardMessage { /// Id of the client session pub id: ConnectionId, @@ -152,13 +152,13 @@ pub struct ChatServer { pool: Pool>, /// Rate limiting based on rate type and IP addr - rate_limiter: Arc>, + rate_limiter: RateLimit, } impl ChatServer { pub fn startup( pool: Pool>, - rate_limiter: Arc>, + rate_limiter: RateLimit, ) -> ChatServer { ChatServer { sessions: HashMap::new(), @@ -389,352 +389,526 @@ impl ChatServer { } } - fn do_user_operation<'a, Data, Response>( - &self, - id: ConnectionId, - ip: IPAddr, - op: UserOperation, - data: &str, - ctx: &mut Context, - ) -> Result - where - for<'de> Data: Deserialize<'de> + 'a, - Response: Serialize, - Oper: Perform, - { - let parsed_data: Data = serde_json::from_str(data)?; - - let ws_info = WebsocketInfo { - chatserver: ctx.address(), - id: Some(id), - }; - - let rate_limit_info = RateLimitInfo { - rate_limiter: self.rate_limiter.clone(), - ip, - }; - - let new_pool = self.pool.clone(); - let res = Oper::new(parsed_data).perform(new_pool, Some(ws_info), Some(rate_limit_info))?; - to_json_string(&op, &res) - } - fn parse_json_message( &mut self, msg: StandardMessage, ctx: &mut Context, - ) -> Result { - let json: Value = serde_json::from_str(&msg.msg)?; - let data = &json["data"].to_string(); - let op = &json["op"].as_str().ok_or(APIError { - message: "Unknown op type".to_string(), - })?; - - let user_operation: UserOperation = UserOperation::from_str(&op)?; + ) -> impl Future> { + let addr = ctx.address(); + let pool = self.pool.clone(); + let rate_limiter = self.rate_limiter.clone(); let ip: IPAddr = match self.sessions.get(&msg.id) { Some(info) => info.ip.to_owned(), None => "blank_ip".to_string(), }; - match user_operation { - // User ops - UserOperation::Login => { - self.do_user_operation::(msg.id, ip, user_operation, data, ctx) - } - UserOperation::Register => { - self.do_user_operation::(msg.id, ip, user_operation, data, ctx) - } - UserOperation::GetUserDetails => self - .do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), - UserOperation::GetReplies => self.do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), - UserOperation::AddAdmin => { - self.do_user_operation::(msg.id, ip, user_operation, data, ctx) - } - UserOperation::BanUser => { - self.do_user_operation::(msg.id, ip, user_operation, data, ctx) - } - UserOperation::GetUserMentions => self - .do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), - UserOperation::EditUserMention => self - .do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), - UserOperation::MarkAllAsRead => self.do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), - UserOperation::DeleteAccount => self.do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), - UserOperation::PasswordReset => self - .do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), - UserOperation::PasswordChange => self.do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), - UserOperation::CreatePrivateMessage => self - .do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), - UserOperation::EditPrivateMessage => self - .do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), - UserOperation::GetPrivateMessages => self - .do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), - UserOperation::UserJoin => { - self.do_user_operation::(msg.id, ip, user_operation, data, ctx) - } - UserOperation::SaveUserSettings => self.do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), + async move { + let msg = msg; + let json: Value = serde_json::from_str(&msg.msg)?; + let data = &json["data"].to_string(); + let op = &json["op"].as_str().ok_or(APIError { + message: "Unknown op type".to_string(), + })?; - // Site ops - UserOperation::GetModlog => self.do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), - UserOperation::CreateSite => { - self.do_user_operation::(msg.id, ip, user_operation, data, ctx) - } - UserOperation::EditSite => { - self.do_user_operation::(msg.id, ip, user_operation, data, ctx) - } - UserOperation::GetSite => { - self.do_user_operation::(msg.id, ip, user_operation, data, ctx) - } - UserOperation::GetSiteConfig => self - .do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), - UserOperation::SaveSiteConfig => self - .do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), - UserOperation::Search => { - self.do_user_operation::(msg.id, ip, user_operation, data, ctx) - } - UserOperation::TransferCommunity => self - .do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), - UserOperation::TransferSite => self.do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), - UserOperation::ListCategories => self - .do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), + let user_operation: UserOperation = UserOperation::from_str(&op)?; - // Community ops - UserOperation::GetCommunity => self.do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), - UserOperation::ListCommunities => self - .do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), - UserOperation::CreateCommunity => self - .do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), - UserOperation::EditCommunity => self.do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), - UserOperation::FollowCommunity => self - .do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), - UserOperation::GetFollowedCommunities => self - .do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), - UserOperation::BanFromCommunity => self - .do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), - UserOperation::AddModToCommunity => self - .do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), + match user_operation { + // User ops + UserOperation::Login => { + do_user_operation::(pool, rate_limiter, addr, msg.id, ip, user_operation, data) + .await + } + UserOperation::Register => { + do_user_operation::(pool, rate_limiter, addr, msg.id, ip, user_operation, data) + .await + } + UserOperation::GetUserDetails => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } + UserOperation::GetReplies => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } + UserOperation::AddAdmin => { + do_user_operation::(pool, rate_limiter, addr, msg.id, ip, user_operation, data) + .await + } + UserOperation::BanUser => { + do_user_operation::(pool, rate_limiter, addr, msg.id, ip, user_operation, data) + .await + } + UserOperation::GetUserMentions => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } + UserOperation::EditUserMention => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } + UserOperation::MarkAllAsRead => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } + UserOperation::DeleteAccount => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } + UserOperation::PasswordReset => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } + UserOperation::PasswordChange => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } + UserOperation::CreatePrivateMessage => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } + UserOperation::EditPrivateMessage => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } + UserOperation::GetPrivateMessages => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } + UserOperation::UserJoin => { + do_user_operation::(pool, rate_limiter, addr, msg.id, ip, user_operation, data) + .await + } + UserOperation::SaveUserSettings => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } - // Post ops - UserOperation::CreatePost => { - self.do_user_operation::(msg.id, ip, user_operation, data, ctx) - } - UserOperation::GetPost => { - self.do_user_operation::(msg.id, ip, user_operation, data, ctx) - } - UserOperation::GetPosts => { - self.do_user_operation::(msg.id, ip, user_operation, data, ctx) - } - UserOperation::EditPost => { - self.do_user_operation::(msg.id, ip, user_operation, data, ctx) - } - UserOperation::CreatePostLike => self.do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), - UserOperation::SavePost => { - self.do_user_operation::(msg.id, ip, user_operation, data, ctx) - } + // Site ops + UserOperation::GetModlog => { + do_user_operation::(pool, rate_limiter, addr, msg.id, ip, user_operation, data) + .await + } + UserOperation::CreateSite => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } + UserOperation::EditSite => { + do_user_operation::(pool, rate_limiter, addr, msg.id, ip, user_operation, data) + .await + } + UserOperation::GetSite => { + do_user_operation::(pool, rate_limiter, addr, msg.id, ip, user_operation, data) + .await + } + UserOperation::GetSiteConfig => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } + UserOperation::SaveSiteConfig => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } + UserOperation::Search => { + do_user_operation::(pool, rate_limiter, addr, msg.id, ip, user_operation, data) + .await + } + UserOperation::TransferCommunity => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } + UserOperation::TransferSite => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } + UserOperation::ListCategories => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } - // Comment ops - UserOperation::CreateComment => self.do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), - UserOperation::EditComment => self.do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), - UserOperation::SaveComment => self.do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), - UserOperation::GetComments => self.do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), - UserOperation::CreateCommentLike => self - .do_user_operation::( - msg.id, - ip, - user_operation, - data, - ctx, - ), + // Community ops + UserOperation::GetCommunity => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } + UserOperation::ListCommunities => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } + UserOperation::CreateCommunity => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } + UserOperation::EditCommunity => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } + UserOperation::FollowCommunity => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } + UserOperation::GetFollowedCommunities => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } + UserOperation::BanFromCommunity => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } + UserOperation::AddModToCommunity => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } + + // Post ops + UserOperation::CreatePost => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } + UserOperation::GetPost => { + do_user_operation::(pool, rate_limiter, addr, msg.id, ip, user_operation, data) + .await + } + UserOperation::GetPosts => { + do_user_operation::(pool, rate_limiter, addr, msg.id, ip, user_operation, data) + .await + } + UserOperation::EditPost => { + do_user_operation::(pool, rate_limiter, addr, msg.id, ip, user_operation, data) + .await + } + UserOperation::CreatePostLike => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } + UserOperation::SavePost => { + do_user_operation::(pool, rate_limiter, addr, msg.id, ip, user_operation, data) + .await + } + + // Comment ops + UserOperation::CreateComment => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } + UserOperation::EditComment => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } + UserOperation::SaveComment => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } + UserOperation::GetComments => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } + UserOperation::CreateCommentLike => { + do_user_operation::( + pool, + rate_limiter, + addr, + msg.id, + ip, + user_operation, + data, + ) + .await + } + } } } } +async fn do_user_operation<'a, Data>( + pool: Pool>, + rate_limiter: RateLimit, + chatserver: Addr, + id: ConnectionId, + ip: IPAddr, + op: UserOperation, + data: &str, +) -> Result +where + for<'de> Data: Deserialize<'de> + 'a, + Oper: Perform, +{ + let ws_info = WebsocketInfo { + chatserver, + id: Some(id), + }; + + let data = data.to_string(); + let op2 = op.clone(); + let fut = async move { + let parsed_data: Data = serde_json::from_str(&data)?; + let res = Oper::new(parsed_data).perform(pool, Some(ws_info))?; + to_json_string(&op, &res) + }; + + match op2 { + UserOperation::Register => rate_limiter.register().wrap(ip, fut).await, + UserOperation::CreatePost => rate_limiter.post().wrap(ip, fut).await, + _ => rate_limiter.message().wrap(ip, fut).await, + } +} + /// Make actor from `ChatServer` impl Actor for ChatServer { /// We are going to use simple Context, we just need ability to communicate @@ -789,19 +963,22 @@ impl Handler for ChatServer { /// Handler for Message message. impl Handler for ChatServer { - type Result = MessageResult; + type Result = ResponseFuture>; fn handle(&mut self, msg: StandardMessage, ctx: &mut Context) -> Self::Result { - match self.parse_json_message(msg, ctx) { - Ok(m) => { - info!("Message Sent: {}", m); - MessageResult(m) + let fut = self.parse_json_message(msg, ctx); + Box::pin(async move { + match fut.await { + Ok(m) => { + info!("Message Sent: {}", m); + Ok(m) + } + Err(e) => { + error!("Error during message handling {}", e); + Ok(e.to_string()) + } } - Err(e) => { - error!("Error during message handling {}", e); - MessageResult(e.to_string()) - } - } + }) } } From 4712ae4eb0160a36d27f531cf940de98c5f695a0 Mon Sep 17 00:00:00 2001 From: asonix Date: Sun, 19 Apr 2020 23:43:30 -0500 Subject: [PATCH 08/20] appease clippy --- server/src/rate_limit/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/server/src/rate_limit/mod.rs b/server/src/rate_limit/mod.rs index 646e3477d..9aeb11718 100644 --- a/server/src/rate_limit/mod.rs +++ b/server/src/rate_limit/mod.rs @@ -54,7 +54,7 @@ impl RateLimited { { let rate_limit: RateLimitConfig = actix_web::web::block(move || { // needs to be in a web::block because the RwLock in settings is from stdlib - Ok(Settings::get().rate_limit.clone()) as Result<_, failure::Error> + Ok(Settings::get().rate_limit) as Result<_, failure::Error> }) .await .map_err(|e| match e { @@ -150,6 +150,8 @@ where } } +type FutResult = dyn Future>; + impl Service for RateLimitedMiddleware where S: Service, @@ -158,7 +160,7 @@ where type Request = S::Request; type Response = S::Response; type Error = actix_web::Error; - type Future = Pin>>>; + type Future = Pin>>; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.1.poll_ready(cx) From 217efdd7067c2fbd2efcb7ab6b76bf9f7345ab99 Mon Sep 17 00:00:00 2001 From: asonix Date: Sun, 19 Apr 2020 23:47:20 -0500 Subject: [PATCH 09/20] Allow rustfmt on api config --- server/src/routes/api.rs | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/server/src/routes/api.rs b/server/src/routes/api.rs index 9d5de33c3..3ef8ca510 100644 --- a/server/src/routes/api.rs +++ b/server/src/routes/api.rs @@ -7,7 +7,6 @@ use crate::api::user::*; use crate::rate_limit::RateLimit; use actix_web::guard; -#[rustfmt::skip] pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimit) { cfg.service( web::scope("/api/v1") @@ -95,17 +94,35 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimit) { .route("/mention", web::get().to(route_get::)) .route("/mention", web::put().to(route_post::)) .route("/replies", web::get().to(route_get::)) - .route("/followed_communities", web::get().to(route_get::)) + .route( + "/followed_communities", + web::get().to(route_get::), + ) // Admin action. I don't like that it's in /user .route("/ban", web::post().to(route_post::)) // Account actions. I don't like that they're in /user maybe /accounts .route("/login", web::post().to(route_post::)) - .route("/delete_account", web::post().to(route_post::)) - .route("/password_reset", web::post().to(route_post::)) - .route("/password_change", web::post().to(route_post::)) + .route( + "/delete_account", + web::post().to(route_post::), + ) + .route( + "/password_reset", + web::post().to(route_post::), + ) + .route( + "/password_change", + web::post().to(route_post::), + ) // mark_all_as_read feels off being in this section as well - .route("/mark_all_as_read", web::post().to(route_post::)) - .route("/save_user_settings", web::put().to(route_post::)), + .route( + "/mark_all_as_read", + web::post().to(route_post::), + ) + .route( + "/save_user_settings", + web::put().to(route_post::), + ), ) // Admin Actions .service( From dc5636c6c120bdad5b6a976ada532fce40531ef0 Mon Sep 17 00:00:00 2001 From: Sylvia Ji Date: Mon, 20 Apr 2020 08:02:06 +0000 Subject: [PATCH 10/20] Translated using Weblate (Japanese) Currently translated at 99.1% (235 of 237 strings) Translation: Lemmy/lemmy Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/ja/ Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (237 of 237 strings) Translation: Lemmy/lemmy Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/zh_Hans/ --- ui/translations/ja.json | 38 +++++++++++- ui/translations/zh.json | 134 ++++++++++++++++++++++++++++++---------- 2 files changed, 136 insertions(+), 36 deletions(-) diff --git a/ui/translations/ja.json b/ui/translations/ja.json index 4cb9a5d9f..066ff589f 100644 --- a/ui/translations/ja.json +++ b/ui/translations/ja.json @@ -24,7 +24,7 @@ "number_of_communities": "{{count}} 個のコミュニティ", "community_reqs": "英小文字、アンダースコア、空白なし。", "create_private_message": "プライベートメッセージの作成", - "send_secure_message": "メッセージを安全に送信", + "send_secure_message": "セキュアメッセージを送信", "send_message": "メッセージを送信", "message": "メッセージ", "edit": "編集", @@ -133,7 +133,7 @@ "no_email_setup": "サーバーのメール設定が正しくありません。", "email": "メールアドレス", "matrix_user_id": "Matrix のユーザーアカウント", - "private_message_disclaimer": "Lemmy でのプライベートメッセージは安全ではありません。安全なメッセージを送るには <1>Riot.im でアカウントを作成してください。", + "private_message_disclaimer": "警告:Lemmy でのプライベートメッセージは安全ではありません。安全なメッセージを送るには <1>Riot.im でアカウントを作成してください。", "send_notifications_to_email": "通知をメール送信", "optional": "任意", "language": "言語", @@ -201,5 +201,37 @@ "block_leaving": "このページから離れてもよろしいですか?", "number_of_upvotes": "{{count}} 票の賛成", "number_of_downvotes": "{{count}} 票の反対", - "show_context": "前後を表示" + "show_context": "前後を表示", + "both": "両方", + "expires": "期限切れ", + "downvotes_disabled": "反対票を無効化", + "chat": "会話", + "sponsors_of_lemmy": "Lemmyのスポンサー", + "from": "から", + "couldnt_create_post": "投稿ができない。", + "couldnt_update_comment": "コメントが更新されない。", + "couldnt_save_comment": "コメントが保存されない。", + "couldnt_get_comments": "コメントが取得されない。", + "no_comment_edit_allowed": "コメントの編集権限がありません。", + "no_post_edit_allowed": "投稿の編集権限がありません。", + "no_community_edit_allowed": "コミュニティの編集許可がありません。", + "couldnt_update_community": "コミュニティが更新されない。", + "community_already_exists": "コミュニティは既に存在します。", + "community_moderator_already_exists": "コミュニティ管理人は既に存在します。", + "community_follower_already_exists": "コミュニティフォロワーは既に存在します。", + "community_user_already_banned": "コミュニティユーザーは既に禁止されています。", + "post_title_too_long": "投稿のタイトルが長すぎます。", + "couldnt_update_post": "投稿が更新されない", + "couldnt_get_posts": "投稿が取得できない", + "couldnt_save_post": "投稿が保存されない。", + "no_slurs": "悪口禁止。", + "not_an_admin": "管理者ではありません。", + "site_already_exists": "サイトは既に存在します。", + "couldnt_update_site": "サイトが更新されない。", + "user_already_exists": "ユーザーは既に存在します。", + "couldnt_update_user": "ユーザーが更新されない。", + "system_err_login": "システムエラーが発生しました。一度ログアウトして、再度ログインをお試しください。", + "couldnt_create_private_message": "プライベートメッセージが作成されない。", + "no_private_message_edit_allowed": "プライベートメッセージの編集許可がありません。", + "couldnt_update_private_message": "プライベートメッセージが更新されない。" } diff --git a/ui/translations/zh.json b/ui/translations/zh.json index 6eb6f201c..bc054b368 100644 --- a/ui/translations/zh.json +++ b/ui/translations/zh.json @@ -4,11 +4,11 @@ "no_posts": "没有帖子.", "create_a_post": "创建新帖子", "create_post": "创建帖子", - "number_of_posts": "{{count}} 帖子", + "number_of_posts": "{{count}} 个帖子", "posts": "帖子", "related_posts": "相关的帖子", "comments": "评论", - "number_of_comments": "{{count}} 评论", + "number_of_comments": "{{count}} 条评论", "remove_comment": "移除评论", "communities": "节点", "create_a_community": "创建新节点", @@ -52,9 +52,9 @@ "create": "创建", "username": "用户名", "email_or_username": "邮箱或用户名", - "number_of_users": "{{count}} 用户", - "number_of_subscribers": "{{count}} 订阅", - "number_of_points": "{{count}} 分", + "number_of_users": "{{count}} 名用户", + "number_of_subscribers": "{{count}} 名订阅者", + "number_of_points": "{{count}} 点数", "name": "名字", "title": "标题", "category": "分类", @@ -127,35 +127,35 @@ "community_ban": "你被此节点禁止.", "site_ban": "你被此站点禁止", "couldnt_create_comment": "不能创建评论.", - "couldnt_like_comment": "不能收藏评论.", + "couldnt_like_comment": "无法点赞评论。", "couldnt_update_comment": "不能更新评论.", "couldnt_save_comment": "不能保存评论.", - "no_comment_edit_allowed": "不允许编辑评论.", - "no_post_edit_allowed": "不运行编辑帖子.", - "no_community_edit_allowed": "不允许编辑节点.", - "couldnt_find_community": "不能找到节点.", - "couldnt_update_community": "不能更新节点.", - "community_already_exists": "节点已存在.", - "community_moderator_already_exists": "节点监管人已存在.", - "community_follower_already_exists": "节点追随者已存在.", - "community_user_already_banned": "节点用户已禁止.", - "couldnt_create_post": "不能创建帖子.", - "couldnt_like_post": "不能收藏帖子.", - "couldnt_find_post": "不能搜寻帖子.", - "couldnt_get_posts": "不能获取帖子", - "couldnt_update_post": "不能更新帖子", - "couldnt_save_post": "不能保持帖子.", - "no_slurs": "和谐.", - "not_an_admin": "不是管理员.", - "site_already_exists": "站点已存在.", - "couldnt_update_site": "不能更新站点.", - "couldnt_find_that_username_or_email": "用户名/邮箱不存在.", - "password_incorrect": "密码不正确.", - "passwords_dont_match": "密码不匹配.", - "admin_already_created": "抱歉,管理员已存在.", - "user_already_exists": "用户已存在.", - "couldnt_update_user": "不可以更新用户.", - "system_err_login": "系统错误. 尝试注销再登录", + "no_comment_edit_allowed": "没有编辑评论的权限。", + "no_post_edit_allowed": "没有编辑帖子的权限。", + "no_community_edit_allowed": "没有编辑节点的权限。", + "couldnt_find_community": "无法找到节点。", + "couldnt_update_community": "无法更新节点。", + "community_already_exists": "节点已存在。", + "community_moderator_already_exists": "节点监管人已存在。", + "community_follower_already_exists": "节点追随者已存在。", + "community_user_already_banned": "节点用户已禁止。", + "couldnt_create_post": "无法创建帖子。", + "couldnt_like_post": "无法点赞帖子。", + "couldnt_find_post": "无法找到帖子。", + "couldnt_get_posts": "无法获取帖子", + "couldnt_update_post": "无法更新帖子", + "couldnt_save_post": "无法保存帖子。", + "no_slurs": "文明语言。", + "not_an_admin": "不是管理员。", + "site_already_exists": "站点已存在。", + "couldnt_update_site": "无法更新站点。", + "couldnt_find_that_username_or_email": "用户名/邮箱不存在。", + "password_incorrect": "密码不正确。", + "passwords_dont_match": "密码不匹配。", + "admin_already_created": "抱歉,管理员已存在。", + "user_already_exists": "用户已存在。", + "couldnt_update_user": "无法更新用户。", + "system_err_login": "系统错误。请尝试注销后重新登入。", "nsfw": "工作场所不宜", "show_nsfw": "显示工作场所不宜内容", "theme": "主题", @@ -167,5 +167,73 @@ "are_you_sure": "你确定吗?", "yes": "是", "no": "否", - "logged_in": "已登陆" + "logged_in": "已登陆", + "message": "信息", + "create_private_message": "创建私信", + "send_secure_message": "发送安全信息", + "send_message": "发送信息", + "more": "更多", + "preview": "预览", + "upload_image": "上传图片", + "enable_nsfw": "允许工作场所不宜", + "show_avatars": "显示头像", + "avatar": "头像", + "formatting_help": "格式帮助", + "sorting_help": "排序帮助", + "view_source": "显示来源", + "sticky": "固定", + "unsticky": "取消固定", + "archive_link": "链接归档", + "settings": "设定", + "stickied": "已固定", + "delete_account": "删除账号", + "delete_account_confirm": "警告:此操作将永久删除你的数据。输入密码进行确认。", + "cross_posts": "此链接也已被发布到:", + "cross_post": "复数发布", + "cross_posted_to": "已被复数发布到: ", + "users": "用户", + "banned": "已被禁止", + "creator": "发布者", + "number_of_communities": "社区", + "old": "最旧", + "docs": "文档", + "upload_avatar": "上传头像", + "replies": "回复", + "number_online": "{{count}}名在线用户", + "mentions": "提到", + "message_sent": "已发送信息", + "old_password": "当前密码", + "forgot_password": "忘记密码", + "reset_password_mail_sent": "发送邮件重置密码。", + "password_change": "修改密码", + "new_password": "新密码", + "messages": "信息", + "no_email_setup": "此服务器还未正确设定邮箱。", + "matrix_user_id": "Matrix用户", + "private_message_disclaimer": "警告:Lemmy的私信功能并不安全。想要进行安全的信息传递,请在 <1>Riot.im上创建账号。", + "send_notifications_to_email": "向邮箱发送通知", + "language": "语言", + "browser_default": "默认浏览器", + "downvotes_disabled": "点踩功能已禁用", + "enable_downvotes": "启用点踩功能", + "upvote": "点赞", + "number_of_upvotes": "{{count}} 个赞", + "downvote": "点踩", + "number_of_downvotes": "{{count}} 个踩", + "open_registration": "开放注册", + "registration_closed": "注册功能已关闭", + "recent_comments": "最新评论", + "by": "来自", + "transfer_community": "节点转让", + "transfer_site": "站点转让", + "post_title_too_long": "帖子标题过长。", + "couldnt_get_comments": "无法获取评论。", + "email_already_exists": "该邮箱已被占用。", + "couldnt_create_private_message": "无法创建私信。", + "no_private_message_edit_allowed": "没有编辑私信的权限。", + "couldnt_update_private_message": "无法更新私信。", + "time": "时间", + "action": "行动", + "block_leaving": "确定要离开吗?", + "show_context": "显示上下文" } From 7ce845cde60ebe0f08dfd8121890fe1941367b73 Mon Sep 17 00:00:00 2001 From: Fenglei Gu Date: Mon, 20 Apr 2020 08:02:06 +0000 Subject: [PATCH 11/20] Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (237 of 237 strings) Translation: Lemmy/lemmy Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/zh_Hans/ --- ui/translations/zh.json | 44 ++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/ui/translations/zh.json b/ui/translations/zh.json index bc054b368..d23de86a2 100644 --- a/ui/translations/zh.json +++ b/ui/translations/zh.json @@ -1,25 +1,25 @@ { "post": "帖子", "remove_post": "移除帖子", - "no_posts": "没有帖子.", + "no_posts": "没有帖子。", "create_a_post": "创建新帖子", "create_post": "创建帖子", "number_of_posts": "{{count}} 个帖子", "posts": "帖子", - "related_posts": "相关的帖子", + "related_posts": "这些帖子可能相关", "comments": "评论", "number_of_comments": "{{count}} 条评论", "remove_comment": "移除评论", - "communities": "节点", - "create_a_community": "创建新节点", - "create_community": "创建节点", - "remove_community": "移除节点", - "subscribed_to_communities": "订阅新 <1>节点", - "trending_communities": "<1>节点趋势", - "list_of_communities": "节点列表", - "community_reqs": "包含小写与下划线且没有空格的字符串.", + "communities": "社群", + "create_a_community": "创建新社群", + "create_community": "创建社群", + "remove_community": "移除社群", + "subscribed_to_communities": "订阅新 <1>社群", + "trending_communities": "热门<1>社群", + "list_of_communities": "社群列表", + "community_reqs": "小写字母、下划线(_)且不含空格。", "edit": "编辑", - "reply": "回应", + "reply": "回复", "cancel": "取消", "unlock": "解锁", "lock": "加锁", @@ -90,7 +90,7 @@ "login_sign_up": "登录/注册", "login": "登录", "sign_up": "注册", - "notifications_error": "你的浏览器不支持桌面通知,尝试 Firefox 或 Chrome", + "notifications_error": "你的浏览器不支持桌面通知,尝试 Firefox 或 Chrome。", "unread_messages": "未读消息", "password": "密码", "verify_password": "确认密码", @@ -106,16 +106,16 @@ "chat": "聊天", "no_results": "没有结果.", "setup": "设置", - "lemmy_instance_setup": "Lemmy Instance Setup", + "lemmy_instance_setup": "创设 Lemmy 实例", "setup_admin": "设置管理员", "your_site": "你的站点", "modified": "修改", "sponsors": "赞助者", "sponsors_of_lemmy": "Lemmy 的赞助者", - "sponsor_message": "Lemmy 是自由的<1>开源 软件, 也就是说,决不会有广告、盈利行为和风险投资介入。您的捐款将被直接用于支持项目的开发。感谢以下诸位:", + "sponsor_message": "Lemmy 是自由的<1>开源软件,这意味着它决不会有广告、盈利行为和风险投资介入。您的捐款将被直接用于支持项目的开发。感谢以下诸位:", "support_on_patreon": "在 Patreon 支持", "support_on_liberapay": "在 Liberapay支持", - "general_sponsors": "General Sponsors are those that pledged $10 to $39 to Lemmy.", + "general_sponsors": "向Lemmy捐助10至39美元者为普通赞助人。", "crypto": "加密货币", "bitcoin": "比特币", "ethereum": "以太币", @@ -124,8 +124,8 @@ "powered_by": "保留所有权利", "landing_0": "Lemmy is a <1>link aggregator / reddit alternative, intended to work in the <2>fediverse.<3>It's self-hostable, has live-updating comment threads, and is tiny (<4>~80kB). Federation into the ActivityPub network is on the roadmap. <5>This is a <6>very early beta version, and a lot of features are currently broken or missing. <7>Suggest new features or report bugs <8>here.<9>Made with <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.", "not_logged_in": "未登录.", - "community_ban": "你被此节点禁止.", - "site_ban": "你被此站点禁止", + "community_ban": "你已被此社群拉黑。", + "site_ban": "你已被本站拉黑", "couldnt_create_comment": "不能创建评论.", "couldnt_like_comment": "无法点赞评论。", "couldnt_update_comment": "不能更新评论.", @@ -156,8 +156,8 @@ "user_already_exists": "用户已存在。", "couldnt_update_user": "无法更新用户。", "system_err_login": "系统错误。请尝试注销后重新登入。", - "nsfw": "工作场所不宜", - "show_nsfw": "显示工作场所不宜内容", + "nsfw": "少儿不宜", + "show_nsfw": "显示少儿不宜内容", "theme": "主题", "from": "来自", "donate_to_lemmy": "向Lemmy捐赠", @@ -167,7 +167,7 @@ "are_you_sure": "你确定吗?", "yes": "是", "no": "否", - "logged_in": "已登陆", + "logged_in": "已登录。", "message": "信息", "create_private_message": "创建私信", "send_secure_message": "发送安全信息", @@ -175,7 +175,7 @@ "more": "更多", "preview": "预览", "upload_image": "上传图片", - "enable_nsfw": "允许工作场所不宜", + "enable_nsfw": "允许少儿不宜内容", "show_avatars": "显示头像", "avatar": "头像", "formatting_help": "格式帮助", @@ -194,7 +194,7 @@ "users": "用户", "banned": "已被禁止", "creator": "发布者", - "number_of_communities": "社区", + "number_of_communities": "{{count}} 个社群", "old": "最旧", "docs": "文档", "upload_avatar": "上传头像", From c18fa5a38e36f764c8020ff3ed56c92bccc909f6 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Mon, 20 Apr 2020 10:45:14 -0400 Subject: [PATCH 12/20] Changing warn to debug for rate limiting message. --- server/src/rate_limit/mod.rs | 2 +- server/src/rate_limit/rate_limiter.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/rate_limit/mod.rs b/server/src/rate_limit/mod.rs index 29a3a9e14..a95c51b5e 100644 --- a/server/src/rate_limit/mod.rs +++ b/server/src/rate_limit/mod.rs @@ -3,7 +3,7 @@ pub mod rate_limiter; use super::{IPAddr, Settings}; use crate::api::APIError; use failure::Error; -use log::warn; +use log::debug; use rate_limiter::RateLimiter; use std::collections::HashMap; use std::sync::Arc; diff --git a/server/src/rate_limit/rate_limiter.rs b/server/src/rate_limit/rate_limiter.rs index 6b01a75b2..47bb3a1cc 100644 --- a/server/src/rate_limit/rate_limiter.rs +++ b/server/src/rate_limit/rate_limiter.rs @@ -105,7 +105,7 @@ impl RateLimiter { } if rate_limit.allowance < 1.0 { - warn!( + debug!( "Rate limited IP: {}, time_passed: {}, allowance: {}", ip, time_passed, rate_limit.allowance ); From db4454b52b8e8f71d35ca2b874ee279ed49a5317 Mon Sep 17 00:00:00 2001 From: asonix Date: Mon, 20 Apr 2020 10:25:47 -0500 Subject: [PATCH 13/20] Add websockets into the api scope --- server/src/main.rs | 3 +-- server/src/routes/api.rs | 2 ++ server/src/routes/websocket.rs | 6 +----- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/server/src/main.rs b/server/src/main.rs index 6abb22439..c92770f2a 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -8,7 +8,7 @@ use diesel::r2d2::{ConnectionManager, Pool}; use diesel::PgConnection; use lemmy_server::{ rate_limit::{rate_limiter::RateLimiter, RateLimit}, - routes::{api, federation, feeds, index, nodeinfo, webfinger, websocket}, + routes::{api, federation, feeds, index, nodeinfo, webfinger}, settings::Settings, websocket::server::*, }; @@ -59,7 +59,6 @@ async fn main() -> io::Result<()> { .configure(index::config) .configure(nodeinfo::config) .configure(webfinger::config) - .configure(websocket::config) // static files .service(actix_files::Files::new( "/static", diff --git a/server/src/routes/api.rs b/server/src/routes/api.rs index 3ef8ca510..025c6e0a7 100644 --- a/server/src/routes/api.rs +++ b/server/src/routes/api.rs @@ -10,6 +10,8 @@ use actix_web::guard; pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimit) { cfg.service( web::scope("/api/v1") + // Websockets + .service(web::resource("/ws").to(super::websocket::chat_route)) // Site .service( web::scope("/site") diff --git a/server/src/routes/websocket.rs b/server/src/routes/websocket.rs index c6bca9aa0..eb1575906 100644 --- a/server/src/routes/websocket.rs +++ b/server/src/routes/websocket.rs @@ -2,17 +2,13 @@ use super::*; use crate::websocket::server::*; use actix_web::{Error, Result}; -pub fn config(cfg: &mut web::ServiceConfig) { - cfg.service(web::resource("/api/v1/ws").to(chat_route)); -} - /// How often heartbeat pings are sent const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5); /// How long before lack of client response causes a timeout const CLIENT_TIMEOUT: Duration = Duration::from_secs(10); /// Entry point for our route -async fn chat_route( +pub async fn chat_route( req: HttpRequest, stream: web::Payload, chat_server: web::Data>, From 904ebf34a5054f5234c7914fc38a253de99b187d Mon Sep 17 00:00:00 2001 From: asonix Date: Mon, 20 Apr 2020 12:31:22 -0500 Subject: [PATCH 14/20] Guard CreateCommunity, represent impossible error with Infallible --- server/src/routes/api.rs | 7 ++++++- server/src/routes/websocket.rs | 2 +- server/src/websocket/server.rs | 5 +++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/server/src/routes/api.rs b/server/src/routes/api.rs index 025c6e0a7..629bd49da 100644 --- a/server/src/routes/api.rs +++ b/server/src/routes/api.rs @@ -40,10 +40,15 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimit) { .route(web::get().to(route_get::)), ) // Community + .service( + web::resource("/community") + .guard(guard::Post()) + .wrap(rate_limit.post()) + .route(web::post().to(route_post::)), + ) .service( web::scope("/community") .wrap(rate_limit.message()) - .route("", web::post().to(route_post::)) .route("", web::get().to(route_get::)) .route("", web::put().to(route_post::)) .route("/list", web::get().to(route_get::)) diff --git a/server/src/routes/websocket.rs b/server/src/routes/websocket.rs index eb1575906..e1efdc0f5 100644 --- a/server/src/routes/websocket.rs +++ b/server/src/routes/websocket.rs @@ -120,7 +120,7 @@ impl StreamHandler> for WSSession { .then(|res, _, ctx| { match res { Ok(Ok(res)) => ctx.text(res), - Ok(Err(e)) => error!("{}", e), + Ok(Err(e)) => match e {}, Err(e) => error!("{}", &e), } actix::fut::ready(()) diff --git a/server/src/websocket/server.rs b/server/src/websocket/server.rs index d16ecf854..cd78bbc7f 100644 --- a/server/src/websocket/server.rs +++ b/server/src/websocket/server.rs @@ -38,7 +38,7 @@ pub struct Disconnect { /// The messages sent to websocket clients #[derive(Serialize, Deserialize, Message)] -#[rtype(result = "Result")] +#[rtype(result = "Result")] pub struct StandardMessage { /// Id of the client session pub id: ConnectionId, @@ -905,6 +905,7 @@ where match op2 { UserOperation::Register => rate_limiter.register().wrap(ip, fut).await, UserOperation::CreatePost => rate_limiter.post().wrap(ip, fut).await, + UserOperation::CreateCommunity => rate_limiter.post().wrap(ip, fut).await, _ => rate_limiter.message().wrap(ip, fut).await, } } @@ -963,7 +964,7 @@ impl Handler for ChatServer { /// Handler for Message message. impl Handler for ChatServer { - type Result = ResponseFuture>; + type Result = ResponseFuture>; fn handle(&mut self, msg: StandardMessage, ctx: &mut Context) -> Self::Result { let fut = self.parse_json_message(msg, ctx); From 4df2031ae035e59c4bbf060c2fad6bfe5e30e256 Mon Sep 17 00:00:00 2001 From: asonix Date: Mon, 20 Apr 2020 12:51:42 -0500 Subject: [PATCH 15/20] Give ratelimit fields explicit names --- server/src/main.rs | 4 ++- server/src/rate_limit/mod.rs | 49 ++++++++++++++++++++++++------------ 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/server/src/main.rs b/server/src/main.rs index c92770f2a..4e773ee57 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -34,7 +34,9 @@ async fn main() -> io::Result<()> { embedded_migrations::run(&conn).unwrap(); // Set up the rate limiter - let rate_limiter = RateLimit(Arc::new(Mutex::new(RateLimiter::default()))); + let rate_limiter = RateLimit { + rate_limiter: Arc::new(Mutex::new(RateLimiter::default())), + }; // Set up websocket server let server = ChatServer::startup(pool.clone(), rate_limiter.clone()).start(); diff --git a/server/src/rate_limit/mod.rs b/server/src/rate_limit/mod.rs index 9aeb11718..bb77db29c 100644 --- a/server/src/rate_limit/mod.rs +++ b/server/src/rate_limit/mod.rs @@ -18,12 +18,20 @@ use strum::IntoEnumIterator; use tokio::sync::Mutex; #[derive(Debug, Clone)] -pub struct RateLimit(pub Arc>); +pub struct RateLimit { + pub rate_limiter: Arc>, +} #[derive(Debug, Clone)] -pub struct RateLimited(Arc>, RateLimitType); +pub struct RateLimited { + rate_limiter: Arc>, + type_: RateLimitType, +} -pub struct RateLimitedMiddleware(RateLimited, S); +pub struct RateLimitedMiddleware { + rate_limited: RateLimited, + service: S, +} impl RateLimit { pub fn message(&self) -> RateLimited { @@ -39,7 +47,10 @@ impl RateLimit { } fn kind(&self, type_: RateLimitType) -> RateLimited { - RateLimited(self.0.clone(), type_) + RateLimited { + rate_limiter: self.rate_limiter.clone(), + type_, + } } } @@ -64,12 +75,12 @@ impl RateLimited { // before { - let mut limiter = self.0.lock().await; + let mut limiter = self.rate_limiter.lock().await; - match self.1 { + match self.type_ { RateLimitType::Message => { limiter.check_rate_limit_full( - self.1, + self.type_, &ip_addr, rate_limit.message, rate_limit.message_per_second, @@ -80,7 +91,7 @@ impl RateLimited { } RateLimitType::Post => { limiter.check_rate_limit_full( - self.1.clone(), + self.type_.clone(), &ip_addr, rate_limit.post, rate_limit.post_per_second, @@ -89,7 +100,7 @@ impl RateLimited { } RateLimitType::Register => { limiter.check_rate_limit_full( - self.1, + self.type_, &ip_addr, rate_limit.register, rate_limit.register_per_second, @@ -103,12 +114,12 @@ impl RateLimited { // after { - let mut limiter = self.0.lock().await; + let mut limiter = self.rate_limiter.lock().await; if res.is_ok() { - match self.1 { + match self.type_ { RateLimitType::Post => { limiter.check_rate_limit_full( - self.1, + self.type_, &ip_addr, rate_limit.post, rate_limit.post_per_second, @@ -117,7 +128,7 @@ impl RateLimited { } RateLimitType::Register => { limiter.check_rate_limit_full( - self.1, + self.type_, &ip_addr, rate_limit.register, rate_limit.register_per_second, @@ -146,7 +157,10 @@ where type Future = Ready>; fn new_transform(&self, service: S) -> Self::Future { - ok(RateLimitedMiddleware(self.clone(), service)) + ok(RateLimitedMiddleware { + rate_limited: self.clone(), + service, + }) } } @@ -163,7 +177,7 @@ where type Future = Pin>>; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - self.1.poll_ready(cx) + self.service.poll_ready(cx) } fn call(&mut self, req: S::Request) -> Self::Future { @@ -176,7 +190,10 @@ where .unwrap_or("127.0.0.1") .to_string(); - let fut = self.0.clone().wrap(ip_addr, self.1.call(req)); + let fut = self + .rate_limited + .clone() + .wrap(ip_addr, self.service.call(req)); Box::pin(async move { fut.await.map_err(actix_web::Error::from) }) } From d058db95e5d642ff79416ad6a11c2707b667517f Mon Sep 17 00:00:00 2001 From: asonix Date: Mon, 20 Apr 2020 13:02:25 -0500 Subject: [PATCH 16/20] Use get_ip in ratelimit middleware --- server/src/lib.rs | 7 +++---- server/src/rate_limit/mod.rs | 10 ++-------- server/src/routes/websocket.rs | 2 +- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/server/src/lib.rs b/server/src/lib.rs index 2d0ecc3a3..79a1437e7 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -34,7 +34,7 @@ pub mod settings; pub mod version; pub mod websocket; -use actix_web::HttpRequest; +use actix_web::dev::ConnectionInfo; use chrono::{DateTime, NaiveDateTime, Utc}; use isahc::prelude::*; use lettre::smtp::authentication::{Credentials, Mechanism}; @@ -233,9 +233,8 @@ pub fn markdown_to_html(text: &str) -> String { comrak::markdown_to_html(text, &comrak::ComrakOptions::default()) } -pub fn get_ip(req: &HttpRequest) -> String { - req - .connection_info() +pub fn get_ip(conn_info: &ConnectionInfo) -> String { + conn_info .remote() .unwrap_or("127.0.0.1:12345") .split(':') diff --git a/server/src/rate_limit/mod.rs b/server/src/rate_limit/mod.rs index bb77db29c..de45002eb 100644 --- a/server/src/rate_limit/mod.rs +++ b/server/src/rate_limit/mod.rs @@ -2,6 +2,7 @@ pub mod rate_limiter; use super::{IPAddr, Settings}; use crate::api::APIError; +use crate::get_ip; use crate::settings::RateLimitConfig; use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform}; use failure::Error; @@ -181,14 +182,7 @@ where } fn call(&mut self, req: S::Request) -> Self::Future { - let ip_addr = req - .connection_info() - .remote() - .unwrap_or("127.0.0.1:12345") - .split(':') - .next() - .unwrap_or("127.0.0.1") - .to_string(); + let ip_addr = get_ip(&req.connection_info()); let fut = self .rate_limited diff --git a/server/src/routes/websocket.rs b/server/src/routes/websocket.rs index e1efdc0f5..808859451 100644 --- a/server/src/routes/websocket.rs +++ b/server/src/routes/websocket.rs @@ -18,7 +18,7 @@ pub async fn chat_route( cs_addr: chat_server.get_ref().to_owned(), id: 0, hb: Instant::now(), - ip: get_ip(&req), + ip: get_ip(&req.connection_info()), }, &req, stream, From d11af012fc1ba2de7684235ef92bf5954a5dab26 Mon Sep 17 00:00:00 2001 From: asonix Date: Mon, 20 Apr 2020 13:05:07 -0500 Subject: [PATCH 17/20] post -> register --- server/src/routes/api.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/routes/api.rs b/server/src/routes/api.rs index 629bd49da..74ad7239b 100644 --- a/server/src/routes/api.rs +++ b/server/src/routes/api.rs @@ -43,7 +43,7 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimit) { .service( web::resource("/community") .guard(guard::Post()) - .wrap(rate_limit.post()) + .wrap(rate_limit.register()) .route(web::post().to(route_post::)), ) .service( From 05b961a90a2b049f391fec7a78aa9fb4db6c3ed2 Mon Sep 17 00:00:00 2001 From: asonix Date: Mon, 20 Apr 2020 13:09:10 -0500 Subject: [PATCH 18/20] Remove unused methods --- server/src/rate_limit/rate_limiter.rs | 30 --------------------------- 1 file changed, 30 deletions(-) diff --git a/server/src/rate_limit/rate_limiter.rs b/server/src/rate_limit/rate_limiter.rs index 8f598c3dd..926e0989f 100644 --- a/server/src/rate_limit/rate_limiter.rs +++ b/server/src/rate_limit/rate_limiter.rs @@ -48,36 +48,6 @@ impl RateLimiter { } } - pub fn check_rate_limit_register(&mut self, ip: &str, check_only: bool) -> Result<(), Error> { - self.check_rate_limit_full( - RateLimitType::Register, - ip, - Settings::get().rate_limit.register, - Settings::get().rate_limit.register_per_second, - check_only, - ) - } - - pub fn check_rate_limit_post(&mut self, ip: &str, check_only: bool) -> Result<(), Error> { - self.check_rate_limit_full( - RateLimitType::Post, - ip, - Settings::get().rate_limit.post, - Settings::get().rate_limit.post_per_second, - check_only, - ) - } - - pub fn check_rate_limit_message(&mut self, ip: &str, check_only: bool) -> Result<(), Error> { - self.check_rate_limit_full( - RateLimitType::Message, - ip, - Settings::get().rate_limit.message, - Settings::get().rate_limit.message_per_second, - check_only, - ) - } - #[allow(clippy::float_cmp)] pub(super) fn check_rate_limit_full( &mut self, From 0d4679a37ba051561389b236e64e00c6be3569f3 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Mon, 20 Apr 2020 14:55:37 -0400 Subject: [PATCH 19/20] Making websocket create community do register rate limit. --- server/src/websocket/server.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/websocket/server.rs b/server/src/websocket/server.rs index cd78bbc7f..0e7c1a4af 100644 --- a/server/src/websocket/server.rs +++ b/server/src/websocket/server.rs @@ -905,7 +905,7 @@ where match op2 { UserOperation::Register => rate_limiter.register().wrap(ip, fut).await, UserOperation::CreatePost => rate_limiter.post().wrap(ip, fut).await, - UserOperation::CreateCommunity => rate_limiter.post().wrap(ip, fut).await, + UserOperation::CreateCommunity => rate_limiter.register().wrap(ip, fut).await, _ => rate_limiter.message().wrap(ip, fut).await, } } From b98fa1274151f61b13679aa30f332686bc9645f3 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Mon, 20 Apr 2020 15:40:56 -0400 Subject: [PATCH 20/20] Version v0.6.51 --- ansible/VERSION | 2 +- docker/prod/docker-compose.yml | 2 +- server/src/version.rs | 2 +- ui/src/version.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ansible/VERSION b/ansible/VERSION index 2102553df..e39e33bc0 100644 --- a/ansible/VERSION +++ b/ansible/VERSION @@ -1 +1 @@ -v0.6.50 +v0.6.51 diff --git a/docker/prod/docker-compose.yml b/docker/prod/docker-compose.yml index 23f0583a5..c38bb8996 100644 --- a/docker/prod/docker-compose.yml +++ b/docker/prod/docker-compose.yml @@ -12,7 +12,7 @@ services: restart: always lemmy: - image: dessalines/lemmy:v0.6.50 + image: dessalines/lemmy:v0.6.51 ports: - "127.0.0.1:8536:8536" restart: always diff --git a/server/src/version.rs b/server/src/version.rs index 2fb90238c..a287e3a46 100644 --- a/server/src/version.rs +++ b/server/src/version.rs @@ -1 +1 @@ -pub const VERSION: &str = "v0.6.50"; +pub const VERSION: &str = "v0.6.51"; diff --git a/ui/src/version.ts b/ui/src/version.ts index c3abebdeb..eb0ecff3c 100644 --- a/ui/src/version.ts +++ b/ui/src/version.ts @@ -1 +1 @@ -export const version: string = 'v0.6.50'; +export const version: string = 'v0.6.51';