From 4b52c37774b52d443c03bb513e12b2b4036bd543 Mon Sep 17 00:00:00 2001 From: TotallyNot <44345987+TotallyNot@users.noreply.github.com> Date: Fri, 25 Apr 2025 17:21:50 +0200 Subject: [PATCH] feat(key-pool): updated key pool to use v2 api --- Cargo.lock | 1267 ++++++++++++++++++++++++++++++++- Cargo.toml | 2 +- torn-api-codegen/openapi.json | 75 +- torn-key-pool/Cargo.toml | 52 +- torn-key-pool/src/lib.rs | 386 +++++++--- torn-key-pool/src/local.rs | 206 ------ torn-key-pool/src/postgres.rs | 230 +++--- torn-key-pool/src/send.rs | 380 ---------- 8 files changed, 1728 insertions(+), 870 deletions(-) delete mode 100644 torn-key-pool/src/local.rs delete mode 100644 torn-key-pool/src/send.rs diff --git a/Cargo.lock b/Cargo.lock index dd80983..6948ecf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -32,6 +32,27 @@ 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 = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "async-compression" version = "0.4.22" @@ -45,6 +66,21 @@ dependencies = [ "tokio", ] +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.4.0" @@ -72,11 +108,29 @@ 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 = "bitflags" version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +dependencies = [ + "serde", +] + +[[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 = "bon" @@ -130,6 +184,12 @@ version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.10.1" @@ -157,6 +217,100 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chrono" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + +[[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 = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +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 = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "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.20.11" @@ -192,6 +346,29 @@ dependencies = [ "syn", ] +[[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 = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -203,18 +380,112 @@ dependencies = [ "syn", ] +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +dependencies = [ + "serde", +] + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + [[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 = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[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", +] + [[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.2.1" @@ -224,6 +495,21 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.31" @@ -231,6 +517,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -239,6 +526,45 @@ 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", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[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", +] + [[package]] name = "futures-sink" version = "0.3.31" @@ -257,10 +583,26 @@ 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.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", ] [[package]] @@ -296,11 +638,44 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "h2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +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", +] [[package]] name = "heck" @@ -308,6 +683,39 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[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", +] + +[[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 = "http" version = "1.3.1" @@ -357,6 +765,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", + "h2", "http", "http-body", "httparse", @@ -385,6 +794,22 @@ dependencies = [ "webpki-roots", ] +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.11" @@ -405,6 +830,30 @@ dependencies = [ "tracing", ] +[[package]] +name = "iana-time-zone" +version = "0.1.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "icu_collections" version = "1.5.0" @@ -561,6 +1010,12 @@ dependencies = [ "serde", ] +[[package]] +name = "indoc" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" + [[package]] name = "ipnet" version = "2.11.0" @@ -583,12 +1038,43 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + [[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.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9627da5196e5d8ed0b0495e61e518847578da83483c37288316d9b2e03a7f72" + +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + [[package]] name = "litemap" version = "0.7.5" @@ -611,6 +1097,16 @@ version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + [[package]] name = "memchr" version = "2.7.4" @@ -643,6 +1139,70 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[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", + "rand 0.8.5", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + [[package]] name = "object" version = "0.36.7" @@ -658,6 +1218,56 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "openssl" +version = "0.10.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.3" @@ -681,6 +1291,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[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.3.1" @@ -699,6 +1318,33 @@ 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 = "ppv-lite86" version = "0.2.21" @@ -755,7 +1401,7 @@ checksum = "b820744eb4dc9b57a3398183639c511b5a26d2ed702cedd3febaa1393caa22cc" dependencies = [ "bytes", "getrandom 0.3.2", - "rand", + "rand 0.9.1", "ring", "rustc-hash", "rustls", @@ -796,14 +1442,35 @@ version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +[[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", - "rand_core", + "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]] @@ -813,7 +1480,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core", + "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.15", ] [[package]] @@ -843,18 +1519,22 @@ dependencies = [ "async-compression", "base64", "bytes", + "encoding_rs", "futures-core", "futures-util", + "h2", "http", "http-body", "http-body-util", "hyper", "hyper-rustls", + "hyper-tls", "hyper-util", "ipnet", "js-sys", "log", "mime", + "native-tls", "once_cell", "percent-encoding", "pin-project-lite", @@ -866,7 +1546,9 @@ dependencies = [ "serde_json", "serde_urlencoded", "sync_wrapper", + "system-configuration", "tokio", + "tokio-native-tls", "tokio-rustls", "tokio-util", "tower", @@ -893,6 +1575,26 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rsa" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "signature", + "spki", + "subtle", + "zeroize", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -905,6 +1607,19 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +[[package]] +name = "rustix" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + [[package]] name = "rustls" version = "0.23.26" @@ -960,12 +1675,44 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.219" @@ -1021,6 +1768,28 @@ dependencies = [ "serde", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "shlex" version = "1.3.0" @@ -1036,6 +1805,16 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + [[package]] name = "slab" version = "0.4.9" @@ -1050,6 +1829,9 @@ name = "smallvec" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +dependencies = [ + "serde", +] [[package]] name = "socket2" @@ -1061,12 +1843,237 @@ dependencies = [ "windows-sys 0.52.0", ] +[[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", + "bytes", + "chrono", + "crc", + "crossbeam-queue", + "either", + "event-listener", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashbrown", + "hashlink", + "indexmap", + "log", + "memchr", + "once_cell", + "percent-encoding", + "rustls", + "serde", + "serde_json", + "sha2", + "smallvec", + "thiserror", + "tokio", + "tokio-stream", + "tracing", + "url", + "webpki-roots", +] + +[[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", +] + +[[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", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", + "syn", + "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", + "bitflags", + "byteorder", + "bytes", + "chrono", + "crc", + "digest", + "dotenvy", + "either", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "generic-array", + "hex", + "hkdf", + "hmac", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "percent-encoding", + "rand 0.8.5", + "rsa", + "serde", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "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", + "bitflags", + "byteorder", + "chrono", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "rand 0.8.5", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c26083e9a520e8eb87a06b12347679b142dc2ea29e6e409f805644a7a979a5bc" +dependencies = [ + "atoi", + "chrono", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "serde_urlencoded", + "sqlx-core", + "thiserror", + "tracing", + "url", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] + [[package]] name = "strsim" version = "0.11.1" @@ -1110,6 +2117,40 @@ dependencies = [ "syn", ] +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +dependencies = [ + "fastrand", + "getrandom 0.3.2", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "thiserror" version = "2.0.12" @@ -1184,6 +2225,16 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.26.2" @@ -1194,6 +2245,17 @@ dependencies = [ "tokio", ] +[[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.14" @@ -1239,6 +2301,23 @@ dependencies = [ "syn", ] +[[package]] +name = "torn-key-pool" +version = "1.0.0" +dependencies = [ + "chrono", + "futures", + "indoc", + "rand 0.9.1", + "reqwest", + "serde", + "serde_json", + "sqlx", + "thiserror", + "tokio", + "torn-api", +] + [[package]] name = "tower" version = "0.5.2" @@ -1272,10 +2351,23 @@ 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", +] + [[package]] name = "tracing-core" version = "0.1.33" @@ -1291,12 +2383,39 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[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.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + [[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.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-properties" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" + [[package]] name = "untrusted" version = "0.9.0" @@ -1326,6 +2445,18 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[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 = "want" version = "0.3.1" @@ -1350,6 +2481,12 @@ 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.100" @@ -1450,6 +2587,51 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "whoami" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6994d13118ab492c3c80c1f81928718159254c53c472bf9ce36f8dae4add02a7" +dependencies = [ + "redox_syscall", + "wasite", +] + +[[package]] +name = "windows-core" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings 0.4.0", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-link" version = "0.1.1" @@ -1463,7 +2645,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" dependencies = [ "windows-result", - "windows-strings", + "windows-strings 0.3.1", "windows-targets 0.53.0", ] @@ -1485,6 +2667,24 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-strings" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +dependencies = [ + "windows-link", +] + +[[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" @@ -1503,6 +2703,21 @@ 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" @@ -1535,6 +2750,12 @@ dependencies = [ "windows_x86_64_msvc 0.53.0", ] +[[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" @@ -1547,6 +2768,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +[[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" @@ -1559,6 +2786,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +[[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" @@ -1583,6 +2816,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +[[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" @@ -1595,6 +2834,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +[[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" @@ -1607,6 +2852,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +[[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" @@ -1619,6 +2870,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +[[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" diff --git a/Cargo.toml b/Cargo.toml index 98c5d14..bd0a83c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = ["torn-api", "torn-api-codegen"] +members = ["torn-api", "torn-api-codegen", "torn-key-pool"] [workspace.package] license-file = "./LICENSE" diff --git a/torn-api-codegen/openapi.json b/torn-api-codegen/openapi.json index 9336cd2..15c72f6 100644 --- a/torn-api-codegen/openapi.json +++ b/torn-api-codegen/openapi.json @@ -3,7 +3,7 @@ "info": { "title": "Torn API", "description": "\n * The development of Torn's API v2 is still ongoing.\n * If selections remain unaltered, they will default to the API v1 version.\n * Unlike API v1, API v2 accepts both selections and IDs as path and query parameters.\n * If any discrepancies or errors are found, please submit a [bug report](https://www.torn.com/forums.php#/p=forums&f=19&b=0&a=0) on the Torn Forums.\n * In case you're using bots to check for changes on openapi.json file, make sure to specificy a custom user-agent header - CloudFlare sometimes prevents requests from default user-agents.", - "version": "1.3.1" + "version": "1.3.2" }, "servers": [ { @@ -8773,7 +8773,8 @@ "attacksdamaging", "attacksrunaway", "highestterritories", - "territoryrespect" + "territoryrespect", + "membersamount" ] }, "FactionBranchStateEnum": { @@ -13864,25 +13865,30 @@ "type": "object" }, "RacingRaceDetailsResponse": { - "allOf": [ - { - "required": [ - "results" - ], - "properties": { - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RacerDetails" - } + "properties": { + "race": { + "allOf": [ + { + "required": [ + "results" + ], + "properties": { + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RacerDetails" + } + } + }, + "type": "object" + }, + { + "$ref": "#/components/schemas/Race" } - }, - "type": "object" - }, - { - "$ref": "#/components/schemas/Race" + ] } - ] + }, + "type": "object" }, "RacingSelectionName": { "type": "string", @@ -15352,7 +15358,14 @@ "searchforcash", "shoplifting", "stats", - "stocks" + "stocks", + "chainreport", + "rackets", + "rankedwarreport", + "rankedwars", + "territorynames", + "territorywarreport", + "territorywars" ] }, "TornLookupResponse": { @@ -19062,10 +19075,7 @@ "format": "int64" }, "rewards": { - "type": "array", - "items": { - "$ref": "#/components/schemas/UserCrimeUniquesReward" - } + "$ref": "#/components/schemas/UserCrimeUniquesReward" } }, "type": "object" @@ -19139,6 +19149,9 @@ }, { "$ref": "#/components/schemas/UserCrimeDetailsScamming" + }, + { + "type": "null" } ] } @@ -19181,7 +19194,14 @@ "$ref": "#/components/schemas/RaceCarId" }, "name": { - "type": "string" + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] }, "worth": { "type": "integer", @@ -19421,10 +19441,7 @@ ], "properties": { "hof": { - "type": "array", - "items": { - "$ref": "#/components/schemas/UserHofStats" - } + "$ref": "#/components/schemas/UserHofStats" } }, "type": "object" diff --git a/torn-key-pool/Cargo.toml b/torn-key-pool/Cargo.toml index cc1afa9..d31b7b7 100644 --- a/torn-key-pool/Cargo.toml +++ b/torn-key-pool/Cargo.toml @@ -1,43 +1,45 @@ [package] name = "torn-key-pool" -version = "0.9.0" +version = "1.0.0" edition = "2021" authors = ["Pyrit [2111649]"] -license = "MIT" -repository = "https://github.com/TotallyNot/torn-api.rs.git" -homepage = "https://github.com/TotallyNot/torn-api.rs.git" +license-file = { workspace = true } +repository = { workspace = true } +homepage = { workspace = true } description = "A generalised API key pool for torn-api" [features] -default = [ "postgres", "tokio-runtime" ] -postgres = [ "dep:sqlx", "dep:chrono", "dep:indoc", "dep:serde" ] -reqwest = [ "dep:reqwest", "torn-api/reqwest" ] -awc = [ "dep:awc", "torn-api/awc" ] -tokio-runtime = [ "dep:tokio", "dep:rand" ] -actix-runtime = [ "dep:actix-rt", "dep:rand" ] +default = ["postgres", "tokio-runtime"] +postgres = ["dep:sqlx", "dep:chrono", "dep:indoc"] +tokio-runtime = ["dep:tokio", "dep:rand"] [dependencies] -torn-api = { path = "../torn-api", default-features = false, version = "0.7" } -async-trait = "0.1" +torn-api = { path = "../torn-api", default-features = false, version = "1.0.1" } thiserror = "2" -sqlx = { version = "0.8", features = [ "postgres", "chrono", "json", "derive" ], optional = true, default-features = false } -serde = { version = "1.0", optional = true } +sqlx = { version = "0.8", features = [ + "postgres", + "chrono", + "json", + "derive", +], optional = true, default-features = false } +serde = { workspace = true } +serde_json = { workspace = true } chrono = { version = "0.4", optional = true } indoc = { version = "2", optional = true } -tokio = { version = "1", optional = true, default-features = false, features = ["time"] } -actix-rt = { version = "2", optional = true, default-features = false } -rand = { version = "0.8", optional = true } +tokio = { version = "1", optional = true, default-features = false, features = [ + "time", +] } +rand = { version = "0.9", optional = true } futures = "0.3" - -reqwest = { version = "0.12", default-features = false, features = [ "json" ], optional = true } -awc = { version = "3", default-features = false, optional = true } +reqwest = { version = "0.12", default-features = false, features = [ + "brotli", + "http2", + "rustls-tls-webpki-roots", +] } [dev-dependencies] -torn-api = { path = "../torn-api", features = [ "reqwest" ] } -sqlx = { version = "0.8", features = [ "runtime-tokio-rustls" ] } -dotenvy = "0.15" +torn-api = { path = "../torn-api" } +sqlx = { version = "0.8", features = ["runtime-tokio-rustls"] } tokio = { version = "1.42", features = ["rt"] } -tokio-test = "0.4" reqwest = { version = "0.12", default-features = true } -awc = { version = "3", features = [ "rustls" ] } diff --git a/torn-key-pool/src/lib.rs b/torn-key-pool/src/lib.rs index 0524829..161cd9a 100644 --- a/torn-key-pool/src/lib.rs +++ b/torn-key-pool/src/lib.rs @@ -3,48 +3,23 @@ #[cfg(feature = "postgres")] pub mod postgres; -// pub mod local; -pub mod send; +use std::{collections::HashMap, future::Future, sync::Arc, time::Duration}; -use std::sync::Arc; +use futures::{future::BoxFuture, FutureExt}; +use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION}; +use serde::Deserialize; +use torn_api::{ + executor::Executor, + request::{ApiRequest, ApiResponse}, + ApiError, +}; -use async_trait::async_trait; -use thiserror::Error; +pub trait ApiKeyId: Clone + PartialEq + Eq + std::hash::Hash + Send + Sync {} -use torn_api::ResponseError; +impl ApiKeyId for T where T: Clone + PartialEq + Eq + std::hash::Hash + Send + Sync {} -#[derive(Debug, Error)] -pub enum KeyPoolError -where - S: std::error::Error + Clone, - C: std::error::Error, -{ - #[error("Key pool storage driver error: {0:?}")] - Storage(#[source] S), - - #[error(transparent)] - Client(#[from] C), - - #[error(transparent)] - Response(ResponseError), -} - -impl KeyPoolError -where - S: std::error::Error + Clone, - C: std::error::Error, -{ - #[inline(always)] - pub fn api_code(&self) -> Option { - match self { - Self::Response(why) => why.api_code(), - _ => None, - } - } -} - -pub trait ApiKey: Sync + Send + std::fmt::Debug + Clone + 'static { - type IdType: PartialEq + Eq + std::hash::Hash + Send + Sync + std::fmt::Debug + Clone; +pub trait ApiKey: Send + Sync + Clone + 'static { + type IdType: ApiKeyId; fn value(&self) -> &str; @@ -105,7 +80,7 @@ where } } -pub trait IntoSelector: Send + Sync +pub trait IntoSelector: Send where K: ApiKey, D: KeyDomain, @@ -133,114 +108,347 @@ where } } -pub enum KeyAction -where - D: KeyDomain, -{ - Delete, - RemoveDomain(D), - Timeout(chrono::Duration), -} - -#[async_trait] -pub trait KeyPoolStorage { +pub trait KeyPoolStorage: Send + Sync { type Key: ApiKey; type Domain: KeyDomain; - type Error: std::error::Error + Sync + Send + Clone; + type Error: From + From + From + Send; - async fn acquire_key(&self, selector: S) -> Result + fn acquire_key( + &self, + selector: S, + ) -> impl Future> + Send where S: IntoSelector; - async fn acquire_many_keys( + fn acquire_many_keys( &self, selector: S, number: i64, - ) -> Result, Self::Error> + ) -> impl Future, Self::Error>> + Send where S: IntoSelector; - async fn flag_key(&self, key: Self::Key, code: u8) -> Result; - - async fn store_key( + fn store_key( &self, user_id: i32, key: String, domains: Vec, - ) -> Result; + ) -> impl Future> + Send; - async fn read_key(&self, selector: S) -> Result, Self::Error> + fn read_key( + &self, + selector: S, + ) -> impl Future, Self::Error>> + Send where S: IntoSelector; - async fn read_keys(&self, selector: S) -> Result, Self::Error> + fn read_keys( + &self, + selector: S, + ) -> impl Future, Self::Error>> + Send where S: IntoSelector; - async fn remove_key(&self, selector: S) -> Result + fn remove_key( + &self, + selector: S, + ) -> impl Future> + Send where S: IntoSelector; - async fn add_domain_to_key( + fn add_domain_to_key( &self, selector: S, domain: Self::Domain, - ) -> Result + ) -> impl Future> + Send where S: IntoSelector; - async fn remove_domain_from_key( + fn remove_domain_from_key( &self, selector: S, domain: Self::Domain, - ) -> Result + ) -> impl Future> + Send where S: IntoSelector; - async fn set_domains_for_key( + fn set_domains_for_key( &self, selector: S, domains: Vec, - ) -> Result + ) -> impl Future> + Send + where + S: IntoSelector; + + fn timeout_key( + &self, + selector: S, + duration: Duration, + ) -> impl Future> + Send where S: IntoSelector; } -#[derive(Debug, Default)] -pub struct PoolOptions { +#[derive(Default)] +pub struct PoolOptions +where + S: KeyPoolStorage, +{ comment: Option, - hooks_before: std::collections::HashMap>, - hooks_after: std::collections::HashMap>, + #[allow(clippy::type_complexity)] + error_hooks: HashMap< + u16, + Box< + dyn for<'a> Fn(&'a S, &'a S::Key) -> BoxFuture<'a, Result> + + Send + + Sync, + >, + >, } -#[derive(Debug, Clone)] -pub struct KeyPoolExecutor<'a, C, S> +pub struct KeyPoolExecutor<'p, S> where S: KeyPoolStorage, { - storage: &'a S, - options: Arc, + pool: &'p KeyPool, selector: KeySelector, - _marker: std::marker::PhantomData, } -impl<'a, C, S> KeyPoolExecutor<'a, C, S> +impl<'p, S> KeyPoolExecutor<'p, S> where S: KeyPoolStorage, { - pub fn new( - storage: &'a S, - selector: KeySelector, - options: Arc, - ) -> Self { - Self { - storage, - selector, - options, - _marker: std::marker::PhantomData, + pub fn new(pool: &'p KeyPool, selector: KeySelector) -> Self { + Self { pool, selector } + } + + async fn execute_request(&self, request: ApiRequest) -> Result, S::Error> + where + D: Send, + { + let key = self.pool.storage.acquire_key(self.selector.clone()).await?; + + let mut headers = HeaderMap::with_capacity(1); + headers.insert( + AUTHORIZATION, + HeaderValue::from_str(&format!("ApiKey {}", key.value())).unwrap(), + ); + + let resp = self + .pool + .client + .get(request.url()) + .headers(headers) + .send() + .await?; + + let status = resp.status(); + + let bytes = resp.bytes().await?; + + if let Some(err) = decode_error(&bytes)? { + if let Some(handler) = self.pool.options.error_hooks.get(&err.code()) { + let retry = (*handler)(&self.pool.storage, &key).await?; + + if retry { + return Box::pin(self.execute_request(request)).await; + } + } + Err(err.into()) + } else { + Ok(ApiResponse { + discriminant: request.disriminant, + body: Some(bytes), + status, + }) } } } -#[cfg(all(test, feature = "postgres"))] -mod test {} +pub struct PoolBuilder +where + S: KeyPoolStorage, +{ + client: reqwest::Client, + storage: S, + options: crate::PoolOptions, +} + +impl PoolBuilder +where + S: KeyPoolStorage, +{ + pub fn new(storage: S) -> Self { + Self { + client: reqwest::Client::builder() + .brotli(true) + .http2_keep_alive_timeout(Duration::from_secs(60)) + .http2_keep_alive_interval(Duration::from_secs(5)) + .https_only(true) + .build() + .unwrap(), + storage, + options: PoolOptions { + comment: None, + error_hooks: Default::default(), + }, + } + } + + pub fn comment(mut self, c: impl ToString) -> Self { + self.options.comment = Some(c.to_string()); + self + } + + pub fn error_hook(mut self, code: u16, handler: F) -> Self + where + F: for<'a> Fn(&'a S, &'a S::Key) -> BoxFuture<'a, Result> + + Send + + Sync + + 'static, + { + self.options.error_hooks.insert(code, Box::new(handler)); + + self + } + + pub fn use_default_hooks(self) -> Self { + self.error_hook(2, |storage, key| { + async move { + storage.remove_key(KeySelector::Id(key.id())).await?; + Ok(true) + } + .boxed() + }) + .error_hook(5, |storage, key| { + async move { + storage + .timeout_key(KeySelector::Id(key.id()), Duration::from_secs(60)) + .await?; + Ok(true) + } + .boxed() + }) + .error_hook(10, |storage, key| { + async move { + storage.remove_key(KeySelector::Id(key.id())).await?; + Ok(true) + } + .boxed() + }) + .error_hook(13, |storage, key| { + async move { + storage + .timeout_key(KeySelector::Id(key.id()), Duration::from_secs(24 * 3_600)) + .await?; + Ok(true) + } + .boxed() + }) + .error_hook(18, |storage, key| { + async move { + storage + .timeout_key(KeySelector::Id(key.id()), Duration::from_secs(24 * 3_600)) + .await?; + Ok(true) + } + .boxed() + }) + } + + pub fn build(self) -> KeyPool { + KeyPool { + client: self.client, + storage: self.storage, + options: Arc::new(self.options), + } + } +} + +pub struct KeyPool +where + S: KeyPoolStorage, +{ + pub client: reqwest::Client, + pub storage: S, + pub options: Arc>, +} + +impl KeyPool +where + S: KeyPoolStorage + Send + Sync + 'static, +{ + pub fn torn_api(&self, selector: I) -> KeyPoolExecutor + where + I: IntoSelector, + { + KeyPoolExecutor::new(self, selector.into_selector()) + } +} + +fn decode_error(buf: &[u8]) -> Result, serde_json::Error> { + if buf.starts_with(br#"{"error":{"#) { + #[derive(Deserialize)] + struct ErrorBody<'a> { + code: u16, + error: &'a str, + } + #[derive(Deserialize)] + struct ErrorContainer<'a> { + #[serde(borrow)] + error: ErrorBody<'a>, + } + + let error: ErrorContainer = serde_json::from_slice(buf)?; + Ok(Some(crate::ApiError::new( + error.error.code, + error.error.error, + ))) + } else { + Ok(None) + } +} + +impl Executor for KeyPoolExecutor<'_, S> +where + S: KeyPoolStorage, +{ + type Error = S::Error; + + async fn execute( + &self, + request: R, + ) -> Result, Self::Error> + where + R: torn_api::request::IntoRequest, + { + let request = request.into_request(); + + self.execute_request(request).await + } +} + +#[cfg(test)] +mod test { + use torn_api::executor::ExecutorExt; + + use crate::postgres; + + use super::*; + + #[sqlx::test] + fn name(pool: sqlx::PgPool) { + let (storage, _) = postgres::test::setup(pool).await; + + let pool = PoolBuilder::new(storage) + .use_default_hooks() + .comment("test_runner") + .build(); + + pool.torn_api(postgres::test::Domain::All) + .faction() + .basic(|b| b) + .await + .unwrap(); + } +} diff --git a/torn-key-pool/src/local.rs b/torn-key-pool/src/local.rs deleted file mode 100644 index 1a23544..0000000 --- a/torn-key-pool/src/local.rs +++ /dev/null @@ -1,206 +0,0 @@ -use std::{collections::HashMap, sync::Arc}; - -use async_trait::async_trait; - -use torn_api::{ - local::{ApiClient, ApiProvider, RequestExecutor}, - ApiRequest, ApiResponse, ApiSelection, ResponseError, -}; - -use crate::{ApiKey, KeyPoolError, KeyPoolExecutor, KeyPoolStorage, IntoSelector}; - -#[async_trait(?Send)] -impl<'client, C, S> RequestExecutor for KeyPoolExecutor<'client, C, S> -where - C: ApiClient, - S: KeyPoolStorage + 'static, -{ - type Error = KeyPoolError; - - async fn execute( - &self, - client: &C, - mut request: ApiRequest, - id: Option, - ) -> Result - where - A: ApiSelection, - { - request.comment = self.comment.map(ToOwned::to_owned); - loop { - let key = self - .storage - .acquire_key(self.selector.clone()) - .await - .map_err(|e| KeyPoolError::Storage(Arc::new(e)))?; - let url = request.url(key.value(), id.as_deref()); - let value = client.request(url).await?; - - match ApiResponse::from_value(value) { - Err(ResponseError::Api { code, reason }) => { - if !self - .storage - .flag_key(key, code) - .await - .map_err(Arc::new) - .map_err(KeyPoolError::Storage)? - { - return Err(KeyPoolError::Response(ResponseError::Api { code, reason })); - } - } - Err(parsing_error) => return Err(KeyPoolError::Response(parsing_error)), - Ok(res) => return Ok(res), - }; - } - } - - async fn execute_many( - &self, - client: &C, - mut request: ApiRequest, - ids: Vec, - ) -> HashMap> - where - A: ApiSelection, - I: ToString + std::hash::Hash + std::cmp::Eq, - { - let keys = match self - .storage - .acquire_many_keys(self.selector.clone(), ids.len() as i64) - .await - { - Ok(keys) => keys, - Err(why) => { - let shared = Arc::new(why); - return ids - .into_iter() - .map(|i| (i, Err(Self::Error::Storage(shared.clone())))) - .collect(); - } - }; - - request.comment = self.comment.map(ToOwned::to_owned); - let request_ref = &request; - - let tuples = - futures::future::join_all(std::iter::zip(ids, keys).map(|(id, mut key)| async move { - let id_string = id.to_string(); - loop { - let url = request_ref.url(key.value(), Some(&id_string)); - let value = match client.request(url).await { - Ok(v) => v, - Err(why) => return (id, Err(Self::Error::Client(why))), - }; - - match ApiResponse::from_value(value) { - Err(ResponseError::Api { code, reason }) => { - match self.storage.flag_key(key, code).await { - Ok(false) => { - return ( - id, - Err(KeyPoolError::Response(ResponseError::Api { - code, - reason, - })), - ) - } - Ok(true) => (), - Err(why) => return (id, Err(KeyPoolError::Storage(Arc::new(why)))), - } - } - Err(parsing_error) => { - return (id, Err(KeyPoolError::Response(parsing_error))) - } - Ok(res) => return (id, Ok(res)), - }; - - key = match self.storage.acquire_key(self.selector.clone()).await { - Ok(k) => k, - Err(why) => return (id, Err(Self::Error::Storage(Arc::new(why)))), - }; - } - })) - .await; - - HashMap::from_iter(tuples) - } -} - -#[derive(Clone, Debug)] -pub struct KeyPool -where - C: ApiClient, - S: KeyPoolStorage, -{ - client: C, - pub storage: S, - comment: Option, -} - -impl KeyPool -where - C: ApiClient, - S: KeyPoolStorage + 'static, -{ - pub fn new(client: C, storage: S, comment: Option) -> Self { - Self { - client, - storage, - comment, - } - } - - pub fn torn_api(&self, selector: I) -> ApiProvider> where I: IntoSelector { - ApiProvider::new( - &self.client, - KeyPoolExecutor::new(&self.storage, selector.into_selector(), self.comment.as_deref()), - ) - } -} - -pub trait WithStorage { - fn with_storage<'a, S, I>( - &'a self, - storage: &'a S, - selector: I - ) -> ApiProvider> - where - Self: ApiClient + Sized, - S: KeyPoolStorage + 'static, - I: IntoSelector - { - ApiProvider::new(self, KeyPoolExecutor::new(storage, selector.into_selector(), None)) - } -} - -#[cfg(feature = "awc")] -impl WithStorage for awc::Client {} - -#[cfg(all(test, feature = "postgres", feature = "awc"))] -mod test { - use tokio::test; - - use super::*; - use crate::postgres::test::{setup, Domain}; - - #[test] - async fn test_pool_request() { - let storage = setup().await; - let pool = KeyPool::new(awc::Client::default(), storage); - - let response = pool.torn_api(Domain::All).user(|b| b).await.unwrap(); - _ = response.profile().unwrap(); - } - - #[test] - async fn test_with_storage_request() { - let storage = setup().await; - - let response = awc::Client::new() - .with_storage(&storage, Domain::All) - .user(|b| b) - .await - .unwrap(); - _ = response.profile().unwrap(); - } -} diff --git a/torn-key-pool/src/postgres.rs b/torn-key-pool/src/postgres.rs index 99ebe01..844e260 100644 --- a/torn-key-pool/src/postgres.rs +++ b/torn-key-pool/src/postgres.rs @@ -1,6 +1,4 @@ -use std::sync::Arc; - -use async_trait::async_trait; +use futures::future::BoxFuture; use indoc::indoc; use sqlx::{FromRow, PgPool, Postgres, QueryBuilder}; use thiserror::Error; @@ -17,13 +15,22 @@ impl PgKeyDomain for T where { } -#[derive(Debug, Error, Clone)] -pub enum PgStorageError +#[derive(Debug, Error)] +pub enum PgKeyPoolError where D: PgKeyDomain, { - #[error(transparent)] - Pg(Arc), + #[error("Databank: {0}")] + Pg(#[from] sqlx::Error), + + #[error("Network: {0}")] + Network(#[from] reqwest::Error), + + #[error("Parsing: {0}")] + Parsing(#[from] serde_json::Error), + + #[error("Api: {0}")] + Api(#[from] torn_api::ApiError), #[error("No key avalaible for domain {0:?}")] Unavailable(KeySelector, D>), @@ -32,15 +39,6 @@ where KeyNotFound(KeySelector, D>), } -impl From for PgStorageError -where - D: PgKeyDomain, -{ - fn from(value: sqlx::Error) -> Self { - Self::Pg(Arc::new(value)) - } -} - #[derive(Debug, Clone, FromRow)] pub struct PgKey where @@ -127,7 +125,7 @@ where } } - pub async fn initialise(&self) -> Result<(), PgStorageError> { + pub async fn initialise(&self) -> Result<(), PgKeyPoolError> { sqlx::query(indoc! {r#" CREATE TABLE IF NOT EXISTS api_keys ( id serial primary key, @@ -184,19 +182,11 @@ where #[cfg(feature = "tokio-runtime")] async fn random_sleep() { - use rand::{thread_rng, Rng}; - let dur = tokio::time::Duration::from_millis(thread_rng().gen_range(1..50)); + use rand::{rng, Rng}; + let dur = tokio::time::Duration::from_millis(rng().random_range(1..50)); tokio::time::sleep(dur).await; } -#[cfg(all(not(feature = "tokio-runtime"), feature = "actix-runtime"))] -async fn random_sleep() { - use rand::{thread_rng, Rng}; - let dur = std::time::Duration::from_millis(thread_rng().gen_range(1..50)); - actix_rt::time::sleep(dur).await; -} - -#[async_trait] impl KeyPoolStorage for PgKeyPoolStorage where D: PgKeyDomain, @@ -204,7 +194,7 @@ where type Key = PgKey; type Domain = D; - type Error = PgStorageError; + type Error = PgKeyPoolError; async fn acquire_key(&self, selector: S) -> Result where @@ -222,10 +212,10 @@ where let mut qb = QueryBuilder::new(indoc::indoc! { r#" with key as ( - select + select id, 0::int2 as uses - from api_keys where last_used < date_trunc('minute', now()) + from api_keys where last_used < date_trunc('minute', now()) and (cooldown is null or now() >= cooldown) and "# }); @@ -235,9 +225,9 @@ where qb.push(indoc::indoc! { " \n union ( - select id, uses from api_keys - where last_used >= date_trunc('minute', now()) - and (cooldown is null or now() >= cooldown) + select id, uses from api_keys + where last_used >= date_trunc('minute', now()) + and (cooldown is null or now() >= cooldown) and " }); @@ -254,7 +244,7 @@ where cooldown = null, flag = null, last_used = now() - from key where + from key where api_keys.id=key.id and key.uses < " }); @@ -280,13 +270,23 @@ where match attempt { Ok(Some(result)) => return Ok(result), Ok(None) => { - return self - .acquire_key( - selector - .fallback() - .ok_or_else(|| PgStorageError::Unavailable(selector))?, - ) - .await + fn recurse( + storage: &PgKeyPoolStorage, + selector: KeySelector, D>, + ) -> BoxFuture, PgKeyPoolError>> + where + D: PgKeyDomain, + { + Box::pin(storage.acquire_key(selector)) + } + + return recurse( + self, + selector + .fallback() + .ok_or_else(|| PgKeyPoolError::Unavailable(selector))?, + ) + .await; } Err(error) => { if let Some(db_error) = error.as_database_error() { @@ -365,7 +365,7 @@ where let available = max.uses - key.uses; let using = std::cmp::min(available, (number as i16) - (result.len() as i16)); key.uses += using; - result.extend(std::iter::repeat(key.clone()).take(using as usize)); + result.extend(std::iter::repeat_n(key.clone(), using as usize)); if result.len() == number as usize { break; @@ -406,14 +406,25 @@ where match attempt { Ok(Some(result)) => return Ok(result), Ok(None) => { - return self - .acquire_many_keys( - selector - .fallback() - .ok_or_else(|| Self::Error::Unavailable(selector))?, - number, - ) - .await + fn recurse( + storage: &PgKeyPoolStorage, + selector: KeySelector, D>, + number: i64, + ) -> BoxFuture>, PgKeyPoolError>> + where + D: PgKeyDomain, + { + Box::pin(storage.acquire_many_keys(selector, number)) + } + + return recurse( + self, + selector + .fallback() + .ok_or_else(|| Self::Error::Unavailable(selector))?, + number, + ) + .await; } Err(error) => { if let Some(db_error) = error.as_database_error() { @@ -431,57 +442,24 @@ where } } - async fn flag_key(&self, key: Self::Key, code: u8) -> Result { - match code { - 2 | 10 | 13 => { - // invalid key, owner fedded or owner inactive - sqlx::query( - "update api_keys set cooldown='infinity'::timestamptz, flag=$1 where id=$2", - ) - .bind(code as i16) - .bind(key.id) - .execute(&self.pool) - .await?; - Ok(true) - } - 5 => { - // too many requests - sqlx::query( - "update api_keys set cooldown=date_trunc('min', now()) + interval '1 min', \ - flag=5 where id=$1", - ) - .bind(key.id) - .execute(&self.pool) - .await?; - Ok(true) - } - 8 => { - // IP block - sqlx::query("update api_keys set cooldown=now() + interval '5 min', flag=8") - .execute(&self.pool) - .await?; - Ok(false) - } - 9 => { - // API disabled - sqlx::query("update api_keys set cooldown=now() + interval '1 min', flag=9") - .execute(&self.pool) - .await?; - Ok(false) - } - 14 => { - // daily read limit reached - sqlx::query( - "update api_keys set cooldown=date_trunc('day', now()) + interval '1 day', \ - flag=14 where id=$1", - ) - .bind(key.id) - .execute(&self.pool) - .await?; - Ok(true) - } - _ => Ok(false), - } + async fn timeout_key( + &self, + selector: S, + duration: std::time::Duration, + ) -> Result<(), Self::Error> + where + S: IntoSelector, + { + let selector = selector.into_selector(); + + let mut qb = QueryBuilder::new("update api_keys set cooldown=now() + "); + qb.push_bind(duration); + qb.push(" where "); + build_predicate(&mut qb, &selector); + + qb.build().fetch_optional(&self.pool).await?; + + Ok(()) } async fn store_key( @@ -546,7 +524,7 @@ where qb.build_query_as() .fetch_optional(&self.pool) .await? - .ok_or_else(|| PgStorageError::KeyNotFound(selector)) + .ok_or_else(|| PgKeyPoolError::KeyNotFound(selector)) } async fn add_domain_to_key(&self, selector: S, domain: D) -> Result @@ -566,7 +544,7 @@ where qb.build_query_as() .fetch_optional(&self.pool) .await? - .ok_or_else(|| PgStorageError::KeyNotFound(selector)) + .ok_or_else(|| PgKeyPoolError::KeyNotFound(selector)) } async fn remove_domain_from_key( @@ -590,7 +568,7 @@ where qb.build_query_as() .fetch_optional(&self.pool) .await? - .ok_or_else(|| PgStorageError::KeyNotFound(selector)) + .ok_or_else(|| PgKeyPoolError::KeyNotFound(selector)) } async fn set_domains_for_key( @@ -612,13 +590,13 @@ where qb.build_query_as() .fetch_optional(&self.pool) .await? - .ok_or_else(|| PgStorageError::KeyNotFound(selector)) + .ok_or_else(|| PgKeyPoolError::KeyNotFound(selector)) } } #[cfg(test)] pub(crate) mod test { - use std::sync::Arc; + use std::{sync::Arc, time::Duration}; use sqlx::Row; @@ -652,7 +630,7 @@ pub(crate) mod test { storage.initialise().await.unwrap(); let key = storage - .store_key(1, std::env::var("APIKEY").unwrap(), vec![Domain::All]) + .store_key(1, std::env::var("API_KEY").unwrap(), vec![Domain::All]) .await .unwrap(); @@ -816,34 +794,6 @@ pub(crate) mod test { } } - #[sqlx::test] - async fn test_flag_key_one(pool: PgPool) { - let (storage, key) = setup(pool).await; - - assert!(storage.flag_key(key, 2).await.unwrap()); - - match storage.acquire_key(Domain::All).await.unwrap_err() { - PgStorageError::Unavailable(KeySelector::Has(domains)) => { - assert_eq!(domains, vec![Domain::All]) - } - why => panic!("Expected domain unavailable error but found '{why}'"), - } - } - - #[sqlx::test] - async fn test_flag_key_many(pool: PgPool) { - let (storage, key) = setup(pool).await; - - assert!(storage.flag_key(key, 2).await.unwrap()); - - match storage.acquire_many_keys(Domain::All, 5).await.unwrap_err() { - PgStorageError::Unavailable(KeySelector::Has(domains)) => { - assert_eq!(domains, vec![Domain::All]) - } - why => panic!("Expected domain unavailable error but found '{why}'"), - } - } - #[sqlx::test] async fn acquire_many(pool: PgPool) { let (storage, _) = setup(pool).await; @@ -1025,6 +975,16 @@ pub(crate) mod test { assert!(key.is_some()); } + #[sqlx::test] + async fn timeout(pool: PgPool) { + let (storage, key) = setup(pool).await; + + storage + .timeout_key(KeySelector::Id(key.id()), Duration::from_secs(60)) + .await + .unwrap(); + } + #[sqlx::test] async fn query_by_set(pool: PgPool) { let (storage, _key) = setup(pool).await; diff --git a/torn-key-pool/src/send.rs b/torn-key-pool/src/send.rs deleted file mode 100644 index 1c3c93d..0000000 --- a/torn-key-pool/src/send.rs +++ /dev/null @@ -1,380 +0,0 @@ -use std::{collections::HashMap, sync::Arc}; - -use async_trait::async_trait; - -use torn_api::{ - send::{ApiClient, ApiProvider, RequestExecutor}, - ApiRequest, ApiResponse, ApiSelection, ResponseError, -}; - -use crate::{ - ApiKey, IntoSelector, KeyAction, KeyDomain, KeyPoolError, KeyPoolExecutor, KeyPoolStorage, - KeySelector, PoolOptions, -}; - -#[async_trait] -impl<'client, C, S> RequestExecutor for KeyPoolExecutor<'client, C, S> -where - C: ApiClient, - S: KeyPoolStorage + Send + Sync + 'static, -{ - type Error = KeyPoolError; - - async fn execute( - &self, - client: &C, - mut request: ApiRequest, - id: Option, - ) -> Result - where - A: ApiSelection, - { - if request.comment.is_none() { - request.comment = self.options.comment.clone(); - } - if let Some(hook) = self.options.hooks_before.get(&std::any::TypeId::of::()) { - let concrete = hook - .downcast_ref::>() - .unwrap(); - - (concrete.body)(&mut request, &self.selector); - } - loop { - let key = self - .storage - .acquire_key(self.selector.clone()) - .await - .map_err(KeyPoolError::Storage)?; - let url = request.url(key.value(), id.as_deref()); - let value = client.request(url).await?; - - match ApiResponse::from_value(value) { - Err(ResponseError::Api { code, reason }) => { - if !self - .storage - .flag_key(key, code) - .await - .map_err(KeyPoolError::Storage)? - { - return Err(KeyPoolError::Response(ResponseError::Api { code, reason })); - } - } - Err(parsing_error) => return Err(KeyPoolError::Response(parsing_error)), - Ok(res) => { - let res = res.into(); - if let Some(hook) = self.options.hooks_after.get(&std::any::TypeId::of::()) { - let concrete = hook - .downcast_ref::>() - .unwrap(); - - match (concrete.body)(&res, &self.selector) { - Err(KeyAction::Delete) => { - self.storage - .remove_key(key.selector()) - .await - .map_err(KeyPoolError::Storage)?; - continue; - } - Err(KeyAction::RemoveDomain(domain)) => { - self.storage - .remove_domain_from_key(key.selector(), domain) - .await - .map_err(KeyPoolError::Storage)?; - continue; - } - _ => (), - }; - } - return Ok(res); - } - }; - } - } - - async fn execute_many( - &self, - client: &C, - mut request: ApiRequest, - ids: Vec, - ) -> HashMap> - where - A: ApiSelection, - I: ToString + std::hash::Hash + std::cmp::Eq + Send + Sync, - { - let keys = match self - .storage - .acquire_many_keys(self.selector.clone(), ids.len() as i64) - .await - { - Ok(keys) => keys, - Err(why) => { - return ids - .into_iter() - .map(|i| (i, Err(Self::Error::Storage(why.clone())))) - .collect(); - } - }; - - if request.comment.is_none() { - request.comment = self.options.comment.clone(); - } - let request_ref = &request; - - let tuples = - futures::future::join_all(std::iter::zip(ids, keys).map(|(id, mut key)| async move { - let id_string = id.to_string(); - loop { - let url = request_ref.url(key.value(), Some(&id_string)); - let value = match client.request(url).await { - Ok(v) => v, - Err(why) => return (id, Err(Self::Error::Client(why))), - }; - - match ApiResponse::from_value(value) { - Err(ResponseError::Api { code, reason }) => { - match self.storage.flag_key(key, code).await { - Ok(false) => { - return ( - id, - Err(KeyPoolError::Response(ResponseError::Api { - code, - reason, - })), - ) - } - Ok(true) => (), - Err(why) => return (id, Err(KeyPoolError::Storage(why))), - } - } - Err(parsing_error) => { - return (id, Err(KeyPoolError::Response(parsing_error))) - } - Ok(res) => return (id, Ok(res.into())), - }; - - key = match self.storage.acquire_key(self.selector.clone()).await { - Ok(k) => k, - Err(why) => return (id, Err(Self::Error::Storage(why))), - }; - } - })) - .await; - - HashMap::from_iter(tuples) - } -} - -#[allow(clippy::type_complexity)] -pub struct BeforeHook -where - A: ApiSelection, - K: ApiKey, - D: KeyDomain, -{ - body: Box, &KeySelector) + Send + Sync + 'static>, -} - -#[allow(clippy::type_complexity)] -pub struct AfterHook -where - A: ApiSelection, - K: ApiKey, - D: KeyDomain, -{ - body: Box< - dyn Fn(&A::Response, &KeySelector) -> Result<(), crate::KeyAction> - + Send - + Sync - + 'static, - >, -} - -pub struct PoolBuilder -where - C: ApiClient, - S: KeyPoolStorage, -{ - client: C, - storage: S, - options: crate::PoolOptions, -} - -impl PoolBuilder -where - C: ApiClient, - S: KeyPoolStorage, -{ - pub fn new(client: C, storage: S) -> Self { - Self { - client, - storage, - options: Default::default(), - } - } - - pub fn comment(mut self, c: impl ToString) -> Self { - self.options.comment = Some(c.to_string()); - self - } - - pub fn hook_before( - mut self, - hook: impl Fn(&mut ApiRequest, &KeySelector) + Send + Sync + 'static, - ) -> Self - where - A: ApiSelection + 'static, - { - self.options.hooks_before.insert( - std::any::TypeId::of::(), - Box::new(BeforeHook { - body: Box::new(hook), - }), - ); - self - } - - pub fn hook_after( - mut self, - hook: impl Fn(&A::Response, &KeySelector) -> Result<(), KeyAction> - + Send - + Sync - + 'static, - ) -> Self - where - A: ApiSelection + 'static, - { - self.options.hooks_after.insert( - std::any::TypeId::of::(), - Box::new(AfterHook:: { - body: Box::new(hook), - }), - ); - self - } - - pub fn build(self) -> KeyPool { - KeyPool { - client: self.client, - storage: self.storage, - options: Arc::new(self.options), - } - } -} - -#[derive(Clone, Debug)] -pub struct KeyPool -where - C: ApiClient, - S: KeyPoolStorage, -{ - pub client: C, - pub storage: S, - pub options: Arc, -} - -impl KeyPool -where - C: ApiClient, - S: KeyPoolStorage + Send + Sync + 'static, -{ - pub fn torn_api(&self, selector: I) -> ApiProvider> - where - I: IntoSelector, - { - ApiProvider::new( - &self.client, - KeyPoolExecutor::new( - &self.storage, - selector.into_selector(), - self.options.clone(), - ), - ) - } -} - -pub trait WithStorage { - fn with_storage<'a, S, I>( - &'a self, - storage: &'a S, - selector: I, - ) -> ApiProvider> - where - Self: ApiClient + Sized, - S: KeyPoolStorage + Send + Sync + 'static, - I: IntoSelector, - { - ApiProvider::new( - self, - KeyPoolExecutor::new(storage, selector.into_selector(), Default::default()), - ) - } -} - -#[cfg(feature = "reqwest")] -impl WithStorage for reqwest::Client {} - -#[cfg(all(test, feature = "postgres", feature = "reqwest"))] -mod test { - use sqlx::PgPool; - - use super::*; - use crate::{ - postgres::test::{setup, Domain}, - KeySelector, - }; - - #[sqlx::test] - async fn test_pool_request(pool: PgPool) { - let (storage, _) = setup(pool).await; - let pool = PoolBuilder::new(reqwest::Client::default(), storage) - .comment("api.rs") - .build(); - - let response = pool.torn_api(Domain::All).user(|b| b).await.unwrap(); - _ = response.profile().unwrap(); - } - - #[sqlx::test] - async fn test_with_storage_request(pool: PgPool) { - let (storage, _) = setup(pool).await; - - let response = reqwest::Client::new() - .with_storage(&storage, Domain::All) - .user(|b| b) - .await - .unwrap(); - _ = response.profile().unwrap(); - } - - #[sqlx::test] - async fn before_hook(pool: PgPool) { - let (storage, _) = setup(pool).await; - - let pool = PoolBuilder::new(reqwest::Client::default(), storage) - .hook_before::(|req, _s| { - req.selections.push("crimes"); - }) - .build(); - - let response = pool.torn_api(Domain::All).user(|b| b).await.unwrap(); - _ = response.crimes().unwrap(); - } - - #[sqlx::test] - async fn after_hook(pool: PgPool) { - let (storage, _) = setup(pool).await; - - let pool = PoolBuilder::new(reqwest::Client::default(), storage) - .hook_after::(|_res, _s| Err(KeyAction::Delete)) - .build(); - - let key = pool.storage.read_key(KeySelector::Id(1)).await.unwrap(); - assert!(key.is_some()); - - let response = pool.torn_api(Domain::All).user(|b| b).await; - assert!(matches!(response, Err(KeyPoolError::Storage(_)))); - - let key = pool.storage.read_key(KeySelector::Id(1)).await.unwrap(); - assert!(key.is_none()); - } -}