From be3bdb7b704c9b333b1b9364e0d4120b6cd4548a Mon Sep 17 00:00:00 2001 From: rakema01 <99795534+rakema01@users.noreply.github.com> Date: Tue, 11 Feb 2025 03:57:50 -0800 Subject: [PATCH 1/4] Update user.rs Backport upstream backend PR #58 --- backend/src/handlers/user.rs | 46 +++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/backend/src/handlers/user.rs b/backend/src/handlers/user.rs index b5e35389..f95b6e5c 100644 --- a/backend/src/handlers/user.rs +++ b/backend/src/handlers/user.rs @@ -26,8 +26,12 @@ pub fn init_routes(cfg: &mut web::ServiceConfig) { .route(web::post().to(login))) .service(web::resource("/ban/{user}") .route(web::delete().to(ban_user))) - .service(web::resource("/verify/{token}") - .route(web::get().to(verify_user))) + .service(web::resource("/token/verify") + .route(web::post().to(verify_token))) + .service(web::resource("/token/renew") + .route(web::post().to(renew_token))) + .service(web::resource("/email/verify/{token}") + .route(web::get().to(verify_email))) ); } @@ -45,6 +49,11 @@ pub struct Login { pub password: String, } +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Token { + pub token: String, +} + pub async fn register(req: HttpRequest, mut payload: web::Json, app_data: WebAppData) -> ServiceResult { let settings = app_data.cfg.settings.read().await; @@ -178,8 +187,39 @@ pub async fn login(payload: web::Json, app_data: WebAppData) -> ServiceRe None => Err(ServiceError::WrongPasswordOrUsername) } } +pub async fn verify_token(payload: web::Json, app_data: WebAppData) -> ServiceResult { + // verify if token is valid + let _claims = app_data.auth.verify_jwt(&payload.token).await?; + + Ok(HttpResponse::Ok().json(OkResponse { + data: format!("Token is valid.") + })) +} + +pub async fn renew_token(payload: web::Json, app_data: WebAppData) -> ServiceResult { + // verify if token is valid + let claims = app_data.auth.verify_jwt(&payload.token).await?; + + let user_compact = app_data.database.get_user_compact_from_id(claims.user.user_id).await?; + + const ONE_WEEK_IN_SECONDS: u64 = 604_800; + + // renew token if it is valid for less than one week + let token = match claims.exp - current_time() { + x if x < ONE_WEEK_IN_SECONDS => app_data.auth.sign_jwt(user_compact.clone()).await, + _ => payload.token.clone() + }; + + Ok(HttpResponse::Ok().json(OkResponse { + data: TokenResponse { + token, + username: user_compact.username, + admin: user_compact.administrator + } + })) +} -pub async fn verify_user(req: HttpRequest, app_data: WebAppData) -> String { +pub async fn verify_email(req: HttpRequest, app_data: WebAppData) -> String { let settings = app_data.cfg.settings.read().await; let token = req.match_info().get("token").unwrap(); From e2dae9ccbe7b48ec9d04235f772bf11aeb3a68de Mon Sep 17 00:00:00 2001 From: rakema01 <99795534+rakema01@users.noreply.github.com> Date: Tue, 11 Feb 2025 03:59:32 -0800 Subject: [PATCH 2/4] Update mailer.rs Backport upstream backend PR torrust/torrust-index#58 --- backend/src/mailer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/mailer.rs b/backend/src/mailer.rs index e39fc5d1..9e396bb2 100644 --- a/backend/src/mailer.rs +++ b/backend/src/mailer.rs @@ -137,7 +137,7 @@ If this account wasn't made by you, you can ignore this email. base_url = cfg_base_url; } - format!("{}/user/verify/{}", base_url, token) + format!("{}/user/email/verify/{}", base_url, token) } } From 4a760bb88ba4024a417b56aab58ed4db57aae3b6 Mon Sep 17 00:00:00 2001 From: sudisk Date: Wed, 14 May 2025 08:23:20 +0200 Subject: [PATCH 3/4] Token renew + updated for Rust 1.83 (#26) * remove odd bar under search box * reverting * take 2 - remove buggy white bar under search * Add click handling to magnifying glass icon on sidebar Epic * - Fix token renewal management and add configuration parameters - Bump some crate versions for recent Rust versions. - Improve logging capability - Bump crate versions for Rust update - Finalize renew token - Add logging support * - Fix token renewal management and add configuration parameters - Bump some crate versions for recent Rust versions. - Improve logging capability * Lower log level and remove unnecessary logs in user.rs --------- Co-authored-by: 63A9F0EA7BB98050796B649E85481845 Co-authored-by: rakema01 <99795534+rakema01@users.noreply.github.com> --- .gitignore | 1 + backend/Cargo.lock | 6982 +++++++++-------- backend/Cargo.toml | 125 +- backend/src/auth.rs | 217 +- backend/src/common.rs | 73 +- backend/src/config.rs | 401 +- backend/src/database.rs | 440 +- backend/src/handlers/torrent.rs | 1041 +-- backend/src/handlers/user.rs | 805 +- backend/src/mailer.rs | 282 +- backend/src/main.rs | 179 +- backend/src/models/user.rs | 54 +- backend/src/tracker.rs | 314 +- .../src/components/navigation/Sidebar.vue | 5 +- 14 files changed, 6103 insertions(+), 4816 deletions(-) diff --git a/.gitignore b/.gitignore index 5fc07205..fbb1ad73 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ backend/package-lock.json /frontend/.env /backend/.env +/backend/.sqlx /backend/config.toml /backend/data.db /backend/data.db-shm diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 8ae92e63..d6bf29f3 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -1,3090 +1,3892 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "actix-codec" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d5dbeb2d9e51344cb83ca7cc170f1217f9fe25bfc50160e6e200b5c31c1019a" -dependencies = [ - "bitflags", - "bytes", - "futures-core", - "futures-sink", - "log", - "pin-project-lite", - "tokio", - "tokio-util 0.6.7", -] - -[[package]] -name = "actix-cors" -version = "0.6.0-beta.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01552b8facccd5d7a4cc5d8e2b07d306160c97a4968181c2db965533389c8725" -dependencies = [ - "actix-service", - "actix-web", - "derive_more", - "futures-util", - "log", - "once_cell", - "smallvec", -] - -[[package]] -name = "actix-http" -version = "3.0.0-beta.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01260589f1aafad11224002741eb37bc603b4ce55b4e3556d2b2122f9aac7c51" -dependencies = [ - "actix-codec", - "actix-rt", - "actix-service", - "actix-tls", - "actix-utils", - "ahash", - "base64 0.13.0", - "bitflags", - "brotli2", - "bytes", - "bytestring", - "derive_more", - "encoding_rs", - "flate2", - "futures-core", - "futures-util", - "h2", - "http", - "httparse", - "itoa", - "language-tags", - "local-channel", - "log", - "mime", - "once_cell", - "percent-encoding", - "pin-project", - "pin-project-lite", - "rand", - "regex", - "serde 1.0.126", - "sha-1", - "smallvec", - "time 0.2.27", - "tokio", - "zstd", -] - -[[package]] -name = "actix-macros" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2f86cd6857c135e6e9fe57b1619a88d1f94a7df34c00e11fe13e64fd3438837" -dependencies = [ - "quote", - "syn", -] - -[[package]] -name = "actix-multipart" -version = "0.4.0-beta.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a32d8964e147f1e411b38cd08a28eb37915be6797191a394fe0ad73f36441a99" -dependencies = [ - "actix-utils", - "actix-web", - "bytes", - "derive_more", - "futures-core", - "futures-util", - "httparse", - "local-waker", - "log", - "mime", - "twoway", -] - -[[package]] -name = "actix-router" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ad299af73649e1fc893e333ccf86f377751eb95ff875d095131574c6f43452c" -dependencies = [ - "bytestring", - "http", - "log", - "regex", - "serde 1.0.126", -] - -[[package]] -name = "actix-rt" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc7d7cd957c9ed92288a7c3c96af81fa5291f65247a76a34dac7b6af74e52ba0" -dependencies = [ - "actix-macros", - "futures-core", - "tokio", -] - -[[package]] -name = "actix-server" -version = "2.0.0-beta.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26369215fcc3b0176018b3b68756a8bcc275bb000e6212e454944913a1f9bf87" -dependencies = [ - "actix-rt", - "actix-service", - "actix-utils", - "futures-core", - "log", - "mio", - "num_cpus", - "slab", - "tokio", -] - -[[package]] -name = "actix-service" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77f5f9d66a8730d0fae62c26f3424f5751e5518086628a40b7ab6fca4a705034" -dependencies = [ - "futures-core", - "paste", - "pin-project-lite", -] - -[[package]] -name = "actix-tls" -version = "3.0.0-beta.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65b7bb60840962ef0332f7ea01a57d73a24d2cb663708511ff800250bbfef569" -dependencies = [ - "actix-codec", - "actix-rt", - "actix-service", - "actix-utils", - "derive_more", - "futures-core", - "http", - "log", - "tokio-util 0.6.7", -] - -[[package]] -name = "actix-utils" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e491cbaac2e7fc788dfff99ff48ef317e23b3cf63dbaf7aaab6418f40f92aa94" -dependencies = [ - "local-waker", - "pin-project-lite", -] - -[[package]] -name = "actix-web" -version = "4.0.0-beta.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c503f726f895e55dac39adeafd14b5ee00cc956796314e9227fc7ae2e176f443" -dependencies = [ - "actix-codec", - "actix-http", - "actix-macros", - "actix-router", - "actix-rt", - "actix-server", - "actix-service", - "actix-utils", - "actix-web-codegen", - "ahash", - "bytes", - "cfg-if", - "cookie", - "derive_more", - "either", - "encoding_rs", - "futures-core", - "futures-util", - "itoa", - "language-tags", - "log", - "mime", - "once_cell", - "paste", - "pin-project", - "regex", - "serde 1.0.126", - "serde_json", - "serde_urlencoded", - "smallvec", - "socket2", - "time 0.2.27", - "url", -] - -[[package]] -name = "actix-web-codegen" -version = "0.5.0-beta.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d048c6986743105c1e8e9729fbc8d5d1667f2f62393a58be8d85a7d9a5a6c8d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "ahash" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43bb833f0bf979d8475d38fbf09ed3b8a55e1885fe93ad3f93239fc6a4f17b98" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - -[[package]] -name = "aho-corasick" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" -dependencies = [ - "memchr", -] - -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - -[[package]] -name = "async-channel" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" -dependencies = [ - "concurrent-queue", - "event-listener", - "futures-core", -] - -[[package]] -name = "async-executor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" -dependencies = [ - "async-task", - "concurrent-queue", - "fastrand", - "futures-lite", - "once_cell", - "slab", -] - -[[package]] -name = "async-global-executor" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9586ec52317f36de58453159d48351bc244bc24ced3effc1fce22f3d48664af6" -dependencies = [ - "async-channel", - "async-executor", - "async-io", - "async-mutex", - "blocking", - "futures-lite", - "num_cpus", - "once_cell", -] - -[[package]] -name = "async-io" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a811e6a479f2439f0c04038796b5cfb3d2ad56c230e0f2d3f7b04d68cfee607b" -dependencies = [ - "concurrent-queue", - "futures-lite", - "libc", - "log", - "once_cell", - "parking", - "polling", - "slab", - "socket2", - "waker-fn", - "winapi", -] - -[[package]] -name = "async-lock" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6a8ea61bf9947a1007c5cada31e647dbc77b103c679858150003ba697ea798b" -dependencies = [ - "event-listener", -] - -[[package]] -name = "async-mutex" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" -dependencies = [ - "event-listener", -] - -[[package]] -name = "async-std" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8056f1455169ab86dd47b47391e4ab0cbd25410a70e9fe675544f49bafaf952" -dependencies = [ - "async-channel", - "async-global-executor", - "async-io", - "async-lock", - "crossbeam-utils", - "futures-channel", - "futures-core", - "futures-io", - "futures-lite", - "gloo-timers", - "kv-log-macro", - "log", - "memchr", - "num_cpus", - "once_cell", - "pin-project-lite", - "pin-utils", - "slab", - "wasm-bindgen-futures", -] - -[[package]] -name = "async-task" -version = "4.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0" - -[[package]] -name = "async-trait" -version = "0.1.51" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44318e776df68115a881de9a8fd1b9e53368d7a4a5ce4cc48517da3393233a5e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "atoi" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616896e05fc0e2649463a93a15183c6a16bf03413a7af88ef1285ddedfa9cda5" -dependencies = [ - "num-traits 0.2.14", -] - -[[package]] -name = "atomic-waker" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" - -[[package]] -name = "autocfg" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" - -[[package]] -name = "base-x" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" - -[[package]] -name = "base64" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" - -[[package]] -name = "base64" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" - -[[package]] -name = "base64ct" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a32fd6af2b5827bce66c29053ba0e7c42b9dcab01835835058558c10851a46b" - -[[package]] -name = "binascii" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72" - -[[package]] -name = "bitflags" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" - -[[package]] -name = "bitvec" -version = "0.19.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8942c8d352ae1838c9dda0b0ca2ab657696ef2232a20147cf1b30ae1a9cb4321" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - -[[package]] -name = "blocking" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e170dbede1f740736619b776d7251cb1b9095c435c34d8ca9f57fcd2f335e9" -dependencies = [ - "async-channel", - "async-task", - "atomic-waker", - "fastrand", - "futures-lite", - "once_cell", -] - -[[package]] -name = "brotli-sys" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4445dea95f4c2b41cde57cc9fee236ae4dbae88d8fcbdb4750fc1bb5d86aaecd" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "brotli2" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cb036c3eade309815c15ddbacec5b22c4d1f3983a774ab2eac2e3e9ea85568e" -dependencies = [ - "brotli-sys", - "libc", -] - -[[package]] -name = "bumpalo" -version = "3.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" - -[[package]] -name = "bytestring" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90706ba19e97b90786e19dc0d5e2abd80008d99d4c0c5d1ad0b5e72cec7c494d" -dependencies = [ - "bytes", -] - -[[package]] -name = "cache-padded" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" - -[[package]] -name = "cc" -version = "1.0.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787" -dependencies = [ - "jobserver", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" -dependencies = [ - "libc", - "num-integer", - "num-traits 0.2.14", - "time 0.1.43", - "winapi", -] - -[[package]] -name = "concurrent-queue" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" -dependencies = [ - "cache-padded", -] - -[[package]] -name = "config" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1b9d958c2b1368a663f05538fc1b5975adce1e19f435acceae987aceeeb369" -dependencies = [ - "lazy_static", - "nom 5.1.2", - "rust-ini", - "serde 1.0.126", - "serde-hjson", - "serde_json", - "toml", - "yaml-rust", -] - -[[package]] -name = "const_fn" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f92cfa0fd5690b3cf8c1ef2cabbd9b7ef22fa53cf5e1f92b05103f6d5d1cf6e7" - -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - -[[package]] -name = "cookie" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f1c7727e460397e56abc4bddc1d49e07a1ad78fc98eb2e1c8f032a58a2f80d" -dependencies = [ - "percent-encoding", - "time 0.2.27", - "version_check", -] - -[[package]] -name = "core-foundation" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" - -[[package]] -name = "cpufeatures" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" -dependencies = [ - "libc", -] - -[[package]] -name = "crc" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10c2722795460108a7872e1cd933a85d6ec38abc4baecad51028f702da28889f" -dependencies = [ - "crc-catalog", -] - -[[package]] -name = "crc-catalog" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccaeedb56da03b09f598226e25e80088cb4cd25f316e6e4df7d695f0feeb1403" - -[[package]] -name = "crc32fast" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b10ddc024425c88c2ad148c1b0fd53f4c6d38db9697c9f1588381212fa657c9" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" -dependencies = [ - "cfg-if", - "lazy_static", -] - -[[package]] -name = "crypto-mac" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "ctor" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccc0a48a9b826acdf4028595adc9db92caea352f7af011a3034acd172a52a0aa" -dependencies = [ - "quote", - "syn", -] - -[[package]] -name = "darling" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f2c43f534ea4b0b049015d00269734195e6d3f0f6635cb692251aca6f9f8b3c" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e91455b86830a1c21799d94524df0845183fa55bafd9aa137b01c7d1065fa36" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn", -] - -[[package]] -name = "darling_macro" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29b5acf0dea37a7f66f7b25d2c5e93fd46f8f6968b1a5d7a3e02e97768afc95a" -dependencies = [ - "darling_core", - "quote", - "syn", -] - -[[package]] -name = "derive_builder" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d13202debe11181040ae9063d739fa32cfcaaebe2275fe387703460ae2365b30" -dependencies = [ - "derive_builder_macro", -] - -[[package]] -name = "derive_builder_core" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66e616858f6187ed828df7c64a6d71720d83767a7f19740b2d1b6fe6327b36e5" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "derive_builder_macro" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58a94ace95092c5acb1e97a7e846b310cfbd499652f72297da7493f618a98d73" -dependencies = [ - "derive_builder_core", - "syn", -] - -[[package]] -name = "derive_more" -version = "0.99.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc7b9cef1e351660e5443924e4f43ab25fbbed3e9a5f052df3677deb4d6b320" -dependencies = [ - "convert_case", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - -[[package]] -name = "discard" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" - -[[package]] -name = "dotenv" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" - -[[package]] -name = "dtoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" - -[[package]] -name = "either" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" - -[[package]] -name = "encoding_rs" -version = "0.8.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80df024fbc5ac80f87dfef0d9f5209a252f2a497f7f42944cff24d8253cac065" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "event-listener" -version = "2.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59" - -[[package]] -name = "fastrand" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b394ed3d285a429378d3b384b9eb1285267e7df4b166df24b7a6939a04dc392e" -dependencies = [ - "instant", -] - -[[package]] -name = "filetime" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "winapi", -] - -[[package]] -name = "flate2" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0" -dependencies = [ - "cfg-if", - "crc32fast", - "libc", - "miniz_oxide", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" -dependencies = [ - "matches", - "percent-encoding", -] - -[[package]] -name = "funty" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" - -[[package]] -name = "futures" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7e43a803dae2fa37c1f6a8fe121e1f7bf9548b4dfc0522a42f34145dadfc27" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e682a68b29a882df0545c143dc3646daefe80ba479bcdede94d5a703de2871e2" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0402f765d8a89a26043b889b26ce3c4679d268fa6bb22cd7c6aad98340e179d1" - -[[package]] -name = "futures-executor" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "badaa6a909fac9e7236d0620a2f57f7664640c56575b71a7552fbd68deafab79" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-intrusive" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62007592ac46aa7c2b6416f7deb9a8a8f63a01e0f1d6e1787d5630170db2b63e" -dependencies = [ - "futures-core", - "lock_api", - "parking_lot", -] - -[[package]] -name = "futures-io" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acc499defb3b348f8d8f3f66415835a9131856ff7714bf10dadfc4ec4bdb29a1" - -[[package]] -name = "futures-lite" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - -[[package]] -name = "futures-macro" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c40298486cdf52cc00cd6d6987892ba502c7656a16a4192a9992b1ccedd121" -dependencies = [ - "autocfg", - "proc-macro-hack", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "futures-sink" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a57bead0ceff0d6dde8f465ecd96c9338121bb7717d3e7b108059531870c4282" - -[[package]] -name = "futures-task" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a16bef9fc1a4dddb5bee51c989e3fbba26569cbb0e31f5b303c184e3dd33dae" - -[[package]] -name = "futures-util" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feb5c238d27e2bf94ffdfd27b2c29e3df4a68c4193bb6427384259e2bf191967" -dependencies = [ - "autocfg", - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "proc-macro-hack", - "proc-macro-nested", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "gloo-timers" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47204a46aaff920a1ea58b11d03dec6f704287d27561724a4631e450654a891f" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "h2" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util 0.7.2", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashlink" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf" -dependencies = [ - "hashbrown", -] - -[[package]] -name = "heck" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hmac" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" -dependencies = [ - "crypto-mac", - "digest", -] - -[[package]] -name = "home" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654" -dependencies = [ - "winapi", -] - -[[package]] -name = "hostname" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" -dependencies = [ - "libc", - "match_cfg", - "winapi", -] - -[[package]] -name = "http" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "399c583b2979440c60be0821a6199eca73bc3c8dcd9d070d75ac726e2c6186e5" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" - -[[package]] -name = "httpdate" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6456b8a6c8f33fee7d958fcd1b60d55b11940a79e63ae87013e6d22e26034440" - -[[package]] -name = "hyper" -version = "0.14.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13f67199e765030fa08fe0bd581af683f0d5bc04ea09c2b1102012c5fb90e7fd" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "indexmap" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" -dependencies = [ - "autocfg", - "hashbrown", -] - -[[package]] -name = "instant" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "ipnet" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" - -[[package]] -name = "itoa" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" - -[[package]] -name = "itoap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9028f49264629065d057f340a86acb84867925865f73bbf8d47b4d149a7e88b8" - -[[package]] -name = "jobserver" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" -dependencies = [ - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.53" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4bf49d50e2961077d9c99f4b7997d770a1114f087c3c2e0069b36c13fc2979d" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "jsonwebtoken" -version = "7.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afabcc15e437a6484fc4f12d0fd63068fe457bf93f1c148d3d9649c60b103f32" -dependencies = [ - "base64 0.12.3", - "pem", - "ring", - "serde 1.0.126", - "serde_json", - "simple_asn1", -] - -[[package]] -name = "kv-log-macro" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" -dependencies = [ - "log", -] - -[[package]] -name = "language-tags" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "lettre" -version = "0.10.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8697ded52353bdd6fec234b3135972433397e86d0493d9fc38fbf407b7c106a" -dependencies = [ - "async-trait", - "base64 0.13.0", - "fastrand", - "futures-io", - "futures-util", - "hostname", - "httpdate", - "idna", - "mime", - "native-tls", - "nom 6.1.2", - "once_cell", - "quoted_printable", - "r2d2", - "regex", - "rustls", - "tokio", - "tokio-rustls", - "webpki", - "webpki-roots", -] - -[[package]] -name = "lexical-core" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" -dependencies = [ - "arrayvec", - "bitflags", - "cfg-if", - "ryu", - "static_assertions", -] - -[[package]] -name = "libc" -version = "0.2.101" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" - -[[package]] -name = "libsqlite3-sys" -version = "0.22.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290b64917f8b0cb885d9de0f9959fe1f775d7fa12f1da2db9001c1c8ab60f89d" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "linked-hash-map" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" - -[[package]] -name = "local-channel" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6246c68cf195087205a0512559c97e15eaf95198bf0e206d662092cdcb03fe9f" -dependencies = [ - "futures-core", - "futures-sink", - "futures-util", - "local-waker", -] - -[[package]] -name = "local-waker" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84f9a2d3e27ce99ce2c3aad0b09b1a7b916293ea9b2bf624c13fe646fadd8da4" - -[[package]] -name = "lock_api" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" -dependencies = [ - "cfg-if", - "value-bag", -] - -[[package]] -name = "maplit" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" - -[[package]] -name = "match_cfg" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" - -[[package]] -name = "matches" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" - -[[package]] -name = "memchr" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" - -[[package]] -name = "mime" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" - -[[package]] -name = "minimal-lexical" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6595bb28ed34f43c3fe088e48f6cfb2e033cab45f25a5384d5fdf564fbc8c4b2" - -[[package]] -name = "miniz_oxide" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" -dependencies = [ - "adler", - "autocfg", -] - -[[package]] -name = "mio" -version = "0.7.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16" -dependencies = [ - "libc", - "log", - "miow", - "ntapi", - "winapi", -] - -[[package]] -name = "miow" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" -dependencies = [ - "winapi", -] - -[[package]] -name = "native-tls" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "nom" -version = "5.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" -dependencies = [ - "lexical-core", - "memchr", - "version_check", -] - -[[package]] -name = "nom" -version = "6.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2" -dependencies = [ - "bitvec", - "funty", - "memchr", - "version_check", -] - -[[package]] -name = "nom" -version = "7.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffd9d26838a953b4af82cbeb9f1592c6798916983959be223a7124e992742c1" -dependencies = [ - "memchr", - "minimal-lexical", - "version_check", -] - -[[package]] -name = "ntapi" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" -dependencies = [ - "winapi", -] - -[[package]] -name = "num-bigint" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" -dependencies = [ - "autocfg", - "num-integer", - "num-traits 0.2.14", -] - -[[package]] -name = "num-integer" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" -dependencies = [ - "autocfg", - "num-traits 0.2.14", -] - -[[package]] -name = "num-traits" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" -dependencies = [ - "num-traits 0.2.14", -] - -[[package]] -name = "num-traits" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "once_cell" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "openssl" -version = "0.10.48" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "518915b97df115dd36109bfa429a48b8f737bd05508cf9588977b599648926d2" -dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "openssl-probe" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" - -[[package]] -name = "openssl-sys" -version = "0.9.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "666416d899cf077260dac8698d60a60b435a46d57e82acb1be3d0dad87284e5b" -dependencies = [ - "autocfg", - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "parking" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" - -[[package]] -name = "parking_lot" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall", - "smallvec", - "winapi", -] - -[[package]] -name = "password-hash" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ad7268ef9bc463fddde8361d358fbfae1aeeb1fb62eca111cd8c763bf1c5891" -dependencies = [ - "base64ct", - "rand_core", - "subtle", -] - -[[package]] -name = "paste" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf547ad0c65e31259204bd90935776d1c693cec2f4ff7abb7a1bbbd40dfe58" - -[[package]] -name = "pbkdf2" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05894bce6a1ba4be299d0c5f29563e08af2bc18bb7d48313113bed71e904739" -dependencies = [ - "crypto-mac", - "hmac", - "password-hash", - "sha2", -] - -[[package]] -name = "pem" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" -dependencies = [ - "base64 0.13.0", - "once_cell", - "regex", -] - -[[package]] -name = "percent-encoding" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" - -[[package]] -name = "pin-project" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7509cc106041c40a4518d2af7a61530e1eed0e6285296a3d8c5472806ccc4a4" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c950132583b500556b1efd71d45b319029f2b71518d979fcc208e16b42426f" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" - -[[package]] -name = "polling" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92341d779fa34ea8437ef4d82d440d5e1ce3f3ff7f824aa64424cd481f9a1f25" -dependencies = [ - "cfg-if", - "libc", - "log", - "wepoll-ffi", - "winapi", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" - -[[package]] -name = "proc-macro-hack" -version = "0.5.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" - -[[package]] -name = "proc-macro-nested" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" - -[[package]] -name = "proc-macro2" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "quote" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "quoted_printable" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1238256b09923649ec89b08104c4dfe9f6cb2fea734a5db5384e44916d59e9c5" - -[[package]] -name = "r2d2" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545c5bc2b880973c9c10e4067418407a0ccaa3091781d1671d46eb35107cb26f" -dependencies = [ - "log", - "parking_lot", - "scheduled-thread-pool", -] - -[[package]] -name = "radium" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8" - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom", -] - -[[package]] -name = "redox_syscall" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee" -dependencies = [ - "bitflags", -] - -[[package]] -name = "regex" -version = "1.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.6.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" - -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - -[[package]] -name = "reqwest" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "246e9f61b9bb77df069a947682be06e31ac43ea37862e244a69f177694ea6d22" -dependencies = [ - "base64 0.13.0", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "http", - "http-body", - "hyper", - "hyper-tls", - "ipnet", - "js-sys", - "lazy_static", - "log", - "mime", - "native-tls", - "percent-encoding", - "pin-project-lite", - "serde 1.0.126", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-native-tls", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg", -] - -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin", - "untrusted", - "web-sys", - "winapi", -] - -[[package]] -name = "rust-ini" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2" - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver", -] - -[[package]] -name = "rustls" -version = "0.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" -dependencies = [ - "base64 0.13.0", - "log", - "ring", - "sct", - "webpki", -] - -[[package]] -name = "ryu" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" - -[[package]] -name = "sailfish" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "816920a08514d9741242b3efe70c16c350ed548bc4a5ba03426e56faf9d45f77" -dependencies = [ - "itoap", - "ryu", - "sailfish-macros", - "version_check", -] - -[[package]] -name = "sailfish-compiler" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4276e7b848bde8e7813d534f014bc35ce5acd2b9e2b6b075727113fcf478ba63" -dependencies = [ - "filetime", - "home", - "memchr", - "proc-macro2", - "quote", - "syn", - "yaml-rust", -] - -[[package]] -name = "sailfish-macros" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bba2458ef07ae12c9aed2edb866c3db2f9c21cf19a2c3f2613b2982bc1a4a46" -dependencies = [ - "proc-macro2", - "sailfish-compiler", -] - -[[package]] -name = "sanitize-filename" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf18934a12018228c5b55a6dae9df5d0641e3566b3630cb46cc55564068e7c2f" -dependencies = [ - "lazy_static", - "regex", -] - -[[package]] -name = "schannel" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" -dependencies = [ - "lazy_static", - "winapi", -] - -[[package]] -name = "scheduled-thread-pool" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc6f74fd1204073fa02d5d5d68bec8021be4c38690b61264b2fdb48083d0e7d7" -dependencies = [ - "parking_lot", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "sct" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "security-framework" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23a2ac85147a3a11d77ecf1bc7166ec0b92febfa4461c37944e180f319ece467" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - -[[package]] -name = "serde" -version = "0.8.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" - -[[package]] -name = "serde" -version = "1.0.126" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-hjson" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a3a4e0ea8a88553209f6cc6cfe8724ecad22e1acf372793c27d995290fe74f8" -dependencies = [ - "lazy_static", - "num-traits 0.1.43", - "regex", - "serde 0.8.23", -] - -[[package]] -name = "serde_bencode" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "934d8bdbaa0126dafaea9a8833424a211d9661897717846c6bb782349ca1c30d" -dependencies = [ - "serde 1.0.126", - "serde_bytes", -] - -[[package]] -name = "serde_bytes" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16ae07dd2f88a366f15bd0632ba725227018c69a1c8550a927324f8eb8368bb9" -dependencies = [ - "serde 1.0.126", -] - -[[package]] -name = "serde_derive" -version = "1.0.126" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" -dependencies = [ - "itoa", - "ryu", - "serde 1.0.126", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde 1.0.126", -] - -[[package]] -name = "serde_yaml" -version = "0.8.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad104641f3c958dab30eb3010e834c2622d1f3f4c530fef1dee20ad9485f3c09" -dependencies = [ - "dtoa", - "indexmap", - "serde 1.0.126", - "yaml-rust", -] - -[[package]] -name = "sha-1" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" -dependencies = [ - "block-buffer", - "cfg-if", - "cpufeatures", - "digest", - "opaque-debug", -] - -[[package]] -name = "sha1" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" - -[[package]] -name = "sha2" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9204c41a1597a8c5af23c82d1c921cb01ec0a4c59e07a9c7306062829a3903f3" -dependencies = [ - "block-buffer", - "cfg-if", - "cpufeatures", - "digest", - "opaque-debug", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" -dependencies = [ - "libc", -] - -[[package]] -name = "simple_asn1" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692ca13de57ce0613a363c8c2f1de925adebc81b04c923ac60c5488bb44abe4b" -dependencies = [ - "chrono", - "num-bigint", - "num-traits 0.2.14", -] - -[[package]] -name = "slab" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" - -[[package]] -name = "smallvec" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" - -[[package]] -name = "socket2" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "765f090f0e423d2b55843402a07915add955e7d60657db13707a159727326cad" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "sqlformat" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "684001e7985ec1a9a66963b77ed151ef22a7876b3fdd7e37a57ec774f54b7d96" -dependencies = [ - "lazy_static", - "maplit", - "nom 7.0.0", - "regex", - "unicode_categories", -] - -[[package]] -name = "sqlx" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4b94ab0f8c21ee4899b93b06451ef5d965f1a355982ee73684338228498440" -dependencies = [ - "sqlx-core", - "sqlx-macros", -] - -[[package]] -name = "sqlx-core" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec28b91a01e1fe286d6ba66f68289a2286df023fc97444e1fd86c2fd6d5dc026" -dependencies = [ - "ahash", - "atoi", - "bitflags", - "byteorder", - "bytes", - "crc", - "crossbeam-channel", - "crossbeam-queue", - "crossbeam-utils", - "either", - "futures-channel", - "futures-core", - "futures-intrusive", - "futures-util", - "hashlink", - "hex", - "itoa", - "libc", - "libsqlite3-sys", - "log", - "memchr", - "once_cell", - "parking_lot", - "percent-encoding", - "rustls", - "sha2", - "smallvec", - "sqlformat", - "sqlx-rt", - "stringprep", - "thiserror", - "time 0.2.27", - "tokio-stream", - "url", - "webpki", - "webpki-roots", - "whoami", -] - -[[package]] -name = "sqlx-macros" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dc33c35d54774eed73d54568d47a6ac099aed8af5e1556a017c131be88217d5" -dependencies = [ - "dotenv", - "either", - "futures", - "heck", - "once_cell", - "proc-macro2", - "quote", - "sha2", - "sqlx-core", - "sqlx-rt", - "syn", - "url", -] - -[[package]] -name = "sqlx-rt" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14302b678d9c76b28f2e60115211e25e0aabc938269991745a169753dc00e35c" -dependencies = [ - "actix-rt", - "once_cell", - "tokio", - "tokio-rustls", -] - -[[package]] -name = "standback" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" -dependencies = [ - "version_check", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "stdweb" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" -dependencies = [ - "discard", - "rustc_version", - "stdweb-derive", - "stdweb-internal-macros", - "stdweb-internal-runtime", - "wasm-bindgen", -] - -[[package]] -name = "stdweb-derive" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" -dependencies = [ - "proc-macro2", - "quote", - "serde 1.0.126", - "serde_derive", - "syn", -] - -[[package]] -name = "stdweb-internal-macros" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" -dependencies = [ - "base-x", - "proc-macro2", - "quote", - "serde 1.0.126", - "serde_derive", - "serde_json", - "sha1", - "syn", -] - -[[package]] -name = "stdweb-internal-runtime" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" - -[[package]] -name = "stringprep" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "syn" -version = "1.0.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "tempfile" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" -dependencies = [ - "cfg-if", - "libc", - "rand", - "redox_syscall", - "remove_dir_all", - "winapi", -] - -[[package]] -name = "thiserror" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "time" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "time" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242" -dependencies = [ - "const_fn", - "libc", - "standback", - "stdweb", - "time-macros", - "version_check", - "winapi", -] - -[[package]] -name = "time-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" -dependencies = [ - "proc-macro-hack", - "time-macros-impl", -] - -[[package]] -name = "time-macros-impl" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" -dependencies = [ - "proc-macro-hack", - "proc-macro2", - "quote", - "standback", - "syn", -] - -[[package]] -name = "tinyvec" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" - -[[package]] -name = "tokio" -version = "1.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c27a64b625de6d309e8c57716ba93021dccf1b3b5c97edd6d3dd2d2135afc0a" -dependencies = [ - "bytes", - "libc", - "memchr", - "mio", - "num_cpus", - "once_cell", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "tokio-macros", - "winapi", -] - -[[package]] -name = "tokio-macros" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" -dependencies = [ - "rustls", - "tokio", - "webpki", -] - -[[package]] -name = "tokio-stream" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b2f3f698253f03119ac0102beaa64f67a67e08074d03a22d18784104543727f" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1caa0b0c8d94a049db56b5acf8cba99dc0623aab1b26d5b5f5e2d945846b3592" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "log", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f988a1a1adc2fb21f9c12aa96441da33a1728193ae0b95d2be22dbd17fcb4e5c" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", - "tracing", -] - -[[package]] -name = "toml" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" -dependencies = [ - "serde 1.0.126", -] - -[[package]] -name = "torrust" -version = "1.0.0" -dependencies = [ - "actix-cors", - "actix-multipart", - "actix-web", - "async-std", - "binascii", - "config", - "derive_builder", - "derive_more", - "futures", - "jsonwebtoken", - "lazy_static", - "lettre", - "pbkdf2", - "rand", - "rand_core", - "reqwest", - "sailfish", - "sanitize-filename", - "serde 1.0.126", - "serde_bencode", - "serde_bytes", - "serde_derive", - "serde_json", - "serde_yaml", - "sha-1", - "sqlx", - "tokio", - "toml", - "urlencoding", -] - -[[package]] -name = "tower-service" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" - -[[package]] -name = "tracing" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d" -dependencies = [ - "cfg-if", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tracing-core" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9ff14f98b1a4b289c6248a023c1c2fa1491062964e9fed67ab29c4e4da4a052" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "try-lock" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" - -[[package]] -name = "twoway" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c57ffb460d7c24cd6eda43694110189030a3d1dfe418416d9468fd1c1d290b47" -dependencies = [ - "memchr", - "unchecked-index", -] - -[[package]] -name = "typenum" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" - -[[package]] -name = "unchecked-index" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeba86d422ce181a719445e51872fa30f1f7413b62becb52e95ec91aa262d85c" - -[[package]] -name = "unicode-bidi" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0" -dependencies = [ - "matches", -] - -[[package]] -name = "unicode-normalization" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-segmentation" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" - -[[package]] -name = "unicode-xid" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - -[[package]] -name = "unicode_categories" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" - -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - -[[package]] -name = "url" -version = "2.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" -dependencies = [ - "form_urlencoded", - "idna", - "matches", - "percent-encoding", -] - -[[package]] -name = "urlencoding" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b90931029ab9b034b300b797048cf23723400aa757e8a2bfb9d748102f9821" - -[[package]] -name = "value-bag" -version = "1.0.0-alpha.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd320e1520f94261153e96f7534476ad869c14022aee1e59af7c778075d840ae" -dependencies = [ - "ctor", - "version_check", -] - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" - -[[package]] -name = "waker-fn" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" - -[[package]] -name = "want" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" -dependencies = [ - "log", - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" - -[[package]] -name = "wasm-bindgen" -version = "0.2.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce9b1b516211d33767048e5d47fa2a381ed8b76fc48d2ce4aa39877f9f183e0" -dependencies = [ - "cfg-if", - "serde 1.0.126", - "serde_json", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe8dc78e2326ba5f845f4b5bf548401604fa20b1dd1d365fb73b6c1d6364041" -dependencies = [ - "bumpalo", - "lazy_static", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95fded345a6559c2cfee778d562300c581f7d4ff3edb9b0d230d69800d213972" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44468aa53335841d9d6b6c023eaab07c0cd4bddbcfdee3e2bb1e8d2cb8069fef" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0195807922713af1e67dc66132c7328206ed9766af3858164fb583eedc25fbad" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdb075a845574a1fa5f09fd77e43f7747599301ea3417a9fbffdeedfc1f4a29" - -[[package]] -name = "web-sys" -version = "0.3.53" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224b2f6b67919060055ef1a67807367c2066ed520c3862cc013d26cf893a783c" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki" -version = "0.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "webpki-roots" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" -dependencies = [ - "webpki", -] - -[[package]] -name = "wepoll-ffi" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" -dependencies = [ - "cc", -] - -[[package]] -name = "whoami" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7741161a40200a867c96dfa5574544efa4178cf4c8f770b62dd1cc0362d7ae1" -dependencies = [ - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "winreg" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" -dependencies = [ - "winapi", -] - -[[package]] -name = "wyz" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" - -[[package]] -name = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] - -[[package]] -name = "zstd" -version = "0.7.0+zstd.1.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9428752481d8372e15b1bf779ea518a179ad6c771cca2d2c60e4fbff3cc2cd52" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "3.1.0+zstd.1.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa1926623ad7fe406e090555387daf73db555b948134b4d73eac5eb08fb666d" -dependencies = [ - "libc", - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "1.5.0+zstd.1.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e6c094340240369025fc6b731b054ee2a834328fa584310ac96aa4baebdc465" -dependencies = [ - "cc", - "libc", -] +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "actix-codec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" +dependencies = [ + "bitflags 2.9.0", + "bytes", + "futures-core", + "futures-sink", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "actix-contrib-logger" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f385cfe5971dd42895ed503c8ae11a6022aae402c20543d1ee795b66d144d4af" +dependencies = [ + "actix-http", + "actix-rt", + "actix-service", + "actix-utils", + "actix-web", + "bytes", + "env_logger", + "futures-core", + "http", + "log", + "pin-project-lite", + "regex", + "time 0.3.41", +] + +[[package]] +name = "actix-cors" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daa239b93927be1ff123eebada5a3ff23e89f0124ccb8609234e5103d5a5ae6d" +dependencies = [ + "actix-utils", + "actix-web", + "derive_more 2.0.1", + "futures-util", + "log", + "once_cell", + "smallvec", +] + +[[package]] +name = "actix-http" +version = "3.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa882656b67966045e4152c634051e70346939fced7117d5f0b52146a7c74c9" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "base64 0.22.1", + "bitflags 2.9.0", + "brotli", + "bytes", + "bytestring", + "derive_more 2.0.1", + "encoding_rs", + "flate2", + "foldhash", + "futures-core", + "h2", + "http", + "httparse", + "httpdate", + "itoa 1.0.15", + "language-tags", + "local-channel", + "mime", + "percent-encoding", + "pin-project-lite", + "rand 0.9.1", + "sha1", + "smallvec", + "tokio", + "tokio-util", + "tracing", + "zstd", +] + +[[package]] +name = "actix-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" +dependencies = [ + "quote", + "syn 2.0.101", +] + +[[package]] +name = "actix-multipart" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5118a26dee7e34e894f7e85aa0ee5080ae4c18bf03c0e30d49a80e418f00a53" +dependencies = [ + "actix-multipart-derive", + "actix-utils", + "actix-web", + "derive_more 0.99.14", + "futures-core", + "futures-util", + "httparse", + "local-waker", + "log", + "memchr", + "mime", + "rand 0.8.5", + "serde 1.0.219", + "serde_json", + "serde_plain", + "tempfile", + "tokio", +] + +[[package]] +name = "actix-multipart-derive" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e11eb847f49a700678ea2fa73daeb3208061afa2b9d1a8527c03390f4c4a1c6b" +dependencies = [ + "darling 0.20.11", + "parse-size", + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "actix-router" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8" +dependencies = [ + "bytestring", + "cfg-if", + "http", + "regex", + "regex-lite", + "serde 1.0.219", + "tracing", +] + +[[package]] +name = "actix-rt" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eda4e2a6e042aa4e55ac438a2ae052d3b5da0ecf83d7411e1a368946925208" +dependencies = [ + "actix-macros", + "futures-core", + "tokio", +] + +[[package]] +name = "actix-server" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6398974fd4284f4768af07965701efbbb5fdc0616bff20cade1bb14b77675e24" +dependencies = [ + "actix-rt", + "actix-service", + "actix-utils", + "futures-core", + "futures-util", + "mio", + "socket2 0.5.9", + "tokio", + "tracing", +] + +[[package]] +name = "actix-service" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77f5f9d66a8730d0fae62c26f3424f5751e5518086628a40b7ab6fca4a705034" +dependencies = [ + "futures-core", + "paste", + "pin-project-lite", +] + +[[package]] +name = "actix-utils" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e491cbaac2e7fc788dfff99ff48ef317e23b3cf63dbaf7aaab6418f40f92aa94" +dependencies = [ + "local-waker", + "pin-project-lite", +] + +[[package]] +name = "actix-web" +version = "4.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2e3b15b3dc6c6ed996e4032389e9849d4ab002b1e92fbfe85b5f307d1479b4d" +dependencies = [ + "actix-codec", + "actix-http", + "actix-macros", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-utils", + "actix-web-codegen", + "bytes", + "bytestring", + "cfg-if", + "cookie", + "derive_more 2.0.1", + "encoding_rs", + "foldhash", + "futures-core", + "futures-util", + "impl-more", + "itoa 1.0.15", + "language-tags", + "log", + "mime", + "once_cell", + "pin-project-lite", + "regex", + "regex-lite", + "serde 1.0.219", + "serde_json", + "serde_urlencoded", + "smallvec", + "socket2 0.5.9", + "time 0.3.41", + "tracing", + "url", +] + +[[package]] +name = "actix-web-codegen" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f591380e2e68490b5dfaf1dd1aa0ebe78d84ba7067078512b4ea6e4492d622b8" +dependencies = [ + "actix-router", + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "async-channel" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" +dependencies = [ + "concurrent-queue 1.2.2", + "event-listener 2.5.1", + "futures-core", +] + +[[package]] +name = "async-executor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" +dependencies = [ + "async-task", + "concurrent-queue 1.2.2", + "fastrand 1.5.0", + "futures-lite", + "once_cell", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9586ec52317f36de58453159d48351bc244bc24ced3effc1fce22f3d48664af6" +dependencies = [ + "async-channel", + "async-executor", + "async-io", + "async-mutex", + "blocking", + "futures-lite", + "num_cpus", + "once_cell", +] + +[[package]] +name = "async-io" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a811e6a479f2439f0c04038796b5cfb3d2ad56c230e0f2d3f7b04d68cfee607b" +dependencies = [ + "concurrent-queue 1.2.2", + "futures-lite", + "libc", + "log", + "once_cell", + "parking", + "polling", + "slab", + "socket2 0.4.1", + "waker-fn", + "winapi", +] + +[[package]] +name = "async-lock" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6a8ea61bf9947a1007c5cada31e647dbc77b103c679858150003ba697ea798b" +dependencies = [ + "event-listener 2.5.1", +] + +[[package]] +name = "async-mutex" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" +dependencies = [ + "event-listener 2.5.1", +] + +[[package]] +name = "async-std" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8056f1455169ab86dd47b47391e4ab0cbd25410a70e9fe675544f49bafaf952" +dependencies = [ + "async-channel", + "async-global-executor", + "async-io", + "async-lock", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "num_cpus", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-task" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0" + +[[package]] +name = "async-trait" +version = "0.1.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44318e776df68115a881de9a8fd1b9e53368d7a4a5ce4cc48517da3393233a5e" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.73", +] + +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits 0.2.14", +] + +[[package]] +name = "atomic-waker" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide 0.8.8", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" + +[[package]] +name = "binascii" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "bitflags" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +dependencies = [ + "serde 1.0.219", +] + +[[package]] +name = "bitvec" +version = "0.19.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8942c8d352ae1838c9dda0b0ca2ab657696ef2232a20147cf1b30ae1a9cb4321" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blocking" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e170dbede1f740736619b776d7251cb1b9095c435c34d8ca9f57fcd2f335e9" +dependencies = [ + "async-channel", + "async-task", + "atomic-waker", + "fastrand 1.5.0", + "futures-lite", + "once_cell", +] + +[[package]] +name = "brotli" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a334ef7c9e23abf0ce748e8cd309037da93e606ad52eb372e4ce327a0dcfbdfd" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "bytestring" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90706ba19e97b90786e19dc0d5e2abd80008d99d4c0c5d1ad0b5e72cec7c494d" +dependencies = [ + "bytes", +] + +[[package]] +name = "cache-padded" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" + +[[package]] +name = "cc" +version = "1.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6127248204b9aba09a362f6c930ef6a78f2c1b2215f8a7b398c06e1083f17af0" +dependencies = [ + "js-sys", + "num-integer", + "num-traits 0.2.14", + "time 0.1.45", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "concurrent-queue" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" +dependencies = [ + "cache-padded", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "config" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1b9d958c2b1368a663f05538fc1b5975adce1e19f435acceae987aceeeb369" +dependencies = [ + "lazy_static", + "nom 5.1.2", + "rust-ini", + "serde 1.0.219", + "serde-hjson", + "serde_json", + "toml 0.5.8", + "yaml-rust", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cookie" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" +dependencies = [ + "percent-encoding", + "time 0.3.41", + "version_check", +] + +[[package]] +name = "core-foundation" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" + +[[package]] +name = "cpufeatures" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crc32fast" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b10ddc024425c88c2ad148c1b0fd53f4c6d38db9697c9f1588381212fa657c9" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "darling" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f2c43f534ea4b0b049015d00269734195e6d3f0f6635cb692251aca6f9f8b3c" +dependencies = [ + "darling_core 0.12.4", + "darling_macro 0.12.4", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling_core" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e91455b86830a1c21799d94524df0845183fa55bafd9aa137b01c7d1065fa36" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn 1.0.73", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.11.1", + "syn 2.0.101", +] + +[[package]] +name = "darling_macro" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29b5acf0dea37a7f66f7b25d2c5e93fd46f8f6968b1a5d7a3e02e97768afc95a" +dependencies = [ + "darling_core 0.12.4", + "quote", + "syn 1.0.73", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core 0.20.11", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derive_builder" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d13202debe11181040ae9063d739fa32cfcaaebe2275fe387703460ae2365b30" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66e616858f6187ed828df7c64a6d71720d83767a7f19740b2d1b6fe6327b36e5" +dependencies = [ + "darling 0.12.4", + "proc-macro2", + "quote", + "syn 1.0.73", +] + +[[package]] +name = "derive_builder_macro" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58a94ace95092c5acb1e97a7e846b310cfbd499652f72297da7493f618a98d73" +dependencies = [ + "derive_builder_core", + "syn 1.0.73", +] + +[[package]] +name = "derive_more" +version = "0.99.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc7b9cef1e351660e5443924e4f43ab25fbbed3e9a5f052df3677deb4d6b320" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "syn 1.0.73", +] + +[[package]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", + "unicode-xid", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "dtoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +dependencies = [ + "serde 1.0.219", +] + +[[package]] +name = "encoding_rs" +version = "0.8.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80df024fbc5ac80f87dfef0d9f5209a252f2a497f7f42944cff24d8253cac065" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "env_logger" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + +[[package]] +name = "event-listener" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59" + +[[package]] +name = "event-listener" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +dependencies = [ + "concurrent-queue 2.5.0", + "parking", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b394ed3d285a429378d3b384b9eb1285267e7df4b166df24b7a6939a04dc392e" +dependencies = [ + "instant", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "filetime" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.59.0", +] + +[[package]] +name = "flate2" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0" +dependencies = [ + "cfg-if", + "crc32fast", + "libc", + "miniz_oxide 0.4.4", +] + +[[package]] +name = "flume" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +dependencies = [ + "futures-core", + "futures-sink", + "spin 0.9.8", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "funty" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" + +[[package]] +name = "futures" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7e43a803dae2fa37c1f6a8fe121e1f7bf9548b4dfc0522a42f34145dadfc27" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot 0.12.3", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" +dependencies = [ + "fastrand 1.5.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "gloo-timers" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47204a46aaff920a1ea58b11d03dec6f704287d27561724a4631e450654a891f" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap 2.9.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "hashbrown" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.3", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.15", +] + +[[package]] +name = "http-body" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "399c583b2979440c60be0821a6199eca73bc3c8dcd9d070d75ac726e2c6186e5" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" + +[[package]] +name = "httpdate" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6456b8a6c8f33fee7d958fcd1b60d55b11940a79e63ae87013e6d22e26034440" + +[[package]] +name = "humantime" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" + +[[package]] +name = "hyper" +version = "0.14.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13f67199e765030fa08fe0bd581af683f0d5bc04ea09c2b1102012c5fb90e7fd" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa 0.4.7", + "pin-project-lite", + "socket2 0.4.1", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "impl-more" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2" + +[[package]] +name = "indexmap" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" +dependencies = [ + "autocfg", + "hashbrown 0.11.2", +] + +[[package]] +name = "indexmap" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +dependencies = [ + "equivalent", + "hashbrown 0.15.3", +] + +[[package]] +name = "instant" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "ipnet" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" + +[[package]] +name = "is-terminal" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" +dependencies = [ + "hermit-abi 0.5.1", + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "itoa" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "itoap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9028f49264629065d057f340a86acb84867925865f73bbf8d47b4d149a7e88b8" + +[[package]] +name = "jobserver" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +dependencies = [ + "getrandom 0.3.2", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4bf49d50e2961077d9c99f4b7997d770a1114f087c3c2e0069b36c13fc2979d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "jsonwebtoken" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afabcc15e437a6484fc4f12d0fd63068fe457bf93f1c148d3d9649c60b103f32" +dependencies = [ + "base64 0.12.3", + "pem", + "ring", + "serde 1.0.219", + "serde_json", + "simple_asn1", +] + +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + +[[package]] +name = "language-tags" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin 0.5.2", +] + +[[package]] +name = "lettre" +version = "0.10.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8697ded52353bdd6fec234b3135972433397e86d0493d9fc38fbf407b7c106a" +dependencies = [ + "async-trait", + "base64 0.13.0", + "fastrand 1.5.0", + "futures-io", + "futures-util", + "hostname", + "httpdate", + "idna", + "mime", + "native-tls", + "nom 6.1.2", + "once_cell", + "quoted_printable", + "r2d2", + "regex", + "rustls", + "tokio", + "tokio-rustls", + "webpki", + "webpki-roots", +] + +[[package]] +name = "lexical-core" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" +dependencies = [ + "arrayvec", + "bitflags 1.2.1", + "cfg-if", + "ryu", + "static_assertions", +] + +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "libm" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a25169bd5913a4b437588a7e3d127cd6e90127b60e0ffbd834a38f1599e016b8" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.9.0", + "libc", + "redox_syscall 0.5.12", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "local-channel" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6246c68cf195087205a0512559c97e15eaf95198bf0e206d662092cdcb03fe9f" +dependencies = [ + "futures-core", + "futures-sink", + "futures-util", + "local-waker", +] + +[[package]] +name = "local-waker" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84f9a2d3e27ce99ce2c3aad0b09b1a7b916293ea9b2bf624c13fe646fadd8da4" + +[[package]] +name = "lock_api" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +dependencies = [ + "value-bag", +] + +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest 0.10.7", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.52.0", +] + +[[package]] +name = "native-tls" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nom" +version = "5.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" +dependencies = [ + "lexical-core", + "memchr", + "version_check", +] + +[[package]] +name = "nom" +version = "6.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2" +dependencies = [ + "bitvec", + "funty", + "memchr", + "version_check", +] + +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +dependencies = [ + "autocfg", + "num-integer", + "num-traits 0.2.14", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits 0.2.14", + "rand 0.8.5", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits 0.2.14", +] + +[[package]] +name = "num-iter" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +dependencies = [ + "autocfg", + "num-integer", + "num-traits 0.2.14", +] + +[[package]] +name = "num-traits" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" +dependencies = [ + "num-traits 0.2.14", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi 0.1.19", + "libc", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl" +version = "0.10.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "518915b97df115dd36109bfa429a48b8f737bd05508cf9588977b599648926d2" +dependencies = [ + "bitflags 1.2.1", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.73", +] + +[[package]] +name = "openssl-probe" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" + +[[package]] +name = "openssl-sys" +version = "0.9.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "666416d899cf077260dac8698d60a60b435a46d57e82acb1be3d0dad87284e5b" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" + +[[package]] +name = "parking_lot" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.3", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.10", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall 0.2.9", + "smallvec", + "winapi", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.12", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "parse-size" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487f2ccd1e17ce8c1bfab3a65c89525af41cfad4c8659021a1e9a2aacd73b89b" + +[[package]] +name = "password-hash" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "paste" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf547ad0c65e31259204bd90935776d1c693cec2f4ff7abb7a1bbbd40dfe58" + +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.7", + "hmac", + "password-hash", + "sha2", +] + +[[package]] +name = "pem" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" +dependencies = [ + "base64 0.13.0", + "once_cell", + "regex", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "polling" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92341d779fa34ea8437ef4d82d440d5e1ce3f3ff7f824aa64424cd481f9a1f25" +dependencies = [ + "cfg-if", + "libc", + "log", + "wepoll-ffi", + "winapi", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "quoted_printable" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1238256b09923649ec89b08104c4dfe9f6cb2fea734a5db5384e44916d59e9c5" + +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + +[[package]] +name = "r2d2" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "545c5bc2b880973c9c10e4067418407a0ccaa3091781d1671d46eb35107cb26f" +dependencies = [ + "log", + "parking_lot 0.11.1", + "scheduled-thread-pool", +] + +[[package]] +name = "radium" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee" +dependencies = [ + "bitflags 1.2.1", +] + +[[package]] +name = "redox_syscall" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +dependencies = [ + "bitflags 2.9.0", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-lite" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "reqwest" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246e9f61b9bb77df069a947682be06e31ac43ea37862e244a69f177694ea6d22" +dependencies = [ + "base64 0.13.0", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "lazy_static", + "log", + "mime", + "native-tls", + "percent-encoding", + "pin-project-lite", + "serde 1.0.219", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin 0.5.2", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rsa" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" +dependencies = [ + "const-oid", + "digest 0.10.7", + "num-bigint-dig", + "num-integer", + "num-traits 0.2.14", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[package]] +name = "rust-ini" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2" + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +dependencies = [ + "bitflags 2.9.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustls" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" +dependencies = [ + "base64 0.13.0", + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "sailfish" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acd5f4680149b62b3478f6af08a8f1c37794bc1bc577e28874a4d0c70084d600" +dependencies = [ + "itoap", + "ryu", + "sailfish-macros", + "version_check", +] + +[[package]] +name = "sailfish-compiler" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67087aca4a3886686a88cee6835089c53e6143a0b8c5be01e63e4fe2f6dfe7cb" +dependencies = [ + "filetime", + "home", + "memchr", + "proc-macro2", + "quote", + "serde 1.0.219", + "syn 2.0.101", + "toml 0.8.22", +] + +[[package]] +name = "sailfish-macros" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e47e31910c5f9230e99992568d05a5968fe4f42a635c3f912c993e9f66a619a5" +dependencies = [ + "proc-macro2", + "sailfish-compiler", +] + +[[package]] +name = "sanitize-filename" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf18934a12018228c5b55a6dae9df5d0641e3566b3630cb46cc55564068e7c2f" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "schannel" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +dependencies = [ + "lazy_static", + "winapi", +] + +[[package]] +name = "scheduled-thread-pool" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6f74fd1204073fa02d5d5d68bec8021be4c38690b61264b2fdb48083d0e7d7" +dependencies = [ + "parking_lot 0.11.1", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sct" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "security-framework" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23a2ac85147a3a11d77ecf1bc7166ec0b92febfa4461c37944e180f319ece467" +dependencies = [ + "bitflags 1.2.1", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-hjson" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a3a4e0ea8a88553209f6cc6cfe8724ecad22e1acf372793c27d995290fe74f8" +dependencies = [ + "lazy_static", + "num-traits 0.1.43", + "regex", + "serde 0.8.23", +] + +[[package]] +name = "serde_bencode" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "934d8bdbaa0126dafaea9a8833424a211d9661897717846c6bb782349ca1c30d" +dependencies = [ + "serde 1.0.219", + "serde_bytes", +] + +[[package]] +name = "serde_bytes" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16ae07dd2f88a366f15bd0632ba725227018c69a1c8550a927324f8eb8368bb9" +dependencies = [ + "serde 1.0.219", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa 1.0.15", + "memchr", + "ryu", + "serde 1.0.219", +] + +[[package]] +name = "serde_plain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1fc6db65a611022b23a0dec6975d63fb80a302cb3388835ff02c097258d50" +dependencies = [ + "serde 1.0.219", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde 1.0.219", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" +dependencies = [ + "form_urlencoded", + "itoa 0.4.7", + "ryu", + "serde 1.0.219", +] + +[[package]] +name = "serde_yaml" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad104641f3c958dab30eb3010e834c2622d1f3f4c530fef1dee20ad9485f3c09" +dependencies = [ + "dtoa", + "indexmap 1.7.0", + "serde 1.0.219", + "yaml-rust", +] + +[[package]] +name = "sha-1" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "simple_asn1" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692ca13de57ce0613a363c8c2f1de925adebc81b04c923ac60c5488bb44abe4b" +dependencies = [ + "chrono", + "num-bigint", + "num-traits 0.2.14", +] + +[[package]] +name = "slab" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" + +[[package]] +name = "smallvec" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +dependencies = [ + "serde 1.0.219", +] + +[[package]] +name = "socket2" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "765f090f0e423d2b55843402a07915add955e7d60657db13707a159727326cad" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "socket2" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "sqlx" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c3a85280daca669cfd3bcb68a337882a8bc57ec882f72c5d13a430613a738e" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", +] + +[[package]] +name = "sqlx-core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f743f2a3cea30a58cd479013f75550e879009e3a02f616f18ca699335aa248c3" +dependencies = [ + "base64 0.22.1", + "bytes", + "crc", + "crossbeam-queue", + "either", + "event-listener 5.4.0", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashbrown 0.15.3", + "hashlink", + "indexmap 2.9.0", + "log", + "memchr", + "once_cell", + "percent-encoding", + "serde 1.0.219", + "serde_json", + "sha2", + "smallvec", + "thiserror", + "time 0.3.41", + "tokio", + "tokio-stream", + "tracing", + "url", +] + +[[package]] +name = "sqlx-macros" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4200e0fde19834956d4252347c12a083bdcb237d7a1a1446bffd8768417dce" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn 2.0.101", +] + +[[package]] +name = "sqlx-macros-core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "882ceaa29cade31beca7129b6beeb05737f44f82dbe2a9806ecea5a7093d00b7" +dependencies = [ + "dotenvy", + "either", + "heck", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde 1.0.219", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", + "syn 2.0.101", + "tempfile", + "tokio", + "url", +] + +[[package]] +name = "sqlx-mysql" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0afdd3aa7a629683c2d750c2df343025545087081ab5942593a5288855b1b7a7" +dependencies = [ + "atoi", + "base64 0.22.1", + "bitflags 2.9.0", + "byteorder", + "bytes", + "crc", + "digest 0.10.7", + "dotenvy", + "either", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "generic-array", + "hex", + "hkdf", + "hmac", + "itoa 1.0.15", + "log", + "md-5", + "memchr", + "once_cell", + "percent-encoding", + "rand 0.8.5", + "rsa", + "serde 1.0.219", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "time 0.3.41", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-postgres" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0bedbe1bbb5e2615ef347a5e9d8cd7680fb63e77d9dafc0f29be15e53f1ebe6" +dependencies = [ + "atoi", + "base64 0.22.1", + "bitflags 2.9.0", + "byteorder", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa 1.0.15", + "log", + "md-5", + "memchr", + "once_cell", + "rand 0.8.5", + "serde 1.0.219", + "serde_json", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "time 0.3.41", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c26083e9a520e8eb87a06b12347679b142dc2ea29e6e409f805644a7a979a5bc" +dependencies = [ + "atoi", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde 1.0.219", + "serde_urlencoded", + "sqlx-core", + "thiserror", + "time 0.3.41", + "tracing", + "url", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "stringprep" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "syn" +version = "2.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +dependencies = [ + "fastrand 2.3.0", + "getrandom 0.3.2", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "time" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +dependencies = [ + "deranged", + "itoa 1.0.15", + "num-conv", + "powerfmt", + "serde 1.0.219", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" + +[[package]] +name = "time-macros" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot 0.12.3", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.5.9", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-stream" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f988a1a1adc2fb21f9c12aa96441da33a1728193ae0b95d2be22dbd17fcb4e5c" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde 1.0.219", +] + +[[package]] +name = "toml" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" +dependencies = [ + "serde 1.0.219", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +dependencies = [ + "serde 1.0.219", +] + +[[package]] +name = "toml_edit" +version = "0.22.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +dependencies = [ + "indexmap 2.9.0", + "serde 1.0.219", + "serde_spanned", + "toml_datetime", + "toml_write", + "winnow", +] + +[[package]] +name = "toml_write" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" + +[[package]] +name = "torrust" +version = "1.0.0" +dependencies = [ + "actix-contrib-logger", + "actix-cors", + "actix-multipart", + "actix-web", + "async-std", + "async-trait", + "binascii", + "config", + "derive_builder", + "derive_more 0.99.14", + "env_logger", + "futures", + "jsonwebtoken", + "lazy_static", + "lettre", + "log", + "pbkdf2", + "rand 0.8.5", + "rand_core 0.6.4", + "reqwest", + "sailfish", + "sanitize-filename", + "serde 1.0.219", + "serde_bencode", + "serde_bytes", + "serde_derive", + "serde_json", + "serde_yaml", + "sha-1", + "sqlx", + "tokio", + "toml 0.5.8", + "urlencoding", +] + +[[package]] +name = "tower-service" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "unicode-bidi" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "urlencoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b90931029ab9b034b300b797048cf23723400aa757e8a2bfb9d748102f9821" + +[[package]] +name = "value-bag" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943ce29a8a743eb10d6082545d861b24f9d1b160b7d741e0f2cdf726bec909c5" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce9b1b516211d33767048e5d47fa2a381ed8b76fc48d2ce4aa39877f9f183e0" +dependencies = [ + "cfg-if", + "serde 1.0.219", + "serde_json", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe8dc78e2326ba5f845f4b5bf548401604fa20b1dd1d365fb73b6c1d6364041" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn 1.0.73", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95fded345a6559c2cfee778d562300c581f7d4ff3edb9b0d230d69800d213972" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44468aa53335841d9d6b6c023eaab07c0cd4bddbcfdee3e2bb1e8d2cb8069fef" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0195807922713af1e67dc66132c7328206ed9766af3858164fb583eedc25fbad" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.73", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdb075a845574a1fa5f09fd77e43f7747599301ea3417a9fbffdeedfc1f4a29" + +[[package]] +name = "web-sys" +version = "0.3.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224b2f6b67919060055ef1a67807367c2066ed520c3862cc013d26cf893a783c" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" +dependencies = [ + "webpki", +] + +[[package]] +name = "wepoll-ffi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" +dependencies = [ + "cc", +] + +[[package]] +name = "whoami" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6994d13118ab492c3c80c1f81928718159254c53c472bf9ce36f8dae4add02a7" +dependencies = [ + "redox_syscall 0.5.12", + "wasite", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9fb597c990f03753e08d3c29efbfcf2019a003b4bf4ba19225c158e1549f0f3" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" +dependencies = [ + "winapi", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.0", +] + +[[package]] +name = "wyz" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "zerocopy" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.15+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/backend/Cargo.toml b/backend/Cargo.toml index cdcec069..bbad435f 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,50 +1,75 @@ -[package] -name = "torrust" -version = "1.0.0" -authors = ["Mick ", "Wesley ", "Wesley , - database: Arc, -} - -impl AuthorizationService { - pub fn new(cfg: Arc, database: Arc) -> AuthorizationService { - AuthorizationService { - cfg, - database - } - } - - pub async fn sign_jwt(&self, user: User) -> String { - let settings = self.cfg.settings.read().await; - - // create JWT that expires in two weeks - let key = settings.auth.secret_key.as_bytes(); - let exp_date = current_time() + 1_209_600; // two weeks from now - - let claims = Claims { - sub: user.username, - admin: user.administrator, - exp: exp_date, - }; - - let token = encode( - &Header::default(), - &claims, - &EncodingKey::from_secret(key), - ) - .unwrap(); - - token - } - - pub async fn verify_jwt(&self, token: &str) -> Result { - let settings = self.cfg.settings.read().await; - - match decode::( - token, - &DecodingKey::from_secret(settings.auth.secret_key.as_bytes()), - &Validation::new(Algorithm::HS256), - ) { - Ok(token_data) => { - if token_data.claims.exp < current_time() { - return Err(ServiceError::TokenExpired) - } - Ok(token_data.claims) - }, - Err(_) => Err(ServiceError::TokenInvalid) - } - } - - pub async fn get_claims_from_request(&self, req: &HttpRequest) -> Result { - let _auth = req.headers().get("Authorization"); - match _auth { - Some(_) => { - let _split: Vec<&str> = _auth.unwrap().to_str().unwrap().split("Bearer").collect(); - let token = _split[1].trim(); - - match self.verify_jwt(token).await { - Ok(claims) => Ok(claims), - Err(e) => Err(e), - } - } - None => Err(ServiceError::TokenNotFound) - } - } - - pub async fn get_user_from_request(&self, req: &HttpRequest) -> Result { - let claims = match self.get_claims_from_request(req).await { - Ok(claims) => Ok(claims), - Err(e) => Err(e) - }?; - - match self.database.get_user_with_username(&claims.sub).await { - Some(user) => Ok(user), - None => Err(ServiceError::AccountNotFound) - } - } -} +use crate::config::Configuration; +use crate::database::SqliteDatabase; +use crate::errors::ServiceError; +use crate::models::user::{User, UserClaims, UserCompact}; +use crate::utils::time::current_time; +use actix_web::HttpRequest; +use jsonwebtoken::{ + dangerous_insecure_decode, decode, encode, Algorithm, DecodingKey, EncodingKey, Header, + Validation, +}; +use log::{log, Level}; +use std::sync::Arc; + +pub struct AuthorizationService { + cfg: Arc, + database: Arc, +} + +impl AuthorizationService { + pub fn new(cfg: Arc, database: Arc) -> AuthorizationService { + AuthorizationService { cfg, database } + } + + pub async fn sign_jwt(&self, user: UserCompact) -> String { + let settings = self.cfg.settings.read().await; + + // create JWT that expires in two weeks + let key = settings.auth.secret_key.as_bytes(); + let exp_date = current_time() + settings.auth.session_duration_seconds; // two weeks from now + + let claims = UserClaims { + user: user, + exp: exp_date, + }; + + let token = encode(&Header::default(), &claims, &EncodingKey::from_secret(key)).unwrap(); + + token + } + + pub async fn verify_jwt(&self, token: &str) -> Result { + match self.decode_token(token).await { + Ok(claims) => { + if claims.exp < current_time() { + log!(Level::Debug, "Token validity expired"); + return Err(ServiceError::TokenExpired); + } + Ok(claims) + } + Err(_) => Err(ServiceError::TokenInvalid), + } + } + + pub async fn decode_jwt(&self, token: &str) -> Result { + let settings = self.cfg.settings.read().await; + + match self.decode_token(token).await { + Ok(claims) => { + if claims.exp + settings.auth.renewal_grace_time < current_time() { + log!(Level::Debug, "Token extended validity expired"); + return Err(ServiceError::TokenExpired); + } + Ok(claims) + } + Err(_) => Err(ServiceError::TokenInvalid), + } + } + + async fn decode_token(&self, token: &str) -> Result { + let settings = self.cfg.settings.read().await; + + match decode::( + token, + &DecodingKey::from_secret(settings.auth.secret_key.as_bytes()), + &Validation::new(Algorithm::HS256), + ) { + Ok(claims) => Ok(claims.claims), + Err(e) => match e.kind() { + jsonwebtoken::errors::ErrorKind::ExpiredSignature => { + Ok(dangerous_insecure_decode::(token) + .unwrap() + .claims) + } + _ => { + log!(Level::Debug, "{:#?}", e.kind()); + Err(ServiceError::TokenInvalid) + } + }, + } + } + + pub async fn get_claims_from_request( + &self, + req: &HttpRequest, + ) -> Result { + let _auth = req.headers().get("Authorization"); + match _auth { + Some(_) => { + let _split: Vec<&str> = _auth.unwrap().to_str().unwrap().split("Bearer").collect(); + let token = _split[1].trim(); + + match self.verify_jwt(token).await { + Ok(claims) => Ok(claims), + Err(e) => Err(e), + } + } + None => Err(ServiceError::TokenNotFound), + } + } + + pub async fn get_user_from_request(&self, req: &HttpRequest) -> Result { + let claims = match self.get_claims_from_request(req).await { + Ok(claims) => Ok(claims), + Err(e) => Err(e), + }?; + + match self + .database + .get_user_with_username(&claims.user.username) + .await + { + Some(user) => Ok(user), + None => Err(ServiceError::AccountNotFound), + } + } +} diff --git a/backend/src/common.rs b/backend/src/common.rs index 17653826..5076e4fa 100644 --- a/backend/src/common.rs +++ b/backend/src/common.rs @@ -1,30 +1,43 @@ -use std::sync::Arc; -use crate::config::Configuration; -use crate::database::Database; -use crate::auth::AuthorizationService; -use crate::tracker::TrackerService; -use crate::mailer::MailerService; - -pub type Username = String; - -pub type WebAppData = actix_web::web::Data>; - -pub struct AppData { - pub cfg: Arc, - pub database: Arc, - pub auth: Arc, - pub tracker: Arc, - pub mailer: Arc -} - -impl AppData { - pub fn new(cfg: Arc, database: Arc, auth: Arc, tracker: Arc, mailer: Arc) -> AppData { - AppData { - cfg, - database, - auth, - tracker, - mailer, - } - } -} +use crate::auth::AuthorizationService; +use crate::config::Configuration; +use crate::database::SqliteDatabase; +use crate::handlers::user::RegistrationService; +use crate::mailer::MailerService; +use crate::tracker::TrackerService; +use std::sync::Arc; + +pub type Username = String; + +pub type WebAppData = actix_web::web::Data>; + +pub struct AppData { + pub cfg: Arc, + pub database: Arc, + pub auth: Arc, + pub tracker: Arc, + pub mailer: Arc, + // Services + pub registration_service: Arc, +} + +impl AppData { + #[allow(clippy::too_many_arguments)] + pub fn new( + cfg: Arc, + database: Arc, + auth: Arc, + tracker: Arc, + mailer: Arc, + // Services + registration_service: Arc, + ) -> AppData { + AppData { + cfg, + database, + auth, + tracker, + mailer, + registration_service, + } + } +} diff --git a/backend/src/config.rs b/backend/src/config.rs index 4d0980bb..082df272 100644 --- a/backend/src/config.rs +++ b/backend/src/config.rs @@ -1,196 +1,205 @@ -use std::fs; -use config::{ConfigError, Config, File}; -use std::path::Path; -use serde::{Serialize, Deserialize}; -use tokio::sync::RwLock; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Website { - pub name: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Tracker { - pub url: String, - pub api_url: String, - pub token: String, - pub token_valid_seconds: u64, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Network { - pub port: u16, - pub base_url: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum EmailOnSignup { - Required, - Optional, - None -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Auth { - pub email_on_signup: EmailOnSignup, - pub min_password_length: usize, - pub max_password_length: usize, - pub secret_key: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Database { - pub connect_url: String, - pub torrent_info_update_interval: u64, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Storage { - pub upload_path: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Mail { - pub email_verification_enabled: bool, - pub from: String, - pub reply_to: String, - pub username: String, - pub password: String, - pub server: String, - pub port: u16, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct TorrustConfig { - pub website: Website, - pub tracker: Tracker, - pub net: Network, - pub auth: Auth, - pub database: Database, - pub storage: Storage, - pub mail: Mail, -} - -#[derive(Debug)] -pub struct Configuration { - // pub website: Website, - // pub tracker: Tracker, - // pub net: Network, - // pub auth: Auth, - // pub database: Database, - // pub storage: Storage, - // pub mail: Mail, - pub settings: RwLock -} - -impl Configuration { - pub fn default() -> Configuration { - let torrust_config = TorrustConfig { - website: Website { - name: "Torrust".to_string() - }, - tracker: Tracker { - url: "udp://localhost:6969".to_string(), - api_url: "http://localhost:1212".to_string(), - token: "MyAccessToken".to_string(), - token_valid_seconds: 7257600 - }, - net: Network { - port: 3000, - base_url: None - }, - auth: Auth { - email_on_signup: EmailOnSignup::Optional, - min_password_length: 6, - max_password_length: 64, - secret_key: "MaxVerstappenWC2021".to_string() - }, - database: Database { - connect_url: "sqlite://data.db?mode=rwc".to_string(), - torrent_info_update_interval: 3600 - }, - storage: Storage { - upload_path: "./uploads".to_string() - }, - mail: Mail { - email_verification_enabled: false, - from: "example@email.com".to_string(), - reply_to: "noreply@email.com".to_string(), - username: "".to_string(), - password: "".to_string(), - server: "".to_string(), - port: 25 - } - }; - - Configuration { - settings: RwLock::new(torrust_config) - } - } - - pub async fn load_from_file() -> Result { - let mut config = Config::new(); - - const CONFIG_PATH: &str = "config.toml"; - - if Path::new(CONFIG_PATH).exists() { - config.merge(File::with_name(CONFIG_PATH))?; - } else { - eprintln!("No config file found."); - eprintln!("Creating config file.."); - let config = Configuration::default(); - let _ = config.save_to_file().await; - return Err(ConfigError::Message(format!("Please edit the config.TOML in the root folder and restart the tracker."))) - } - - let torrust_config: TorrustConfig = match config.try_into() { - Ok(data) => Ok(data), - Err(e) => Err(ConfigError::Message(format!("Errors while processing config: {}.", e))), - }?; - - Ok(Configuration { - settings: RwLock::new(torrust_config) - }) - } - - pub async fn save_to_file(&self) -> Result<(), ()>{ - let settings = self.settings.read().await; - - let toml_string = toml::to_string(&*settings).expect("Could not encode TOML value"); - - drop(settings); - - fs::write("config.toml", toml_string).expect("Could not write to file!"); - Ok(()) - } - - pub async fn update_settings(&self, new_settings: TorrustConfig) -> Result<(), ()> { - let mut settings = self.settings.write().await; - *settings = new_settings; - - drop(settings); - - let _ = self.save_to_file().await; - - Ok(()) - } -} - -impl Configuration { - pub async fn get_public(&self) -> ConfigurationPublic { - let settings_lock = self.settings.read().await; - - ConfigurationPublic { - website_name: settings_lock.website.name.clone(), - tracker_url: settings_lock.tracker.url.clone(), - email_on_signup: settings_lock.auth.email_on_signup.clone() - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ConfigurationPublic { - website_name: String, - tracker_url: String, - email_on_signup: EmailOnSignup -} +use config::{Config, ConfigError, File}; +use serde::{Deserialize, Serialize}; +use std::fs; +use std::path::Path; +use tokio::sync::RwLock; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Website { + pub name: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Tracker { + pub url: String, + pub api_url: String, + pub token: String, + pub token_valid_seconds: u64, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Network { + pub port: u16, + pub base_url: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum EmailOnSignup { + Required, + Optional, + None, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Auth { + pub email_on_signup: EmailOnSignup, + pub min_password_length: usize, + pub max_password_length: usize, + pub secret_key: String, + pub session_duration_seconds: u64, + pub renewal_grace_time: u64, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Database { + pub connect_url: String, + pub torrent_info_update_interval: u64, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Storage { + pub upload_path: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Mail { + pub email_verification_enabled: bool, + pub from: String, + pub reply_to: String, + pub username: String, + pub password: String, + pub server: String, + pub port: u16, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TorrustConfig { + pub website: Website, + pub tracker: Tracker, + pub net: Network, + pub auth: Auth, + pub database: Database, + pub storage: Storage, + pub mail: Mail, +} + +#[derive(Debug)] +pub struct Configuration { + // pub website: Website, + // pub tracker: Tracker, + // pub net: Network, + // pub auth: Auth, + // pub database: Database, + // pub storage: Storage, + // pub mail: Mail, + pub settings: RwLock, +} + +impl Configuration { + pub fn default() -> Configuration { + let torrust_config = TorrustConfig { + website: Website { + name: "Torrust".to_string(), + }, + tracker: Tracker { + url: "udp://localhost:6969".to_string(), + api_url: "http://localhost:1212".to_string(), + token: "MyAccessToken".to_string(), + token_valid_seconds: 7257600, + }, + net: Network { + port: 3000, + base_url: None, + }, + auth: Auth { + email_on_signup: EmailOnSignup::Optional, + min_password_length: 6, + max_password_length: 64, + secret_key: "MaxVerstappenWC2021".to_string(), + session_duration_seconds: 604_800, + renewal_grace_time: 604_800, + }, + database: Database { + connect_url: "sqlite://data.db?mode=rwc".to_string(), + torrent_info_update_interval: 3600, + }, + storage: Storage { + upload_path: "./uploads".to_string(), + }, + mail: Mail { + email_verification_enabled: false, + from: "example@email.com".to_string(), + reply_to: "noreply@email.com".to_string(), + username: "".to_string(), + password: "".to_string(), + server: "".to_string(), + port: 25, + }, + }; + + Configuration { + settings: RwLock::new(torrust_config), + } + } + + pub async fn load_from_file() -> Result { + let mut config = Config::new(); + + const CONFIG_PATH: &str = "config.toml"; + + if Path::new(CONFIG_PATH).exists() { + config.merge(File::with_name(CONFIG_PATH))?; + } else { + eprintln!("No config file found."); + eprintln!("Creating config file.."); + let config = Configuration::default(); + let _ = config.save_to_file().await; + return Err(ConfigError::Message(format!( + "Please edit the config.TOML in the root folder and restart the tracker." + ))); + } + + let torrust_config: TorrustConfig = match config.try_into() { + Ok(data) => Ok(data), + Err(e) => Err(ConfigError::Message(format!( + "Errors while processing config: {}.", + e + ))), + }?; + + Ok(Configuration { + settings: RwLock::new(torrust_config), + }) + } + + pub async fn save_to_file(&self) -> Result<(), ()> { + let settings = self.settings.read().await; + + let toml_string = toml::to_string(&*settings).expect("Could not encode TOML value"); + + drop(settings); + + fs::write("config.toml", toml_string).expect("Could not write to file!"); + Ok(()) + } + + pub async fn update_settings(&self, new_settings: TorrustConfig) -> Result<(), ()> { + let mut settings = self.settings.write().await; + *settings = new_settings; + + drop(settings); + + let _ = self.save_to_file().await; + + Ok(()) + } +} + +impl Configuration { + pub async fn get_public(&self) -> ConfigurationPublic { + let settings_lock = self.settings.read().await; + + ConfigurationPublic { + website_name: settings_lock.website.name.clone(), + tracker_url: settings_lock.tracker.url.clone(), + email_on_signup: settings_lock.auth.email_on_signup.clone(), + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ConfigurationPublic { + website_name: String, + tracker_url: String, + email_on_signup: EmailOnSignup, +} diff --git a/backend/src/database.rs b/backend/src/database.rs index aae808f0..0acde080 100644 --- a/backend/src/database.rs +++ b/backend/src/database.rs @@ -1,184 +1,256 @@ -use sqlx::SqlitePool; -use sqlx::sqlite::SqlitePoolOptions; -use crate::models::user::User; -use crate::errors::ServiceError; -use crate::models::torrent::TorrentListing; -use crate::utils::time::current_time; -use crate::models::tracker_key::TrackerKey; -use serde::Serialize; - -#[derive(Debug, Serialize)] -pub struct TorrentCompact { - pub torrent_id: i64, - pub info_hash: String, -} - -pub struct Database { - pub pool: SqlitePool -} - -pub struct Category { - pub name: String -} - -impl Database { - pub async fn new(database_url: &str) -> Database { - let db = SqlitePoolOptions::new() - .connect(database_url) - .await - .expect("Unable to create database pool"); - - Database { - pool: db - } - } - - pub async fn get_user_with_username(&self, username: &str) -> Option { - let res = sqlx::query_as!( - User, - "SELECT * FROM torrust_users WHERE username = ?", - username, - ) - .fetch_one(&self.pool) - .await; - - match res { - Ok(user) => Some(user), - _ => None - } - } - - pub async fn delete_user(&self, user_id: i64) -> Result<(), sqlx::Error> { - let _res = sqlx::query!( - "DELETE FROM torrust_users WHERE rowid = ?", - user_id - ) - .execute(&self.pool) - .await?; - - Ok(()) - } - - pub async fn insert_torrent_and_get_id(&self, username: String, info_hash: String, title: String, category_id: i64, description: String, file_size: i64, seeders: i64, leechers: i64) -> Result { - let current_time = current_time() as i64; - - let res = sqlx::query!( - r#"INSERT INTO torrust_torrents (uploader, info_hash, title, category_id, description, upload_date, file_size, seeders, leechers) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) - RETURNING torrent_id as "torrent_id: i64""#, - username, - info_hash, - title, - category_id, - description, - current_time, - file_size, - seeders, - leechers - ) - .fetch_one(&self.pool) - .await?; - - Ok(res.torrent_id) - } - - pub async fn get_torrent_by_id(&self, torrent_id: i64) -> Result { - let res = sqlx::query_as!( - TorrentListing, - r#"SELECT * FROM torrust_torrents - WHERE torrent_id = ?"#, - torrent_id - ) - .fetch_one(&self.pool) - .await; - - match res { - Ok(torrent) => Ok(torrent), - _ => Err(ServiceError::TorrentNotFound) - } - } - - pub async fn get_all_torrent_ids(&self) -> Result, ()> { - let res = sqlx::query_as!( - TorrentCompact, - r#"SELECT torrent_id, info_hash FROM torrust_torrents"# - ) - .fetch_all(&self.pool) - .await; - - match res { - Ok(torrents) => Ok(torrents), - Err(e) => { - println!("{:?}", e); - Err(()) - } - } - } - - pub async fn update_tracker_info(&self, info_hash: &str, seeders: i64, leechers: i64) -> Result<(), ()> { - let res = sqlx::query!( - "UPDATE torrust_torrents SET seeders = $1, leechers = $2 WHERE info_hash = $3", - seeders, - leechers, - info_hash - ) - .execute(&self.pool) - .await; - - match res { - Ok(_) => Ok(()), - _ => Err(()) - } - } - - pub async fn get_valid_tracker_key(&self, user_id: i64) -> Option { - const WEEK: i64 = 604_800; - let current_time_plus_week = (current_time() as i64) + WEEK; - - let res = sqlx::query_as!( - TrackerKey, - r#"SELECT key, valid_until FROM torrust_tracker_keys - WHERE user_id = $1 AND valid_until > $2"#, - user_id, - current_time_plus_week - ) - .fetch_one(&self.pool) - .await; - - match res { - Ok(tracker_key) => Some(tracker_key), - _ => None - } - } - - pub async fn issue_tracker_key(&self, tracker_key: &TrackerKey, user_id: i64) -> Result<(), ServiceError> { - let res = sqlx::query!( - "INSERT INTO torrust_tracker_keys (user_id, key, valid_until) VALUES ($1, $2, $3)", - user_id, - tracker_key.key, - tracker_key.valid_until, - ) - .execute(&self.pool) - .await; - - match res { - Ok(_) => Ok(()), - Err(_) => Err(ServiceError::InternalServerError) - } - } - - pub async fn verify_category(&self, category: &str) -> Option { - let res = sqlx::query_as!( - Category, - "SELECT name FROM torrust_categories WHERE name = ?", - category - ) - .fetch_one(&self.pool) - .await; - - match res { - Ok(v) => Some(v.name), - Err(_) => None - } - } -} +use crate::errors::ServiceError; +use crate::models::torrent::TorrentListing; +use crate::models::tracker_key::TrackerKey; +use crate::models::user::{User, UserCompact}; +use crate::utils::time::current_time; +use serde::Serialize; +use sqlx::sqlite::SqlitePoolOptions; +use sqlx::{query_as, SqlitePool}; + +/// Database errors. +#[derive(Debug)] +pub enum Error { + Error, + ErrorWithText(String), + UnrecognizedDatabaseDriver, // when the db path does not start with sqlite or mysql + UsernameTaken, + EmailTaken, + UserNotFound, + CategoryNotFound, + TagAlreadyExists, + TagNotFound, + TorrentNotFound, + TorrentAlreadyExists, // when uploading an already uploaded info_hash + TorrentTitleAlreadyExists, + TorrentInfoHashNotFound, +} + +#[derive(Debug, Serialize)] +pub struct TorrentCompact { + pub torrent_id: i64, + pub info_hash: String, +} + +// pub trait Database: Sync + Send { +// async fn get_user_with_username(&self, username: &str) -> Option; +// async fn delete_user(&self, user_id: i64) -> Result<(), sqlx::Error>; +// async fn insert_torrent_and_get_id( +// &self, +// username: String, +// info_hash: String, +// title: String, +// category_id: i64, +// description: String, +// file_size: i64, +// seeders: i64, +// leechers: i64, +// ) -> Result; +// async fn get_torrent_by_id(&self, torrent_id: i64) -> Result; +// async fn get_all_torrent_ids(&self) -> Result, ()>; +// async fn update_tracker_info( +// &self, +// info_hash: &str, +// seeders: i64, +// leechers: i64, +// ) -> Result<(), ()>; +// async fn get_valid_tracker_key(&self, user_id: i64) -> Option; +// async fn issue_tracker_key( +// &self, +// tracker_key: &TrackerKey, +// user_id: i64, +// ) -> Result<(), ServiceError>; +// async fn verify_category(&self, category: &str) -> Option; +// async fn get_user_compact_from_id(&self, user_id: i64) -> Result; +// } + +pub struct SqliteDatabase { + pub pool: SqlitePool, +} + +pub struct Category { + pub name: String, +} + +impl SqliteDatabase { + pub async fn new(database_url: &str) -> SqliteDatabase { + let db = SqlitePoolOptions::new() + .connect(database_url) + .await + .expect("Unable to create database pool"); + + SqliteDatabase { pool: db } + } + + pub async fn get_user_with_username(&self, username: &str) -> Option { + let res = sqlx::query_as!( + User, + "SELECT * FROM torrust_users WHERE username = ?", + username, + ) + .fetch_one(&self.pool) + .await; + + match res { + Ok(user) => Some(user), + _ => None, + } + } + + pub async fn delete_user(&self, user_id: i64) -> Result<(), sqlx::Error> { + let _res = sqlx::query!("DELETE FROM torrust_users WHERE rowid = ?", user_id) + .execute(&self.pool) + .await?; + + Ok(()) + } + + pub async fn insert_torrent_and_get_id( + &self, + username: String, + info_hash: String, + title: String, + category_id: i64, + description: String, + file_size: i64, + seeders: i64, + leechers: i64, + ) -> Result { + let current_time = current_time() as i64; + + let res = sqlx::query!( + r#"INSERT INTO torrust_torrents (uploader, info_hash, title, category_id, description, upload_date, file_size, seeders, leechers) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) + RETURNING torrent_id as "torrent_id: i64""#, + username, + info_hash, + title, + category_id, + description, + current_time, + file_size, + seeders, + leechers + ) + .fetch_one(&self.pool) + .await?; + + Ok(res.torrent_id) + } + + pub async fn get_torrent_by_id(&self, torrent_id: i64) -> Result { + let res = sqlx::query_as!( + TorrentListing, + r#"SELECT * FROM torrust_torrents + WHERE torrent_id = ?"#, + torrent_id + ) + .fetch_one(&self.pool) + .await; + + match res { + Ok(torrent) => Ok(torrent), + _ => Err(ServiceError::TorrentNotFound), + } + } + + pub async fn get_all_torrent_ids(&self) -> Result, ()> { + let res = sqlx::query_as!( + TorrentCompact, + r#"SELECT torrent_id, info_hash FROM torrust_torrents"# + ) + .fetch_all(&self.pool) + .await; + + match res { + Ok(torrents) => Ok(torrents), + Err(e) => { + println!("{:?}", e); + Err(()) + } + } + } + + pub async fn update_tracker_info( + &self, + info_hash: &str, + seeders: i64, + leechers: i64, + ) -> Result<(), ()> { + let res = sqlx::query!( + "UPDATE torrust_torrents SET seeders = $1, leechers = $2 WHERE info_hash = $3", + seeders, + leechers, + info_hash + ) + .execute(&self.pool) + .await; + + match res { + Ok(_) => Ok(()), + _ => Err(()), + } + } + + pub async fn get_valid_tracker_key(&self, user_id: i64) -> Option { + const WEEK: i64 = 604_800; + let current_time_plus_week = (current_time() as i64) + WEEK; + + let res = sqlx::query_as!( + TrackerKey, + r#"SELECT key, valid_until FROM torrust_tracker_keys + WHERE user_id = $1 AND valid_until > $2"#, + user_id, + current_time_plus_week + ) + .fetch_one(&self.pool) + .await; + + match res { + Ok(tracker_key) => Some(tracker_key), + _ => None, + } + } + + pub async fn issue_tracker_key( + &self, + tracker_key: &TrackerKey, + user_id: i64, + ) -> Result<(), ServiceError> { + let res = sqlx::query!( + "INSERT INTO torrust_tracker_keys (user_id, key, valid_until) VALUES ($1, $2, $3)", + user_id, + tracker_key.key, + tracker_key.valid_until, + ) + .execute(&self.pool) + .await; + + match res { + Ok(_) => Ok(()), + Err(_) => Err(ServiceError::InternalServerError), + } + } + + pub async fn verify_category(&self, category: &str) -> Option { + let res = sqlx::query_as!( + Category, + "SELECT name FROM torrust_categories WHERE name = ?", + category + ) + .fetch_one(&self.pool) + .await; + + match res { + Ok(v) => Some(v.name), + Err(_) => None, + } + } + + pub async fn get_user_compact_from_id(&self, user_id: i64) -> Result { + query_as::<_, UserCompact>("SELECT tu.user_id, tu.username, tu.administrator FROM torrust_users tu WHERE tu.user_id = ?") + .bind(user_id) + .fetch_one(&self.pool) + .await + .map_err(|_| Error::UserNotFound) + } +} diff --git a/backend/src/handlers/torrent.rs b/backend/src/handlers/torrent.rs index 8866a27e..cb0735fb 100644 --- a/backend/src/handlers/torrent.rs +++ b/backend/src/handlers/torrent.rs @@ -1,481 +1,560 @@ -use actix_multipart::Multipart; -use actix_web::{HttpRequest, HttpResponse, Responder, web}; -use actix_web::web::{Query}; -use futures::{AsyncWriteExt, StreamExt, TryStreamExt}; -use serde::{Deserialize}; -use crate::errors::{ServiceError, ServiceResult}; -use crate::models::response::{NewTorrentResponse, OkResponse, TorrentResponse, TorrentsResponse}; -use crate::models::torrent::{TorrentListing, TorrentRequest}; -use crate::utils::parse_torrent; -use crate::common::{WebAppData}; -use std::io::Cursor; -use std::io::{Write}; -use crate::models::torrent_file::{Torrent, File}; -use crate::AsCSV; -use std::option::Option::Some; -use sqlx::{FromRow}; - -pub fn init_routes(cfg: &mut web::ServiceConfig) { - cfg.service( - web::scope("/torrent") - .service(web::resource("/upload") - .route(web::post().to(upload_torrent))) - .service(web::resource("/download/{id}") - .route(web::get().to(download_torrent))) - .service(web::resource("/{id}") - .route(web::get().to(get_torrent)) - .route(web::put().to(update_torrent)) - .route(web::delete().to(delete_torrent))) - ); - cfg.service( - web::scope("/torrents") - .service(web::resource("") - .route(web::get().to(get_torrents))) - ); -} - -#[derive(Debug, Deserialize)] -pub struct DisplayInfo { - page_size: Option, - page: Option, - sort: Option, - // expects comma separated string, eg: "?categories=movie,other,app" - categories: Option, - search: Option, -} - -#[derive(FromRow)] -pub struct TorrentCount { - pub count: i32, -} - -#[derive(Debug, Deserialize)] -pub struct CreateTorrent { - pub title: String, - pub description: String, - pub category: String, -} - -impl CreateTorrent { - pub fn verify(&self) -> Result<(), ServiceError>{ - if !self.title.is_empty() && !self.category.is_empty() { - return Ok(()) - } - - Err(ServiceError::BadRequest) - } -} - -// eg: /torrents?categories=music,other,movie&search=bunny&sort=size_DESC -pub async fn get_torrents(params: Query, app_data: WebAppData) -> ServiceResult { - let page = params.page.unwrap_or(0); - let page_size = params.page_size.unwrap_or(30); - let offset = page * page_size; - let categories = params.categories.as_csv::().unwrap_or(None); - let search = match ¶ms.search { - None => "%".to_string(), - Some(v) => format!("%{}%", v) - }; - - let sort_query: String = match ¶ms.sort { - Some(sort) => { - match sort.as_str() { - "uploaded_ASC" => "upload_date ASC".to_string(), - "uploaded_DESC" => "upload_date DESC".to_string(), - "seeders_ASC" => "seeders ASC".to_string(), - "seeders_DESC" => "seeders DESC".to_string(), - "leechers_ASC" => "leechers ASC".to_string(), - "leechers_DESC" => "leechers DESC".to_string(), - "name_ASC" => "title ASC".to_string(), - "name_DESC" => "title DESC".to_string(), - "size_ASC" => "file_size ASC".to_string(), - "size_DESC" => "file_size DESC".to_string(), - _ => "upload_date DESC".to_string() - } - } - None => "upload_date DESC".to_string() - }; - - let category_filter_query = if let Some(c) = categories { - let mut i = 0; - let mut category_filters = String::new(); - for category in c.iter() { - // don't take user input in the db query - if let Some(sanitized_category) = &app_data.database.verify_category(category).await { - let mut str = format!("tc.name = '{}'", sanitized_category); - if i > 0 { str = format!(" OR {}", str); } - category_filters.push_str(&str); - i += 1; - } - } - if category_filters.len() > 0 { - format!("INNER JOIN torrust_categories tc ON tt.category_id = tc.category_id AND ({})", category_filters) - } else { - String::new() - } - } else { - String::new() - }; - - let mut query_string = format!("SELECT tt.* FROM torrust_torrents tt {} WHERE title LIKE ?", category_filter_query); - let count_query_string = format!("SELECT COUNT(torrent_id) as count FROM ({})", query_string); - - let count: TorrentCount = sqlx::query_as::<_, TorrentCount>(&count_query_string) - .bind(search.clone()) - .fetch_one(&app_data.database.pool) - .await?; - - query_string = format!("{} ORDER BY {} LIMIT ?, ?", query_string, sort_query); - - let res: Vec = sqlx::query_as::<_, TorrentListing>(&query_string) - .bind(search) - .bind(offset) - .bind(page_size) - .fetch_all(&app_data.database.pool).await?; - - let torrents_response = TorrentsResponse { - total: count.count as u32, - results: res - }; - - Ok(HttpResponse::Ok().json(OkResponse { - data: torrents_response - })) -} - -pub async fn get_torrent(req: HttpRequest, app_data: WebAppData) -> ServiceResult { - // optional - let user = app_data.auth.get_user_from_request(&req).await; - - let settings = app_data.cfg.settings.read().await; - - let torrent_id = get_torrent_id_from_request(&req)?; - - let torrent_listing = app_data.database.get_torrent_by_id(torrent_id).await?; - let mut torrent_response = TorrentResponse::from_listing(torrent_listing); - - let filepath = format!("{}/{}", settings.storage.upload_path, torrent_response.torrent_id.to_string() + ".torrent"); - - let tracker_url = settings.tracker.url.clone(); - - drop(settings); - - if let Ok(torrent) = parse_torrent::read_torrent_from_file(&filepath) { - // add torrent file/files to response - if let Some(files) = torrent.info.files { - torrent_response.files = Some(files); - } else { - // todo: tidy up this code, it's error prone - let file = File { - path: vec![torrent.info.name], - length: torrent.info.length.unwrap_or(0), - md5sum: None - }; - - torrent_response.files = Some(vec![file]); - } - - // add additional torrent tracker/trackers to response - if let Some(trackers) = torrent.announce_list { - for tracker in trackers { - torrent_response.trackers.push(tracker[0].clone()); - } - } - } - - // add self-hosted tracker url - if user.is_ok() { - let unwrapped_user = user.unwrap(); - let personal_announce_url = app_data.tracker.get_personal_announce_url(&unwrapped_user).await?; - // add personal tracker url to front of vec - torrent_response.trackers.insert(0, personal_announce_url); - } else { - // add tracker to front of vec - torrent_response.trackers.insert(0, tracker_url); - } - - // add magnet link - let mut magnet = format!("magnet:?xt=urn:btih:{}&dn={}", torrent_response.info_hash, urlencoding::encode(&torrent_response.title)); - // add trackers from torrent file to magnet link - for tracker in &torrent_response.trackers { - magnet.push_str(&format!("&tr={}", urlencoding::encode(tracker))); - } - torrent_response.magnet_link = magnet; - - // get realtime seeders and leechers - if let Ok(torrent_info) = app_data.tracker.get_torrent_info(&torrent_response.info_hash).await { - torrent_response.seeders = torrent_info.seeders; - torrent_response.leechers = torrent_info.leechers; - } - - Ok(HttpResponse::Ok().json(OkResponse { - data: torrent_response - })) -} - -#[derive(Debug, Deserialize)] -pub struct TorrentUpdate { - description: String -} - -pub async fn update_torrent(req: HttpRequest, payload: web::Json, app_data: WebAppData) -> ServiceResult { - let user = app_data.auth.get_user_from_request(&req).await?; - - let torrent_id = get_torrent_id_from_request(&req)?; - - let torrent_listing = app_data.database.get_torrent_by_id(torrent_id).await?; - - // check if user is owner or administrator - if torrent_listing.uploader != user.username && !user.administrator { return Err(ServiceError::Unauthorized) } - - // update torrent - let res = sqlx::query!( - "UPDATE torrust_torrents SET description = $1 WHERE torrent_id = $2", - payload.description, - torrent_id - ) - .execute(&app_data.database.pool) - .await; - - if let Err(_) = res { return Err(ServiceError::TorrentNotFound) } - - if res.unwrap().rows_affected() == 0 { return Err(ServiceError::TorrentNotFound) } - - let mut torrent_response = TorrentResponse::from_listing(torrent_listing); - torrent_response.description = Some(payload.description.clone()); - - Ok(HttpResponse::Ok().json(OkResponse { - data: torrent_response - })) -} - -pub async fn delete_torrent(req: HttpRequest, app_data: WebAppData) -> ServiceResult { - let user = app_data.auth.get_user_from_request(&req).await?; - - // check if user is administrator - if !user.administrator { return Err(ServiceError::Unauthorized) } - - let torrent_id = get_torrent_id_from_request(&req)?; - - let res = sqlx::query!( - "DELETE FROM torrust_torrents WHERE torrent_id = ?", - torrent_id - ) - .execute(&app_data.database.pool) - .await; - - if let Err(_) = res { return Err(ServiceError::TorrentNotFound) } - if res.unwrap().rows_affected() == 0 { return Err(ServiceError::TorrentNotFound) } - - Ok(HttpResponse::Ok().json(OkResponse { - data: NewTorrentResponse { - torrent_id - } - })) -} - -pub async fn upload_torrent(req: HttpRequest, payload: Multipart, app_data: WebAppData) -> ServiceResult { - let user = app_data.auth.get_user_from_request(&req).await?; - - let mut torrent_request = get_torrent_request_from_payload(payload).await?; - - // update announce url to our own tracker url - torrent_request.torrent.set_torrust_config(&app_data.cfg).await; - - let res = sqlx::query!( - "SELECT category_id FROM torrust_categories WHERE name = ?", - torrent_request.fields.category - ) - .fetch_one(&app_data.database.pool) - .await; - - let row = match res { - Ok(row) => row, - Err(_) => return Err(ServiceError::InvalidCategory), - }; - - let username = user.username; - let info_hash = torrent_request.torrent.info_hash(); - let title = torrent_request.fields.title; - //let category = torrent_request.fields.category; - let description = torrent_request.fields.description; - //let current_time = current_time() as i64; - let file_size = torrent_request.torrent.file_size(); - let mut seeders = 0; - let mut leechers = 0; - - if let Ok(torrent_info) = app_data.tracker.get_torrent_info(&info_hash).await { - seeders = torrent_info.seeders; - leechers = torrent_info.leechers; - } - - let torrent_id = app_data.database.insert_torrent_and_get_id(username, info_hash, title, row.category_id, description, file_size, seeders, leechers).await?; - - // whitelist info hash on tracker - let _ = app_data.tracker.whitelist_info_hash(torrent_request.torrent.info_hash()).await; - - let settings = app_data.cfg.settings.read().await; - - let upload_folder = settings.storage.upload_path.clone(); - let filepath = format!("{}/{}", upload_folder, torrent_id.to_string() + ".torrent"); - - drop(settings); - - save_torrent_file(&upload_folder, &filepath, &torrent_request.torrent).await?; - - Ok(HttpResponse::Ok().json(OkResponse { - data: NewTorrentResponse { - torrent_id - } - })) -} - -pub async fn download_torrent(req: HttpRequest, app_data: WebAppData) -> ServiceResult { - let torrent_id = get_torrent_id_from_request(&req)?; - - let settings = app_data.cfg.settings.read().await; - - // optional - let user = app_data.auth.get_user_from_request(&req).await; - - let filepath = format!("{}/{}", settings.storage.upload_path, torrent_id.to_string() + ".torrent"); - - let mut torrent = match parse_torrent::read_torrent_from_file(&filepath) { - Ok(torrent) => Ok(torrent), - Err(e) => { - println!("{:?}", e); - Err(ServiceError::InternalServerError) - } - }?; - - if user.is_ok() { - let unwrapped_user = user.unwrap(); - let personal_announce_url = app_data.tracker.get_personal_announce_url(&unwrapped_user).await?; - torrent.announce = Some(personal_announce_url.clone()); - if let Some(list) = &mut torrent.announce_list { - let mut vec = Vec::new(); - vec.push(personal_announce_url); - list.insert(0, vec); - } - } else { - torrent.announce = Some(settings.tracker.url.clone()); - } - - drop(settings); - - let buffer = match parse_torrent::encode_torrent(&torrent) { - Ok(v) => Ok(v), - Err(e) => { - println!("{:?}", e); - Err(ServiceError::InternalServerError) - } - }?; - - Ok(HttpResponse::Ok() - .content_type("application/x-bittorrent") - .body(buffer) - ) -} - -// async fn verify_torrent_ownership(user: &User, torrent_listing: &TorrentListing) -> Result<(), ServiceError> { -// match torrent_listing.uploader == user.username { -// true => Ok(()), -// false => Err(ServiceError::BadRequest) -// } -// } - -async fn save_torrent_file(upload_folder: &str, filepath: &str, torrent: &Torrent) -> Result<(), ServiceError> { - let torrent_bytes = match parse_torrent::encode_torrent(torrent) { - Ok(v) => Ok(v), - Err(_) => Err(ServiceError::InternalServerError) - }?; - - // create torrent upload folder if it does not exist - async_std::fs::create_dir_all(&upload_folder).await?; - - let mut f = match async_std::fs::File::create(&filepath).await { - Ok(v) => Ok(v), - Err(_) => Err(ServiceError::InternalServerError) - }?; - - match AsyncWriteExt::write_all(&mut f, &torrent_bytes.as_slice()).await { - Ok(v) => Ok(v), - Err(_) => Err(ServiceError::InternalServerError) - }?; - - Ok(()) -} - -fn get_torrent_id_from_request(req: &HttpRequest) -> Result { - match req.match_info().get("id") { - None => Err(ServiceError::BadRequest), - Some(torrent_id) => { - match torrent_id.parse() { - Err(_) => Err(ServiceError::BadRequest), - Ok(v) => Ok(v) - } - } - } -} - -async fn get_torrent_request_from_payload(mut payload: Multipart) -> Result { - let torrent_buffer = vec![0u8]; - let mut torrent_cursor = Cursor::new(torrent_buffer); - - let mut title = "".to_string(); - let mut description = "".to_string(); - let mut category = "".to_string(); - - while let Ok(Some(mut field)) = payload.try_next().await { - let content_type = field.content_disposition().unwrap(); - let name = content_type.get_name().unwrap(); - - match name { - "title" | "description" | "category" => { - let data = field.next().await; - if data.is_none() { continue } - let wrapped_data = &data.unwrap().unwrap(); - let parsed_data = std::str::from_utf8(&wrapped_data).unwrap(); - - match name { - "title" => { title = parsed_data.to_string() } - "description" => { description = parsed_data.to_string() } - "category" => { category = parsed_data.to_string() } - _ => {} - } - } - "torrent" => { - if field.content_type().to_string() != "application/x-bittorrent" { - return Err(ServiceError::InvalidFileType) - } - - while let Some(chunk) = field.next().await { - let data = chunk.unwrap(); - torrent_cursor.write_all(&data)?; - } - } - _ => {} - } - } - - let fields = CreateTorrent { - title, - description, - category, - }; - - fields.verify()?; - - let position = torrent_cursor.position() as usize; - let inner = torrent_cursor.get_ref(); - - let torrent = match parse_torrent::decode_torrent(&inner[..position]) { - Ok(torrent) => Ok(torrent), - Err(_) => Err(ServiceError::InvalidTorrentFile) - }?; - - Ok(TorrentRequest { - fields, - torrent, - }) -} +use crate::common::WebAppData; +use crate::errors::{ServiceError, ServiceResult}; +use crate::models::response::{NewTorrentResponse, OkResponse, TorrentResponse, TorrentsResponse}; +use crate::models::torrent::{TorrentListing, TorrentRequest}; +use crate::models::torrent_file::{File, Torrent}; +use crate::utils::parse_torrent; +use crate::AsCSV; +use actix_multipart::Multipart; +use actix_web::web::Query; +use actix_web::{web, HttpRequest, HttpResponse, Responder}; +use futures::{AsyncWriteExt, StreamExt, TryStreamExt}; +use serde::Deserialize; +use sqlx::FromRow; +use std::io::Cursor; +use std::io::Write; +use std::option::Option::Some; + +pub fn init_routes(cfg: &mut web::ServiceConfig) { + cfg.service( + web::scope("/torrent") + .service(web::resource("/upload").route(web::post().to(upload_torrent))) + .service(web::resource("/download/{id}").route(web::get().to(download_torrent))) + .service( + web::resource("/{id}") + .route(web::get().to(get_torrent)) + .route(web::put().to(update_torrent)) + .route(web::delete().to(delete_torrent)), + ), + ); + cfg.service( + web::scope("/torrents").service(web::resource("").route(web::get().to(get_torrents))), + ); +} + +#[derive(Debug, Deserialize)] +pub struct DisplayInfo { + page_size: Option, + page: Option, + sort: Option, + // expects comma separated string, eg: "?categories=movie,other,app" + categories: Option, + search: Option, +} + +#[derive(FromRow)] +pub struct TorrentCount { + pub count: i32, +} + +#[derive(Debug, Deserialize)] +pub struct CreateTorrent { + pub title: String, + pub description: String, + pub category: String, +} + +impl CreateTorrent { + pub fn verify(&self) -> Result<(), ServiceError> { + if !self.title.is_empty() && !self.category.is_empty() { + return Ok(()); + } + + Err(ServiceError::BadRequest) + } +} + +// eg: /torrents?categories=music,other,movie&search=bunny&sort=size_DESC +pub async fn get_torrents( + params: Query, + app_data: WebAppData, +) -> ServiceResult { + let page = params.page.unwrap_or(0); + let page_size = params.page_size.unwrap_or(30); + let offset = page * page_size; + let categories = params.categories.as_csv::().unwrap_or(None); + let search = match ¶ms.search { + None => "%".to_string(), + Some(v) => format!("%{}%", v), + }; + + let sort_query: String = match ¶ms.sort { + Some(sort) => match sort.as_str() { + "uploaded_ASC" => "upload_date ASC".to_string(), + "uploaded_DESC" => "upload_date DESC".to_string(), + "seeders_ASC" => "seeders ASC".to_string(), + "seeders_DESC" => "seeders DESC".to_string(), + "leechers_ASC" => "leechers ASC".to_string(), + "leechers_DESC" => "leechers DESC".to_string(), + "name_ASC" => "title ASC".to_string(), + "name_DESC" => "title DESC".to_string(), + "size_ASC" => "file_size ASC".to_string(), + "size_DESC" => "file_size DESC".to_string(), + _ => "upload_date DESC".to_string(), + }, + None => "upload_date DESC".to_string(), + }; + + let category_filter_query = if let Some(c) = categories { + let mut i = 0; + let mut category_filters = String::new(); + for category in c.iter() { + // don't take user input in the db query + if let Some(sanitized_category) = &app_data.database.verify_category(category).await { + let mut str = format!("tc.name = '{}'", sanitized_category); + if i > 0 { + str = format!(" OR {}", str); + } + category_filters.push_str(&str); + i += 1; + } + } + if category_filters.len() > 0 { + format!( + "INNER JOIN torrust_categories tc ON tt.category_id = tc.category_id AND ({})", + category_filters + ) + } else { + String::new() + } + } else { + String::new() + }; + + let mut query_string = format!( + "SELECT tt.* FROM torrust_torrents tt {} WHERE title LIKE ?", + category_filter_query + ); + let count_query_string = format!("SELECT COUNT(torrent_id) as count FROM ({})", query_string); + + let count: TorrentCount = sqlx::query_as::<_, TorrentCount>(&count_query_string) + .bind(search.clone()) + .fetch_one(&app_data.database.pool) + .await?; + + query_string = format!("{} ORDER BY {} LIMIT ?, ?", query_string, sort_query); + + let res: Vec = sqlx::query_as::<_, TorrentListing>(&query_string) + .bind(search) + .bind(offset) + .bind(page_size) + .fetch_all(&app_data.database.pool) + .await?; + + let torrents_response = TorrentsResponse { + total: count.count as u32, + results: res, + }; + + Ok(HttpResponse::Ok().json(OkResponse { + data: torrents_response, + })) +} + +pub async fn get_torrent(req: HttpRequest, app_data: WebAppData) -> ServiceResult { + // optional + let user = app_data.auth.get_user_from_request(&req).await; + + let settings = app_data.cfg.settings.read().await; + + let torrent_id = get_torrent_id_from_request(&req)?; + + let torrent_listing = app_data.database.get_torrent_by_id(torrent_id).await?; + let mut torrent_response = TorrentResponse::from_listing(torrent_listing); + + let filepath = format!( + "{}/{}", + settings.storage.upload_path, + torrent_response.torrent_id.to_string() + ".torrent" + ); + + let tracker_url = settings.tracker.url.clone(); + + drop(settings); + + if let Ok(torrent) = parse_torrent::read_torrent_from_file(&filepath) { + // add torrent file/files to response + if let Some(files) = torrent.info.files { + torrent_response.files = Some(files); + } else { + // todo: tidy up this code, it's error prone + let file = File { + path: vec![torrent.info.name], + length: torrent.info.length.unwrap_or(0), + md5sum: None, + }; + + torrent_response.files = Some(vec![file]); + } + + // add additional torrent tracker/trackers to response + if let Some(trackers) = torrent.announce_list { + for tracker in trackers { + torrent_response.trackers.push(tracker[0].clone()); + } + } + } + + // add self-hosted tracker url + if user.is_ok() { + let unwrapped_user = user.unwrap(); + let personal_announce_url = app_data + .tracker + .get_personal_announce_url(&unwrapped_user) + .await?; + // add personal tracker url to front of vec + torrent_response.trackers.insert(0, personal_announce_url); + } else { + // add tracker to front of vec + torrent_response.trackers.insert(0, tracker_url); + } + + // add magnet link + let mut magnet = format!( + "magnet:?xt=urn:btih:{}&dn={}", + torrent_response.info_hash, + urlencoding::encode(&torrent_response.title) + ); + // add trackers from torrent file to magnet link + for tracker in &torrent_response.trackers { + magnet.push_str(&format!("&tr={}", urlencoding::encode(tracker))); + } + torrent_response.magnet_link = magnet; + + // get realtime seeders and leechers + if let Ok(torrent_info) = app_data + .tracker + .get_torrent_info(&torrent_response.info_hash) + .await + { + torrent_response.seeders = torrent_info.seeders; + torrent_response.leechers = torrent_info.leechers; + } + + Ok(HttpResponse::Ok().json(OkResponse { + data: torrent_response, + })) +} + +#[derive(Debug, Deserialize)] +pub struct TorrentUpdate { + description: String, +} + +pub async fn update_torrent( + req: HttpRequest, + payload: web::Json, + app_data: WebAppData, +) -> ServiceResult { + let user = app_data.auth.get_user_from_request(&req).await?; + + let torrent_id = get_torrent_id_from_request(&req)?; + + let torrent_listing = app_data.database.get_torrent_by_id(torrent_id).await?; + + // check if user is owner or administrator + if torrent_listing.uploader != user.username && !user.administrator { + return Err(ServiceError::Unauthorized); + } + + // update torrent + let res = sqlx::query!( + "UPDATE torrust_torrents SET description = $1 WHERE torrent_id = $2", + payload.description, + torrent_id + ) + .execute(&app_data.database.pool) + .await; + + if let Err(_) = res { + return Err(ServiceError::TorrentNotFound); + } + + if res.unwrap().rows_affected() == 0 { + return Err(ServiceError::TorrentNotFound); + } + + let mut torrent_response = TorrentResponse::from_listing(torrent_listing); + torrent_response.description = Some(payload.description.clone()); + + Ok(HttpResponse::Ok().json(OkResponse { + data: torrent_response, + })) +} + +pub async fn delete_torrent( + req: HttpRequest, + app_data: WebAppData, +) -> ServiceResult { + let user = app_data.auth.get_user_from_request(&req).await?; + + // check if user is administrator + if !user.administrator { + return Err(ServiceError::Unauthorized); + } + + let torrent_id = get_torrent_id_from_request(&req)?; + + let res = sqlx::query!( + "DELETE FROM torrust_torrents WHERE torrent_id = ?", + torrent_id + ) + .execute(&app_data.database.pool) + .await; + + if let Err(_) = res { + return Err(ServiceError::TorrentNotFound); + } + if res.unwrap().rows_affected() == 0 { + return Err(ServiceError::TorrentNotFound); + } + + Ok(HttpResponse::Ok().json(OkResponse { + data: NewTorrentResponse { torrent_id }, + })) +} + +pub async fn upload_torrent( + req: HttpRequest, + payload: Multipart, + app_data: WebAppData, +) -> ServiceResult { + let user = app_data.auth.get_user_from_request(&req).await?; + + let mut torrent_request = get_torrent_request_from_payload(payload).await?; + + // update announce url to our own tracker url + torrent_request + .torrent + .set_torrust_config(&app_data.cfg) + .await; + + let res = sqlx::query!( + "SELECT category_id FROM torrust_categories WHERE name = ?", + torrent_request.fields.category + ) + .fetch_one(&app_data.database.pool) + .await; + + let row = match res { + Ok(row) => row, + Err(_) => return Err(ServiceError::InvalidCategory), + }; + + let username = user.username; + let info_hash = torrent_request.torrent.info_hash(); + let title = torrent_request.fields.title; + //let category = torrent_request.fields.category; + let description = torrent_request.fields.description; + //let current_time = current_time() as i64; + let file_size = torrent_request.torrent.file_size(); + let mut seeders = 0; + let mut leechers = 0; + + if let Ok(torrent_info) = app_data.tracker.get_torrent_info(&info_hash).await { + seeders = torrent_info.seeders; + leechers = torrent_info.leechers; + } + + let torrent_id = app_data + .database + .insert_torrent_and_get_id( + username, + info_hash, + title, + row.category_id, + description, + file_size, + seeders, + leechers, + ) + .await?; + + // whitelist info hash on tracker + let _ = app_data + .tracker + .whitelist_info_hash(torrent_request.torrent.info_hash()) + .await; + + let settings = app_data.cfg.settings.read().await; + + let upload_folder = settings.storage.upload_path.clone(); + let filepath = format!("{}/{}", upload_folder, torrent_id.to_string() + ".torrent"); + + drop(settings); + + save_torrent_file(&upload_folder, &filepath, &torrent_request.torrent).await?; + + Ok(HttpResponse::Ok().json(OkResponse { + data: NewTorrentResponse { torrent_id }, + })) +} + +pub async fn download_torrent( + req: HttpRequest, + app_data: WebAppData, +) -> ServiceResult { + let torrent_id = get_torrent_id_from_request(&req)?; + + let settings = app_data.cfg.settings.read().await; + + // optional + let user = app_data.auth.get_user_from_request(&req).await; + + let filepath = format!( + "{}/{}", + settings.storage.upload_path, + torrent_id.to_string() + ".torrent" + ); + + let mut torrent = match parse_torrent::read_torrent_from_file(&filepath) { + Ok(torrent) => Ok(torrent), + Err(e) => { + println!("{:?}", e); + Err(ServiceError::InternalServerError) + } + }?; + + if user.is_ok() { + let unwrapped_user = user.unwrap(); + let personal_announce_url = app_data + .tracker + .get_personal_announce_url(&unwrapped_user) + .await?; + torrent.announce = Some(personal_announce_url.clone()); + if let Some(list) = &mut torrent.announce_list { + let mut vec = Vec::new(); + vec.push(personal_announce_url); + list.insert(0, vec); + } + drop(settings); + + let buffer = match parse_torrent::encode_torrent(&torrent) { + Ok(v) => Ok(v), + Err(e) => { + println!("{:?}", e); + Err(ServiceError::InternalServerError) + } + }?; + + Ok(HttpResponse::Ok() + .content_type("application/x-bittorrent") + .body(buffer)) + } else { + if let Err(error) = user { + Err(error) + } else { + Err(ServiceError::Unauthorized) + } + // torrent.announce = Some(settings.tracker.url.clone()); + } +} + +// async fn verify_torrent_ownership(user: &User, torrent_listing: &TorrentListing) -> Result<(), ServiceError> { +// match torrent_listing.uploader == user.username { +// true => Ok(()), +// false => Err(ServiceError::BadRequest) +// } +// } + +async fn save_torrent_file( + upload_folder: &str, + filepath: &str, + torrent: &Torrent, +) -> Result<(), ServiceError> { + let torrent_bytes = match parse_torrent::encode_torrent(torrent) { + Ok(v) => Ok(v), + Err(_) => Err(ServiceError::InternalServerError), + }?; + + // create torrent upload folder if it does not exist + async_std::fs::create_dir_all(&upload_folder).await?; + + let mut f = match async_std::fs::File::create(&filepath).await { + Ok(v) => Ok(v), + Err(_) => Err(ServiceError::InternalServerError), + }?; + + match AsyncWriteExt::write_all(&mut f, &torrent_bytes.as_slice()).await { + Ok(v) => Ok(v), + Err(_) => Err(ServiceError::InternalServerError), + }?; + + Ok(()) +} + +fn get_torrent_id_from_request(req: &HttpRequest) -> Result { + match req.match_info().get("id") { + None => Err(ServiceError::BadRequest), + Some(torrent_id) => match torrent_id.parse() { + Err(_) => Err(ServiceError::BadRequest), + Ok(v) => Ok(v), + }, + } +} + +async fn get_torrent_request_from_payload( + mut payload: Multipart, +) -> Result { + let torrent_buffer = vec![0u8]; + let mut torrent_cursor = Cursor::new(torrent_buffer); + + let mut title = "".to_string(); + let mut description = "".to_string(); + let mut category = "".to_string(); + + while let Ok(Some(mut field)) = payload.try_next().await { + let content_type = field.content_disposition().unwrap().clone(); + let name = content_type.get_name().unwrap(); + + match name { + "title" | "description" | "category" => { + let data = field.next().await; + if data.is_none() { + continue; + } + + let wrapped_data = &data.unwrap().unwrap(); + let parsed_data = std::str::from_utf8(&wrapped_data).unwrap(); + + match name { + "title" => title = parsed_data.to_string(), + "description" => description = parsed_data.to_string(), + "category" => category = parsed_data.to_string(), + _ => {} + } + } + "torrent" => { + if field.content_type().is_none() + || field.content_type().unwrap().to_string() != "application/x-bittorrent" + { + return Err(ServiceError::InvalidFileType); + } + + while let Some(chunk) = field.next().await { + let data = chunk.unwrap(); + torrent_cursor.write_all(&data)?; + } + } + _ => {} + } + } + + let fields = CreateTorrent { + title, + description, + category, + }; + + fields.verify()?; + + let position = torrent_cursor.position() as usize; + let inner = torrent_cursor.get_ref(); + + let torrent = match parse_torrent::decode_torrent(&inner[..position]) { + Ok(torrent) => Ok(torrent), + Err(_) => Err(ServiceError::InvalidTorrentFile), + }?; + + Ok(TorrentRequest { fields, torrent }) +} diff --git a/backend/src/handlers/user.rs b/backend/src/handlers/user.rs index f95b6e5c..f0b0c9f8 100644 --- a/backend/src/handlers/user.rs +++ b/backend/src/handlers/user.rs @@ -1,296 +1,509 @@ -use actix_web::{web, Responder, HttpResponse, HttpRequest}; -use serde::{Deserialize, Serialize}; -use pbkdf2::{ - password_hash::{ - rand_core::OsRng, - PasswordHash, PasswordHasher, PasswordVerifier, SaltString, - }, - Pbkdf2, -}; -use std::borrow::Cow; -use crate::errors::{ServiceResult, ServiceError}; -use crate::common::WebAppData; -use jsonwebtoken::{DecodingKey, decode, Validation, Algorithm}; -use crate::config::EmailOnSignup; -use crate::models::response::OkResponse; -use crate::models::response::TokenResponse; -use crate::mailer::VerifyClaims; -use crate::utils::random::random_string; - -pub fn init_routes(cfg: &mut web::ServiceConfig) { - cfg.service( - web::scope("/user") - .service(web::resource("/register") - .route(web::post().to(register))) - .service(web::resource("/login") - .route(web::post().to(login))) - .service(web::resource("/ban/{user}") - .route(web::delete().to(ban_user))) - .service(web::resource("/token/verify") - .route(web::post().to(verify_token))) - .service(web::resource("/token/renew") - .route(web::post().to(renew_token))) - .service(web::resource("/email/verify/{token}") - .route(web::get().to(verify_email))) - ); -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct Register { - pub username: String, - pub email: Option, - pub password: String, - pub confirm_password: String, -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct Login { - pub login: String, - pub password: String, -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct Token { - pub token: String, -} - -pub async fn register(req: HttpRequest, mut payload: web::Json, app_data: WebAppData) -> ServiceResult { - let settings = app_data.cfg.settings.read().await; - - match settings.auth.email_on_signup { - EmailOnSignup::Required => { - if payload.email.is_none() { return Err(ServiceError::EmailMissing) } - } - EmailOnSignup::None => { - payload.email = None - } - _ => {} - } - - if payload.password != payload.confirm_password { - return Err(ServiceError::PasswordsDontMatch); - } - - let password_length = payload.password.len(); - if password_length <= settings.auth.min_password_length { - return Err(ServiceError::PasswordTooShort); - } - if password_length >= settings.auth.max_password_length { - return Err(ServiceError::PasswordTooLong); - } - - let salt = SaltString::generate(&mut OsRng); - let password_hash; - if let Ok(password) = Pbkdf2.hash_password(payload.password.as_bytes(), &salt) { - password_hash = password.to_string(); - } else { - return Err(ServiceError::InternalServerError); - } - - if payload.username.contains('@') { - return Err(ServiceError::UsernameInvalid) - } - - // can't drop not null constraint on sqlite, so we fill the email with unique junk :) - let email = payload.email.as_ref().unwrap_or(&format!("EMPTY_EMAIL_{}", random_string(16))).to_string(); - - let res = sqlx::query!( - "INSERT INTO torrust_users (username, email, password) VALUES ($1, $2, $3)", - payload.username, - email, - password_hash, - ) - .execute(&app_data.database.pool) - .await; - - if let Err(sqlx::Error::Database(err)) = res { - return if err.code() == Some(Cow::from("2067")) { - if err.message().contains("torrust_users.username") { - Err(ServiceError::UsernameTaken) - } else if err.message().contains("torrust_users.email") { - Err(ServiceError::EmailTaken) - } else { - Err(ServiceError::InternalServerError) - } - } else { - Err(sqlx::Error::Database(err).into()) - }; - } - - // count accounts - let res_count: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM torrust_users") - .fetch_one(&app_data.database.pool) - .await?; - - // make admin if first account - if res_count.0 == 1 { - let _res_make_admin = sqlx::query!("UPDATE torrust_users SET administrator = 1") - .execute(&app_data.database.pool) - .await; - } - - let conn_info = req.connection_info(); - - if settings.mail.email_verification_enabled && payload.email.is_some() { - let mail_res = app_data.mailer.send_verification_mail( - &payload.email.as_ref().unwrap(), - &payload.username, - format!("{}://{}", conn_info.scheme(), conn_info.host()).as_str() - ) - .await; - - // get user id from user insert res - let user_id = res.unwrap().last_insert_rowid(); - - if mail_res.is_err() { - let _ = app_data.database.delete_user(user_id).await; - return Err(ServiceError::FailedToSendVerificationEmail) - } - } else { - - } - - Ok(HttpResponse::Ok()) -} - -pub async fn login(payload: web::Json, app_data: WebAppData) -> ServiceResult { - let settings = app_data.cfg.settings.read().await; - - let res = app_data.database.get_user_with_username(&payload.login).await; - - match res { - Some(user) => { - if settings.mail.email_verification_enabled && !user.email_verified { - return Err(ServiceError::EmailNotVerified) - } - - drop(settings); - - let parsed_hash = PasswordHash::new(&user.password)?; - - if !Pbkdf2.verify_password(payload.password.as_bytes(), &parsed_hash).is_ok() { - return Err(ServiceError::WrongPasswordOrUsername); - } - - let username = user.username.clone(); - let token = app_data.auth.sign_jwt(user.clone()).await; - - - Ok(HttpResponse::Ok().json(OkResponse { - data: TokenResponse { - token, - username, - admin: user.administrator - } - })) - } - None => Err(ServiceError::WrongPasswordOrUsername) - } -} -pub async fn verify_token(payload: web::Json, app_data: WebAppData) -> ServiceResult { - // verify if token is valid - let _claims = app_data.auth.verify_jwt(&payload.token).await?; - - Ok(HttpResponse::Ok().json(OkResponse { - data: format!("Token is valid.") - })) -} - -pub async fn renew_token(payload: web::Json, app_data: WebAppData) -> ServiceResult { - // verify if token is valid - let claims = app_data.auth.verify_jwt(&payload.token).await?; - - let user_compact = app_data.database.get_user_compact_from_id(claims.user.user_id).await?; - - const ONE_WEEK_IN_SECONDS: u64 = 604_800; - - // renew token if it is valid for less than one week - let token = match claims.exp - current_time() { - x if x < ONE_WEEK_IN_SECONDS => app_data.auth.sign_jwt(user_compact.clone()).await, - _ => payload.token.clone() - }; - - Ok(HttpResponse::Ok().json(OkResponse { - data: TokenResponse { - token, - username: user_compact.username, - admin: user_compact.administrator - } - })) -} - -pub async fn verify_email(req: HttpRequest, app_data: WebAppData) -> String { - let settings = app_data.cfg.settings.read().await; - let token = req.match_info().get("token").unwrap(); - - let token_data = match decode::( - token, - &DecodingKey::from_secret(settings.auth.secret_key.as_bytes()), - &Validation::new(Algorithm::HS256), - ) { - Ok(token_data) => { - if !token_data.claims.iss.eq("email-verification") { - return ServiceError::TokenInvalid.to_string() - } - - token_data.claims - }, - Err(_) => return ServiceError::TokenInvalid.to_string() - }; - - drop(settings); - - let res = sqlx::query!( - "UPDATE torrust_users SET email_verified = TRUE WHERE username = ?", - token_data.sub - ) - .execute(&app_data.database.pool) - .await; - - if let Err(_) = res { - return ServiceError::InternalServerError.to_string() - } - - String::from("Email verified, you can close this page.") -} - -pub async fn ban_user(req: HttpRequest, app_data: WebAppData) -> ServiceResult { - let user = app_data.auth.get_user_from_request(&req).await?; - - // check if user is administrator - if !user.administrator { return Err(ServiceError::Unauthorized) } - - let to_be_banned_username = req.match_info().get("user").unwrap(); - - let res = sqlx::query!( - "DELETE FROM torrust_users WHERE username = ? AND administrator = 0", - to_be_banned_username - ) - .execute(&app_data.database.pool) - .await; - - if let Err(_) = res { return Err(ServiceError::UsernameNotFound) } - if res.unwrap().rows_affected() == 0 { return Err(ServiceError::UsernameNotFound) } - - Ok(HttpResponse::Ok().json(OkResponse { - data: format!("Banned user: {}", to_be_banned_username) - })) -} - -pub async fn me(req: HttpRequest, app_data: WebAppData) -> ServiceResult { - let user = match app_data.auth.get_user_from_request(&req).await { - Ok(user) => Ok(user), - Err(e) => Err(e) - }?; - - let username = user.username.clone(); - let token = app_data.auth.sign_jwt(user.clone()).await; - - Ok(HttpResponse::Ok().json(OkResponse { - data: TokenResponse { - token, - username, - admin: user.administrator - } - })) -} +use crate::common::WebAppData; +use crate::config::Configuration; +use crate::config::EmailOnSignup; +use crate::database::SqliteDatabase; +use crate::mailer; +use crate::mailer::VerifyClaims; +use crate::models::response::OkResponse; +use crate::models::response::TokenResponse; +use crate::models::user::UserCompact; +use crate::models::user::UserId; +use crate::utils::random::random_string; +use crate::{ + errors::{ServiceError, ServiceResult}, + utils::time::current_time, +}; +use actix_web::{web, HttpRequest, HttpResponse, Responder}; +use async_trait::async_trait; +use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation}; +use log::log; +use log::Level; +use pbkdf2::{ + password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString}, + Pbkdf2, +}; +use serde::{Deserialize, Serialize}; +use std::borrow::Cow; +use std::sync::Arc; + +pub fn init_routes(cfg: &mut web::ServiceConfig) { + cfg.service( + web::scope("/user") + .service(web::resource("/register").route(web::post().to(register))) + .service(web::resource("/login").route(web::post().to(login))) + .service(web::resource("/ban/{user}").route(web::delete().to(ban_user))) + .service(web::resource("/token/verify").route(web::post().to(verify_token))) + .service(web::resource("/token/renew").route(web::post().to(renew_token_handler))) + .service(web::resource("/email/verify/{token}").route(web::get().to(verify_email))), + ); +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Register { + pub username: String, + pub email: Option, + pub password: String, + pub confirm_password: String, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Login { + pub login: String, + pub password: String, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Token { + pub token: String, +} + +pub struct RegistrationService { + configuration: Arc, + #[allow(unused)] + mailer: Arc, + user_repository: Arc>, + // V3 feature + // user_profile_repository: Arc, +} + +impl RegistrationService { + #[must_use] + pub fn new( + configuration: Arc, + mailer: Arc, + user_repository: Arc>, + // user_profile_repository: Arc, + ) -> Self { + Self { + configuration, + mailer, + user_repository, + // user_profile_repository, + } + } + + pub async fn renew_token( + &self, + payload: web::Json, + app_data: &WebAppData, + ) -> Result<(String, UserCompact), ServiceError> { + // verify if token is valid + let claims = app_data.auth.verify_jwt(&payload.token).await; + + // If claims are valid, we will return the original token with the compact user + // Else we retrieve the JWT if the grace period has not expired + let claims = match claims { + Ok(claims) => claims, + Err(_) => app_data.auth.decode_jwt(&payload.token).await?, + }; + + let user_compact = self + .user_repository + .get_compact(&claims.user.user_id) + .await + .map_err(|_| ServiceError::UsernameNotFound)?; + + let settings = self.configuration.settings.read().await; + + // renew token if it is invalid for less than grace period + let time_checked = current_time(); + let grace_time = settings.auth.renewal_grace_time; + drop(settings); + let token: String; + if claims.exp + grace_time < time_checked { + Err(ServiceError::TokenExpired) + } else { + if claims.exp < time_checked { + token = app_data.auth.sign_jwt(user_compact.clone()).await + } else { + token = payload.token.clone() + } + Ok((token, user_compact)) + } + } +} + +pub async fn register( + req: HttpRequest, + mut payload: web::Json, + app_data: WebAppData, +) -> ServiceResult { + let settings = app_data.cfg.settings.read().await; + + match settings.auth.email_on_signup { + EmailOnSignup::Required => { + if payload.email.is_none() { + return Err(ServiceError::EmailMissing); + } + } + EmailOnSignup::None => payload.email = None, + _ => {} + } + + if payload.password != payload.confirm_password { + return Err(ServiceError::PasswordsDontMatch); + } + + let password_length = payload.password.len(); + if password_length <= settings.auth.min_password_length { + return Err(ServiceError::PasswordTooShort); + } + if password_length >= settings.auth.max_password_length { + return Err(ServiceError::PasswordTooLong); + } + + let salt = SaltString::generate(&mut OsRng); + let password_hash; + if let Ok(password) = Pbkdf2.hash_password(payload.password.as_bytes(), &salt) { + password_hash = password.to_string(); + } else { + return Err(ServiceError::InternalServerError); + } + + if payload.username.contains('@') { + return Err(ServiceError::UsernameInvalid); + } + + // can't drop not null constraint on sqlite, so we fill the email with unique junk :) + let email = payload + .email + .as_ref() + .unwrap_or(&format!("EMPTY_EMAIL_{}", random_string(16))) + .to_string(); + + let res = sqlx::query!( + "INSERT INTO torrust_users (username, email, password) VALUES ($1, $2, $3)", + payload.username, + email, + password_hash, + ) + .execute(&app_data.database.pool) + .await; + + if let Err(sqlx::Error::Database(err)) = res { + return if err.code() == Some(Cow::from("2067")) { + if err.message().contains("torrust_users.username") { + Err(ServiceError::UsernameTaken) + } else if err.message().contains("torrust_users.email") { + Err(ServiceError::EmailTaken) + } else { + Err(ServiceError::InternalServerError) + } + } else { + Err(sqlx::Error::Database(err).into()) + }; + } + + // count accounts + let res_count: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM torrust_users") + .fetch_one(&app_data.database.pool) + .await?; + + // make admin if first account + if res_count.0 == 1 { + let _res_make_admin = sqlx::query!("UPDATE torrust_users SET administrator = 1") + .execute(&app_data.database.pool) + .await; + } + + let conn_info = req.connection_info(); + + if settings.mail.email_verification_enabled && payload.email.is_some() { + let mail_res = app_data + .mailer + .send_verification_mail( + &payload.email.as_ref().unwrap(), + &payload.username, + format!("{}://{}", conn_info.scheme(), conn_info.host()).as_str(), + ) + .await; + + // get user id from user insert res + let user_id = res.unwrap().last_insert_rowid(); + + if mail_res.is_err() { + let _ = app_data.database.delete_user(user_id).await; + return Err(ServiceError::FailedToSendVerificationEmail); + } + } else { + } + + Ok(HttpResponse::Ok()) +} + +pub async fn login( + payload: web::Json, + app_data: WebAppData, +) -> ServiceResult { + let settings = app_data.cfg.settings.read().await; + + let res = app_data + .database + .get_user_with_username(&payload.login) + .await; + + match res { + Some(user) => { + if settings.mail.email_verification_enabled && !user.email_verified { + return Err(ServiceError::EmailNotVerified); + } + log!(Level::Debug, "User email ok."); + drop(settings); + + let parsed_hash = PasswordHash::new(&user.password)?; + + if !Pbkdf2 + .verify_password(payload.password.as_bytes(), &parsed_hash) + .is_ok() + { + return Err(ServiceError::WrongPasswordOrUsername); + } + log!(Level::Debug, "Password ok."); + + let user_compact = app_data + .database + .get_user_compact_from_id(user.user_id) + .await + .map_err(|_| ServiceError::UsernameNotFound)?; + + log!(Level::Debug, "Compact user ok."); + + let token = app_data.auth.sign_jwt(user_compact.clone()).await; + + log!(Level::Debug, "Token ok."); + // let username = user_compact.username; + + Ok(HttpResponse::Ok().json(OkResponse { + data: TokenResponse { + token, + username: user_compact.username, + admin: user.administrator, + }, + })) + } + None => Err(ServiceError::WrongPasswordOrUsername), + } +} +pub async fn verify_token( + payload: web::Json, + app_data: WebAppData, +) -> ServiceResult { + // verify if token is valid + let _claims = app_data.auth.verify_jwt(&payload.token).await?; + + Ok(HttpResponse::Ok().json(OkResponse { + data: format!("Token is valid."), + })) +} + +/// It renews the JWT. +/// +/// # Errors +/// +/// It returns an error if: +/// +/// - Unable to parse the supplied payload as a valid JWT. +/// - The JWT is not invalid or expired. +#[allow(clippy::unused_async)] +pub async fn renew_token_handler( + payload: web::Json, + app_data: WebAppData, +) -> ServiceResult { + match app_data + .registration_service + .renew_token(payload, &app_data) + .await + { + Ok((token, user_compact)) => Ok(HttpResponse::Ok().json(OkResponse { + data: TokenResponse { + token, + username: user_compact.username, + admin: user_compact.administrator, + }, + })), + Err(error) => Err(error), + } +} + +pub async fn verify_email(req: HttpRequest, app_data: WebAppData) -> String { + let settings = app_data.cfg.settings.read().await; + let token = req.match_info().get("token").unwrap(); + + let token_data = match decode::( + token, + &DecodingKey::from_secret(settings.auth.secret_key.as_bytes()), + &Validation::new(Algorithm::HS256), + ) { + Ok(token_data) => { + if !token_data.claims.iss.eq("email-verification") { + return ServiceError::TokenInvalid.to_string(); + } + + token_data.claims + } + Err(_) => return ServiceError::TokenInvalid.to_string(), + }; + + drop(settings); + + let res = sqlx::query!( + "UPDATE torrust_users SET email_verified = TRUE WHERE username = ?", + token_data.sub + ) + .execute(&app_data.database.pool) + .await; + + if let Err(_) = res { + return ServiceError::InternalServerError.to_string(); + } + + String::from("Email verified, you can close this page.") +} + +pub async fn ban_user(req: HttpRequest, app_data: WebAppData) -> ServiceResult { + let user = app_data.auth.get_user_from_request(&req).await?; + + // check if user is administrator + if !user.administrator { + return Err(ServiceError::Unauthorized); + } + + let to_be_banned_username = req.match_info().get("user").unwrap(); + + let res = sqlx::query!( + "DELETE FROM torrust_users WHERE username = ? AND administrator = 0", + to_be_banned_username + ) + .execute(&app_data.database.pool) + .await; + + if let Err(_) = res { + return Err(ServiceError::UsernameNotFound); + } + if res.unwrap().rows_affected() == 0 { + return Err(ServiceError::UsernameNotFound); + } + + Ok(HttpResponse::Ok().json(OkResponse { + data: format!("Banned user: {}", to_be_banned_username), + })) +} + +pub async fn me(req: HttpRequest, app_data: WebAppData) -> ServiceResult { + let user = match app_data.auth.get_user_from_request(&req).await { + Ok(user) => Ok(user), + Err(e) => Err(e), + }?; + + let user_compact = app_data + .database + .get_user_compact_from_id(user.user_id) + .await + .map_err(|_| ServiceError::UsernameNotFound)?; + + let token = app_data.auth.sign_jwt(user_compact.clone()).await; + + Ok(HttpResponse::Ok().json(OkResponse { + data: TokenResponse { + token, + username: user_compact.username, + admin: user.administrator, + }, + })) +} + +// #[cfg_attr(test, automock)] +#[async_trait] +pub trait Repository: Sync + Send { + async fn get_compact(&self, user_id: &UserId) -> Result; + // async fn grant_admin_role(&self, user_id: &UserId) -> Result<(), Error>; + // async fn delete(&self, user_id: &UserId) -> Result<(), Error>; + // async fn add(&self, username: &str, email: &str, password_hash: &str) -> Result; +} + +pub struct DbUserRepository { + database: Arc, +} + +impl DbUserRepository { + #[must_use] + pub fn new(database: Arc) -> Self { + Self { database } + } +} + +#[async_trait] +impl Repository for DbUserRepository { + /// It returns the compact user. + /// + /// # Errors + /// + /// It returns an error if there is a database error. + async fn get_compact(&self, user_id: &UserId) -> Result { + // todo: persistence layer should have its own errors instead of + // returning a `ServiceError`. + self.database + .get_user_compact_from_id(*user_id) + .await + .map_err(|_| ServiceError::UsernameNotFound) + } + + // It grants the admin role to the user. + // + // # Errors + // + // It returns an error if there is a database error. + // async fn grant_admin_role(&self, user_id: &UserId) -> Result<(), Error> { + // self.database.grant_admin_role(*user_id).await + // } + + // It deletes the user. + // + // # Errors + // + // It returns an error if there is a database error. + // async fn delete(&self, user_id: &UserId) -> Result<(), Error> { + // self.database.delete_user(*user_id).await + // } + + // It adds a new user. + // + // # Errors + // + // It returns an error if there is a database error. + // async fn add(&self, username: &str, email: &str, password_hash: &str) -> Result { + // self.database + // .insert_user_and_get_id(username, email, password_hash) + // .await + // } +} + +#[cfg(test)] +mod tests { + use pbkdf2::{ + password_hash::{rand_core::OsRng, PasswordHasher, SaltString}, + Pbkdf2, + }; + + #[test] + fn password_hash() { + // dotenvy::dotenv().ok(); + // let connection_string = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set"); + + // let pool = SqlitePoolOptions::new() + // .connect(database_url) + // .await + // .expect("Unable to create database pool"); + + let salt = SaltString::generate(&mut OsRng); + let password_hash; + if let Ok(password) = Pbkdf2.hash_password("toto".as_bytes(), &salt) { + password_hash = password.to_string(); + } else { + password_hash = "".to_owned(); + assert!(false); + } + println!("{}", password_hash); + assert!(true); + } +} diff --git a/backend/src/mailer.rs b/backend/src/mailer.rs index 9e396bb2..ae119b16 100644 --- a/backend/src/mailer.rs +++ b/backend/src/mailer.rs @@ -1,144 +1,138 @@ -use crate::config::Configuration; -use std::sync::Arc; -use crate::errors::ServiceError; -use serde::{Serialize, Deserialize}; -use lettre::{AsyncSmtpTransport, Tokio1Executor, Message, AsyncTransport}; -use lettre::transport::smtp::authentication::{Credentials, Mechanism}; -use lettre::message::{MessageBuilder, MultiPart, SinglePart}; -use jsonwebtoken::{encode, Header, EncodingKey}; -use sailfish::TemplateOnce; -use crate::utils::time::current_time; - -pub struct MailerService { - cfg: Arc, - mailer: Arc -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct VerifyClaims { - pub iss: String, - pub sub: String, - pub exp: u64, -} - -#[derive(TemplateOnce)] -#[template(path = "../templates/verify.html")] -struct VerifyTemplate { - username: String, - verification_url: String, -} - - -impl MailerService { - pub async fn new(cfg: Arc) -> MailerService { - let mailer = Arc::new(Self::get_mailer(&cfg).await); - - Self { - cfg, - mailer, - } - } - - async fn get_mailer(cfg: &Configuration) -> Mailer { - let settings = cfg.settings.read().await; - - let creds = Credentials::new(settings.mail.username.to_owned(), settings.mail.password.to_owned()); - - AsyncSmtpTransport::::builder_dangerous(&settings.mail.server) - .port(settings.mail.port) - .credentials(creds) - .authentication(vec![ - Mechanism::Login, - Mechanism::Xoauth2, - Mechanism::Plain, - ]) - .build() - } - - pub async fn send_verification_mail(&self, to: &str, username: &str, base_url: &str) -> Result<(), ServiceError> { - let builder = self.get_builder(to).await; - let verification_url = self.get_verification_url(username, base_url).await; - - let mail_body = format!( - r#" -Welcome to Torrust, {}! - -Please click the confirmation link below to verify your account. -{} - -If this account wasn't made by you, you can ignore this email. - "#, - username, - verification_url - ); - - let ctx = VerifyTemplate { - username: String::from(username), - verification_url, - }; - - let mail = builder - .subject("Torrust - Email verification") - .multipart( - MultiPart::alternative() - .singlepart( - SinglePart::builder() - .header(lettre::message::header::ContentType::TEXT_PLAIN) - .body(mail_body) - ) - .singlepart( - SinglePart::builder() - .header(lettre::message::header::ContentType::TEXT_HTML) - .body(ctx.render_once().unwrap()) - ) - ) - .unwrap(); - - match self.mailer.send(mail).await { - Ok(_res) => Ok(()), - Err(e) => { - eprintln!("Failed to send email: {}", e); - Err(ServiceError::FailedToSendVerificationEmail) - }, - } - } - - async fn get_builder(&self, to: &str) -> MessageBuilder { - let settings = self.cfg.settings.read().await; - - Message::builder() - .from(settings.mail.from.parse().unwrap()) - .reply_to(settings.mail.reply_to.parse().unwrap()) - .to(to.parse().unwrap()) - } - - async fn get_verification_url(&self, username: &str, base_url: &str) -> String { - let settings = self.cfg.settings.read().await; - - // create verification JWT - let key = settings.auth.secret_key.as_bytes(); - - // Create non expiring token that is only valid for email-verification - let claims = VerifyClaims { - iss: String::from("email-verification"), - sub: String::from(username), - exp: current_time() + 315_569_260 // 10 years from now - }; - - let token = encode( - &Header::default(), - &claims, - &EncodingKey::from_secret(key), - ) - .unwrap(); - - let mut base_url = base_url.clone(); - if let Some(cfg_base_url) = &settings.net.base_url { - base_url = cfg_base_url; - } - - format!("{}/user/email/verify/{}", base_url, token) - } -} - -pub type Mailer = AsyncSmtpTransport; +use crate::config::Configuration; +use crate::errors::ServiceError; +use crate::utils::time::current_time; +use jsonwebtoken::{encode, EncodingKey, Header}; +use lettre::message::{MessageBuilder, MultiPart, SinglePart}; +use lettre::transport::smtp::authentication::{Credentials, Mechanism}; +use lettre::{AsyncSmtpTransport, AsyncTransport, Message, Tokio1Executor}; +use sailfish::TemplateOnce; +use serde::{Deserialize, Serialize}; +use std::sync::Arc; + +pub struct MailerService { + cfg: Arc, + mailer: Arc, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct VerifyClaims { + pub iss: String, + pub sub: String, + pub exp: u64, +} + +#[derive(TemplateOnce)] +#[template(path = "../templates/verify.html")] +struct VerifyTemplate { + username: String, + verification_url: String, +} + +impl MailerService { + pub async fn new(cfg: Arc) -> MailerService { + let mailer = Arc::new(Self::get_mailer(&cfg).await); + + Self { cfg, mailer } + } + + async fn get_mailer(cfg: &Configuration) -> Mailer { + let settings = cfg.settings.read().await; + + let creds = Credentials::new( + settings.mail.username.to_owned(), + settings.mail.password.to_owned(), + ); + + AsyncSmtpTransport::::builder_dangerous(&settings.mail.server) + .port(settings.mail.port) + .credentials(creds) + .authentication(vec![Mechanism::Login, Mechanism::Xoauth2, Mechanism::Plain]) + .build() + } + + pub async fn send_verification_mail( + &self, + to: &str, + username: &str, + base_url: &str, + ) -> Result<(), ServiceError> { + let builder = self.get_builder(to).await; + let verification_url = self.get_verification_url(username, base_url).await; + + let mail_body = format!( + r#" +Welcome to Torrust, {}! + +Please click the confirmation link below to verify your account. +{} + +If this account wasn't made by you, you can ignore this email. + "#, + username, verification_url + ); + + let ctx = VerifyTemplate { + username: String::from(username), + verification_url, + }; + + let mail = builder + .subject("Torrust - Email verification") + .multipart( + MultiPart::alternative() + .singlepart( + SinglePart::builder() + .header(lettre::message::header::ContentType::TEXT_PLAIN) + .body(mail_body), + ) + .singlepart( + SinglePart::builder() + .header(lettre::message::header::ContentType::TEXT_HTML) + .body(ctx.render_once().unwrap()), + ), + ) + .unwrap(); + + match self.mailer.send(mail).await { + Ok(_res) => Ok(()), + Err(e) => { + eprintln!("Failed to send email: {}", e); + Err(ServiceError::FailedToSendVerificationEmail) + } + } + } + + async fn get_builder(&self, to: &str) -> MessageBuilder { + let settings = self.cfg.settings.read().await; + + Message::builder() + .from(settings.mail.from.parse().unwrap()) + .reply_to(settings.mail.reply_to.parse().unwrap()) + .to(to.parse().unwrap()) + } + + async fn get_verification_url(&self, username: &str, base_url: &str) -> String { + let settings = self.cfg.settings.read().await; + + // create verification JWT + let key = settings.auth.secret_key.as_bytes(); + + // Create non expiring token that is only valid for email-verification + let claims = VerifyClaims { + iss: String::from("email-verification"), + sub: String::from(username), + exp: current_time() + 315_569_260, // 10 years from now + }; + + let token = encode(&Header::default(), &claims, &EncodingKey::from_secret(key)).unwrap(); + + let mut base_url = base_url; + if let Some(cfg_base_url) = &settings.net.base_url { + base_url = cfg_base_url; + } + + format!("{}/user/email/verify/{}", base_url, token) + } +} + +pub type Mailer = AsyncSmtpTransport; diff --git a/backend/src/main.rs b/backend/src/main.rs index d435969b..712616f6 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -1,77 +1,102 @@ -use std::sync::Arc; -use actix_web::{App, HttpServer, middleware, web}; -use actix_cors::Cors; -use torrust::database::Database; -use torrust::{handlers}; -use torrust::config::{Configuration}; -use torrust::common::AppData; -use torrust::auth::AuthorizationService; -use torrust::tracker::TrackerService; -use torrust::mailer::MailerService; - -#[actix_web::main] -async fn main() -> std::io::Result<()> { - let cfg = match Configuration::load_from_file().await { - Ok(config) => Arc::new(config), - Err(error) => { - panic!("{}", error) - } - }; - - let settings = cfg.settings.read().await; - - let database = Arc::new(Database::new(&settings.database.connect_url).await); - let auth = Arc::new(AuthorizationService::new(cfg.clone(), database.clone())); - let tracker_service = Arc::new(TrackerService::new(cfg.clone(), database.clone())); - let mailer_service = Arc::new(MailerService::new(cfg.clone()).await); - let app_data = Arc::new( - AppData::new( - cfg.clone(), - database.clone(), - auth.clone(), - tracker_service.clone(), - mailer_service.clone(), - ) - ); - - // create/update database tables - let _ = sqlx::migrate!().run(&database.pool).await; - - // create torrent upload folder - async_std::fs::create_dir_all(&settings.storage.upload_path).await?; - - let interval = settings.database.torrent_info_update_interval; - let weak_tracker_service = std::sync::Arc::downgrade(&tracker_service); - - // repeating task, update all seeders and leechers info - tokio::spawn(async move { - let interval = std::time::Duration::from_secs(interval); - let mut interval = tokio::time::interval(interval); - interval.tick().await; // first tick is immediate... - loop { - interval.tick().await; - if let Some(tracker) = weak_tracker_service.upgrade() { - let _ = tracker.update_torrents().await; - } else { - break; - } - } - }); - - let port = settings.net.port; - - drop(settings); - - println!("Listening on 0.0.0.0:{}", port); - - HttpServer::new(move || { - App::new() - .wrap(Cors::permissive()) - .app_data(web::Data::new(app_data.clone())) - .wrap(middleware::Logger::default()) - .configure(handlers::init_routes) - }) - .bind(("0.0.0.0", port))? - .run() - .await -} +use actix_contrib_logger::middleware::Logger; +use actix_cors::Cors; +use actix_web::{web, App, HttpServer}; +use log::Level; +use reqwest::StatusCode; +use std::sync::Arc; +use torrust::auth::AuthorizationService; +use torrust::common::AppData; +use torrust::config::Configuration; +use torrust::database::SqliteDatabase; +use torrust::handlers; +use torrust::handlers::user::{DbUserRepository, RegistrationService, Repository}; +use torrust::mailer::MailerService; +use torrust::tracker::TrackerService; + +#[actix_web::main] +async fn main() -> std::io::Result<()> { + let cfg = match Configuration::load_from_file().await { + Ok(config) => Arc::new(config), + Err(error) => { + panic!("{}", error) + } + }; + + let settings = cfg.settings.read().await; + + let database = Arc::new(SqliteDatabase::new(&settings.database.connect_url).await); + let auth = Arc::new(AuthorizationService::new(cfg.clone(), database.clone())); + let tracker_service = Arc::new(TrackerService::new(cfg.clone(), database.clone())); + let mailer_service = Arc::new(MailerService::new(cfg.clone()).await); + let user_repository: Arc> = + Arc::new(Box::new(DbUserRepository::new(database.clone()))); + + let registration_service = Arc::new(RegistrationService::new( + cfg.clone(), + mailer_service.clone(), + user_repository.clone(), + // user_profile_repository.clone(), + )); + + let app_data = Arc::new(AppData::new( + cfg.clone(), + database.clone(), + auth.clone(), + tracker_service.clone(), + mailer_service.clone(), + registration_service.clone(), + )); + + // create/update database tables + let _ = sqlx::migrate!().run(&database.pool).await; + + // create torrent upload folder + async_std::fs::create_dir_all(&settings.storage.upload_path).await?; + + let interval = settings.database.torrent_info_update_interval; + let weak_tracker_service = std::sync::Arc::downgrade(&tracker_service); + + // repeating task, update all seeders and leechers info + tokio::spawn(async move { + let interval = std::time::Duration::from_secs(interval); + let mut interval = tokio::time::interval(interval); + interval.tick().await; // first tick is immediate... + loop { + interval.tick().await; + if let Some(tracker) = weak_tracker_service.upgrade() { + let _ = tracker.update_torrents().await; + } else { + break; + } + } + }); + + let port = settings.net.port; + + drop(settings); + + env_logger::init_from_env(env_logger::Env::new().default_filter_or("error")); + + println!("Listening on 0.0.0.0:{}", port); + + HttpServer::new(move || { + let logger = Logger::default().custom_level(|status| { + if status.is_server_error() { + Level::Error + } else if status == StatusCode::NOT_FOUND { + Level::Warn + } else { + Level::Info + } + }); + + App::new() + .wrap(Cors::permissive()) + .app_data(web::Data::new(app_data.clone())) + .wrap(logger) + .configure(handlers::init_routes) + }) + .bind(("0.0.0.0", port))? + .run() + .await +} diff --git a/backend/src/models/user.rs b/backend/src/models/user.rs index 403a54f3..a0960296 100644 --- a/backend/src/models/user.rs +++ b/backend/src/models/user.rs @@ -1,18 +1,36 @@ -use serde::{Serialize, Deserialize}; - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct User { - pub user_id: i64, - pub username: String, - pub email: String, - pub email_verified: bool, - pub password: String, - pub administrator: bool, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct Claims { - pub sub: String, // username - pub admin: bool, - pub exp: u64, // epoch in seconds -} +use serde::{Deserialize, Serialize}; + +#[allow(clippy::module_name_repetitions)] +pub type UserId = i64; + +#[derive(Debug, Serialize, Deserialize, Clone, sqlx::FromRow)] +pub struct User { + pub user_id: UserId, + pub username: String, + pub email: String, + pub email_verified: bool, + pub password: String, + pub administrator: bool, +} + +// #[derive(Debug, Serialize, Deserialize, Clone)] +// pub struct Claims { +// pub sub: String, // username +// pub admin: bool, +// pub exp: u64, // epoch in seconds +// } + +#[allow(clippy::module_name_repetitions)] +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct UserClaims { + pub user: UserCompact, + pub exp: u64, // epoch in seconds +} + +#[allow(clippy::module_name_repetitions)] +#[derive(Debug, Serialize, Deserialize, Clone, sqlx::FromRow)] +pub struct UserCompact { + pub user_id: UserId, + pub username: String, + pub administrator: bool, +} diff --git a/backend/src/tracker.rs b/backend/src/tracker.rs index c2ac0640..53e29aa2 100644 --- a/backend/src/tracker.rs +++ b/backend/src/tracker.rs @@ -1,156 +1,158 @@ -use crate::config::Configuration; -use std::sync::Arc; -use crate::database::Database; -use crate::models::tracker_key::TrackerKey; -use crate::errors::ServiceError; -use crate::models::user::User; -use serde::{Serialize, Deserialize}; - -#[derive(Debug, Serialize, Deserialize)] -pub struct TorrentInfo { - pub info_hash: String, - pub seeders: i64, - pub completed: i64, - pub leechers: i64, - pub peers: Vec, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Peer { - pub peer_id: Option, - pub peer_addr: Option, - pub updated: Option, - pub uploaded: Option, - pub downloaded: Option, - pub left: Option, - pub event: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct PeerId { - pub id: Option, - pub client: Option -} - -pub struct TrackerService { - cfg: Arc, - database: Arc, -} - -impl TrackerService { - pub fn new(cfg: Arc, database: Arc) -> TrackerService { - TrackerService { - cfg, - database - } - } - - pub async fn whitelist_info_hash(&self, info_hash: String) -> Result<(), ServiceError> { - let settings = self.cfg.settings.read().await; - - let request_url = - format!("{}/api/whitelist/{}?token={}", settings.tracker.api_url, info_hash, settings.tracker.token); - - drop(settings); - - let client = reqwest::Client::new(); - - let response = match client.post(request_url).send().await { - Ok(v) => Ok(v), - Err(_) => Err(ServiceError::InternalServerError) - }?; - - if response.status().is_success() { - return Ok(()) - } - - Err(ServiceError::InternalServerError) - } - - pub async fn get_personal_announce_url(&self, user: &User) -> Result { - let settings = self.cfg.settings.read().await; - - let tracker_key = self.database.get_valid_tracker_key(user.user_id).await; - - match tracker_key { - Some(v) => { Ok(format!("{}/{}", settings.tracker.url, v.key)) } - None => { - match self.retrieve_new_tracker_key(user.user_id).await { - Ok(v) => { Ok(format!("{}/{}", settings.tracker.url, v.key)) }, - Err(_) => { Err(ServiceError::TrackerOffline) } - } - } - } - } - - pub async fn retrieve_new_tracker_key(&self, user_id: i64) -> Result { - let settings = self.cfg.settings.read().await; - - let request_url = - format!("{}/api/key/{}?token={}", settings.tracker.api_url, settings.tracker.token_valid_seconds, settings.tracker.token); - - drop(settings); - - let client = reqwest::Client::new(); - let response = match client.post(request_url) - .send() - .await { - Ok(v) => Ok(v), - Err(_) => Err(ServiceError::InternalServerError) - }?; - - let tracker_key: TrackerKey = match response.json::().await { - Ok(v) => Ok(v), - Err(_) => Err(ServiceError::InternalServerError) - }?; - - println!("{:?}", tracker_key); - - self.database.issue_tracker_key(&tracker_key, user_id).await?; - - Ok(tracker_key) - } - - // get torrent info from tracker api - pub async fn get_torrent_info(&self, info_hash: &str) -> Result { - let settings = self.cfg.settings.read().await; - - let request_url = - format!("{}/api/torrent/{}?token={}", settings.tracker.api_url, info_hash, settings.tracker.token); - - drop(settings); - - let client = reqwest::Client::new(); - let response = match client.get(request_url) - .send() - .await { - Ok(v) => Ok(v), - Err(_) => Err(ServiceError::InternalServerError) - }?; - - let torrent_info = match response.json::().await { - Ok(torrent_info) => { - let _ = self.database.update_tracker_info(info_hash, torrent_info.seeders, torrent_info.leechers).await; - Ok(torrent_info) - }, - Err(e) => { - eprintln!("{:?}", e); - let _ = self.database.update_tracker_info(info_hash, 0, 0).await; - Err(ServiceError::TorrentNotFound) - } - }?; - - Ok(torrent_info) - } - - pub async fn update_torrents(&self) -> Result<(), ()> { - println!("Updating torrents.."); - let torrents = self.database.get_all_torrent_ids().await?; - - for torrent in torrents { - let _ = self.get_torrent_info(&torrent.info_hash).await; - } - - Ok(()) - } -} +use crate::config::Configuration; +use crate::database::SqliteDatabase; +use crate::errors::ServiceError; +use crate::models::tracker_key::TrackerKey; +use crate::models::user::User; +use serde::{Deserialize, Serialize}; +use std::sync::Arc; + +#[derive(Debug, Serialize, Deserialize)] +pub struct TorrentInfo { + pub info_hash: String, + pub seeders: i64, + pub completed: i64, + pub leechers: i64, + pub peers: Vec, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Peer { + pub peer_id: Option, + pub peer_addr: Option, + pub updated: Option, + pub uploaded: Option, + pub downloaded: Option, + pub left: Option, + pub event: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct PeerId { + pub id: Option, + pub client: Option, +} + +pub struct TrackerService { + cfg: Arc, + database: Arc, +} + +impl TrackerService { + pub fn new(cfg: Arc, database: Arc) -> TrackerService { + TrackerService { cfg, database } + } + + pub async fn whitelist_info_hash(&self, info_hash: String) -> Result<(), ServiceError> { + let settings = self.cfg.settings.read().await; + + let request_url = format!( + "{}/api/whitelist/{}?token={}", + settings.tracker.api_url, info_hash, settings.tracker.token + ); + + drop(settings); + + let client = reqwest::Client::new(); + + let response = match client.post(request_url).send().await { + Ok(v) => Ok(v), + Err(_) => Err(ServiceError::InternalServerError), + }?; + + if response.status().is_success() { + return Ok(()); + } + + Err(ServiceError::InternalServerError) + } + + pub async fn get_personal_announce_url(&self, user: &User) -> Result { + let settings = self.cfg.settings.read().await; + + let tracker_key = self.database.get_valid_tracker_key(user.user_id).await; + + match tracker_key { + Some(v) => Ok(format!("{}/{}", settings.tracker.url, v.key)), + None => match self.retrieve_new_tracker_key(user.user_id).await { + Ok(v) => Ok(format!("{}/{}", settings.tracker.url, v.key)), + Err(_) => Err(ServiceError::TrackerOffline), + }, + } + } + + pub async fn retrieve_new_tracker_key(&self, user_id: i64) -> Result { + let settings = self.cfg.settings.read().await; + + let request_url = format!( + "{}/api/key/{}?token={}", + settings.tracker.api_url, settings.tracker.token_valid_seconds, settings.tracker.token + ); + + drop(settings); + + let client = reqwest::Client::new(); + let response = match client.post(request_url).send().await { + Ok(v) => Ok(v), + Err(_) => Err(ServiceError::InternalServerError), + }?; + + let tracker_key: TrackerKey = match response.json::().await { + Ok(v) => Ok(v), + Err(_) => Err(ServiceError::InternalServerError), + }?; + + println!("{:?}", tracker_key); + + self.database + .issue_tracker_key(&tracker_key, user_id) + .await?; + + Ok(tracker_key) + } + + // get torrent info from tracker api + pub async fn get_torrent_info(&self, info_hash: &str) -> Result { + let settings = self.cfg.settings.read().await; + + let request_url = format!( + "{}/api/torrent/{}?token={}", + settings.tracker.api_url, info_hash, settings.tracker.token + ); + + drop(settings); + + let client = reqwest::Client::new(); + let response = match client.get(request_url).send().await { + Ok(v) => Ok(v), + Err(_) => Err(ServiceError::InternalServerError), + }?; + + let torrent_info = match response.json::().await { + Ok(torrent_info) => { + let _ = self + .database + .update_tracker_info(info_hash, torrent_info.seeders, torrent_info.leechers) + .await; + Ok(torrent_info) + } + Err(e) => { + eprintln!("{:?}", e); + let _ = self.database.update_tracker_info(info_hash, 0, 0).await; + Err(ServiceError::TorrentNotFound) + } + }?; + + Ok(torrent_info) + } + + pub async fn update_torrents(&self) -> Result<(), ()> { + println!("Updating torrents.."); + let torrents = self.database.get_all_torrent_ids().await?; + + for torrent in torrents { + let _ = self.get_torrent_info(&torrent.info_hash).await; + } + + Ok(()) + } +} diff --git a/frontend/src/components/navigation/Sidebar.vue b/frontend/src/components/navigation/Sidebar.vue index 12623576..fe613689 100644 --- a/frontend/src/components/navigation/Sidebar.vue +++ b/frontend/src/components/navigation/Sidebar.vue @@ -11,7 +11,7 @@
-
-
+
-