diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fa7a701..21508ec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,8 +18,12 @@ jobs: steps: - uses: actions/checkout@v4 - run: rustup update stable && rustup default stable - - run: cargo fmt --check + - run: cargo outdated --root-deps-only --exit-code 1 + - run: cargo fmt --all -- --check + - run: cargo generate-lockfile --locked + - run: cargo check --locked - run: cargo clippy --locked + - run: cargo verify-project --locked test: name: Test diff --git a/.gitignore b/.gitignore index 8b0e667..9280096 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,9 @@ # Added by cargo /target + +# don't check in our precious secrets +keycloak-client-secret + +# don't check in Python virtual environments +env diff --git a/Cargo.lock b/Cargo.lock index 18fc3df..940e1fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,18 +4,31 @@ version = 3 [[package]] name = "addr2line" -version = "0.22.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "ahash" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] [[package]] name = "aho-corasick" @@ -26,11 +39,26 @@ dependencies = [ "memchr", ] +[[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 = "anstream" -version = "0.6.14" +version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" dependencies = [ "anstyle", "anstyle-parse", @@ -43,33 +71,33 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anstyle-parse" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -77,29 +105,163 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37bf3594c4c988a53154954629820791dde498571819ae4ca50ca811e060cc95" + +[[package]] +name = "async-trait" +version = "0.1.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "atomic-time" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9622f5c6fb50377516c70f65159e70b25465409760c6bd6d4e581318bf704e83" +dependencies = [ + "once_cell", + "portable-atomic", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "axum" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" +dependencies = [ + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.5.0", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 1.0.1", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-extra" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73c3220b188aea709cf1b6c5f9b01c3bd936bb08bd2b5184a12b35ac8131b1f9" +dependencies = [ + "axum", + "axum-core", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite", + "serde", + "serde_json", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-keycloak-auth" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be9f06871201a9be2a42da89f98ad7b588681d7ac6c6b18193e010732f90b8d3" +dependencies = [ + "atomic-time", + "axum", + "educe", + "futures", + "http 1.1.0", + "jsonwebtoken", + "nonempty", + "reqwest", + "serde", + "serde-querystring", + "serde_json", + "serde_with", + "snafu", + "time 0.3.36", + "tokio", + "tower", + "tracing", + "try-again", + "typed-builder 0.20.0", + "url", + "uuid", +] [[package]] name = "backtrace" -version = "0.3.73" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", + "windows-targets 0.52.6", ] [[package]] @@ -108,6 +270,18 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "base64" version = "0.22.1" @@ -123,29 +297,92 @@ dependencies = [ "serde", ] +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[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 = "borrow-bag" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b654fee34da149f35fa96ba970ca2d342490f7461fbaa99b6588b63595c7fccf" +[[package]] +name = "bson" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "068208f2b6fcfa27a7f1ee37488d2bb8ba2640f68f5475d08e1d9130696aba59" +dependencies = [ + "ahash", + "base64 0.13.1", + "bitvec", + "hex", + "indexmap 2.6.0", + "js-sys", + "once_cell", + "rand", + "serde", + "serde_bytes", + "serde_json", + "time 0.3.36", + "uuid", +] + [[package]] name = "bumpalo" version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" [[package]] name = "cc" -version = "1.0.99" +version = "1.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" +checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" @@ -153,11 +390,26 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets 0.52.6", +] + [[package]] name = "colorchoice" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" [[package]] name = "const_fn" @@ -165,6 +417,12 @@ version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "373e9fafaa20882876db20562275ff58d50e0caa2590077fe7ce7bef90211d0d" +[[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.2" @@ -176,152 +434,412 @@ dependencies = [ ] [[package]] -name = "deranged" -version = "0.3.11" +name = "core-foundation" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ - "powerfmt", + "core-foundation-sys", + "libc", ] [[package]] -name = "discard" -version = "1.0.4" +name = "core-foundation-sys" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] -name = "either" -version = "1.12.0" +name = "cpufeatures" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] [[package]] -name = "env_filter" -version = "0.1.0" +name = "crypto-common" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "log", - "regex", + "generic-array", + "typenum", ] [[package]] -name = "env_logger" -version = "0.11.3" +name = "darling" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ - "anstream", - "anstyle", - "env_filter", - "humantime", - "log", + "darling_core", + "darling_macro", ] [[package]] -name = "equivalent" -version = "1.0.1" +name = "darling_core" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.82", +] [[package]] -name = "fnv" -version = "1.0.7" +name = "darling_macro" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.82", +] [[package]] -name = "futures-channel" -version = "0.3.30" +name = "data-encoding" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ - "futures-core", + "powerfmt", + "serde", ] [[package]] -name = "futures-core" -version = "0.3.30" +name = "derivative" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] [[package]] -name = "futures-macro" -version = "0.3.30" +name = "derive_more" +version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ + "convert_case", "proc-macro2", "quote", - "syn 2.0.66", + "rustc_version 0.4.1", + "syn 2.0.82", ] [[package]] -name = "futures-sink" -version = "0.3.30" +name = "digest" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] [[package]] -name = "futures-task" -version = "0.3.30" +name = "discard" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" [[package]] -name = "futures-util" -version = "0.3.30" +name = "educe" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" dependencies = [ - "futures-core", - "futures-macro", - "futures-task", - "pin-project-lite", - "pin-utils", - "slab", + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 2.0.82", ] [[package]] -name = "getrandom" -version = "0.2.15" +name = "either" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if", - "libc", - "wasi", ] [[package]] -name = "gimli" -version = "0.29.0" +name = "enum-as-inner" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.82", +] [[package]] -name = "gotham" -version = "0.7.4" +name = "enum-ordinalize" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c0e1892f0ec4bb9a4f5441c1d56af1d13d9bbb2a8f44ac58a670862eff3f8a6" +checksum = "fea0dcfa4e54eeb516fe454635a95753ddd39acda650ce703031c6973e315dd5" dependencies = [ - "anyhow", - "base64", - "bincode", - "borrow-bag", - "bytes", - "cookie", - "futures-util", - "gotham_derive", - "httpdate", - "hyper", - "linked-hash-map", - "log", - "mime", - "mime_guess", - "num_cpus", - "percent-encoding", + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "env_filter" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + +[[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.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[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" +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-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 2.0.82", +] + +[[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.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "gotham" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c0e1892f0ec4bb9a4f5441c1d56af1d13d9bbb2a8f44ac58a670862eff3f8a6" +dependencies = [ + "anyhow", + "base64 0.22.1", + "bincode", + "borrow-bag", + "bytes", + "cookie", + "futures-util", + "gotham_derive", + "httpdate", + "hyper 0.14.31", + "linked-hash-map", + "log", + "mime", + "mime_guess", + "num_cpus", + "percent-encoding", "pin-project", "rand", "rand_chacha", @@ -340,185 +858,570 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39260b1324f4cf40bf4d86e4aa3a7574fabf6b25fe1b07345b776698d2a0459" dependencies = [ "quote", - "syn 2.0.66", + "syn 2.0.82", +] + +[[package]] +name = "gotham_restful" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7a8a70322a2455f2b4bdf45f8598df4601fe5458a943813c37426da3f81bc6" +dependencies = [ + "futures-core", + "futures-util", + "gotham", + "gotham_restful_derive", + "log", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "gotham_restful_derive" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4804f5a4e9bc029f57f34f8906757e1666621db6dc0ead37d9aedbea9c70372" +dependencies = [ + "either", + "lazy-regex", + "paste", + "proc-macro2", + "quote", + "syn 2.0.82", + "unindent", +] + +[[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 0.2.12", + "indexmap 2.6.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.1.0", + "indexmap 2.6.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + +[[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.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hickory-proto" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07698b8420e2f0d6447a436ba999ec85d8fbf2a398bbd737b82cac4a2e96e512" +dependencies = [ + "async-trait", + "cfg-if", + "data-encoding", + "enum-as-inner", + "futures-channel", + "futures-io", + "futures-util", + "idna 0.4.0", + "ipnet", + "once_cell", + "rand", + "thiserror", + "tinyvec", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "hickory-resolver" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28757f23aa75c98f254cf0405e6d8c25b831b32921b050a66692427679b1f243" +dependencies = [ + "cfg-if", + "futures-util", + "hickory-proto", + "ipconfig", + "lru-cache", + "once_cell", + "parking_lot", + "rand", + "resolv-conf", + "smallvec", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[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", +] + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.1.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2 0.4.6", + "http 1.1.0", + "http-body 1.0.1", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +dependencies = [ + "futures-util", + "http 1.1.0", + "hyper 1.5.0", + "hyper-util", + "rustls 0.23.15", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.0", + "tower-service", +] + +[[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 1.5.0", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "hyper 1.5.0", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "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 = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", ] [[package]] -name = "gotham_restful" -version = "0.9.0" +name = "indexmap" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7a8a70322a2455f2b4bdf45f8598df4601fe5458a943813c37426da3f81bc6" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ - "futures-core", - "futures-util", - "gotham", - "gotham_restful_derive", - "log", + "equivalent", + "hashbrown 0.15.0", "serde", - "serde_json", - "thiserror", ] [[package]] -name = "gotham_restful_derive" -version = "0.9.0" +name = "ipconfig" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4804f5a4e9bc029f57f34f8906757e1666621db6dc0ead37d9aedbea9c70372" +checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "either", - "lazy-regex", - "paste", - "proc-macro2", - "quote", - "syn 2.0.66", - "unindent", + "socket2", + "widestring", + "windows-sys 0.48.0", + "winreg", ] [[package]] -name = "h2" -version = "0.3.26" +name = "ipnet" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" [[package]] -name = "hashbrown" -version = "0.14.5" +name = "is_terminal_polyfill" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] -name = "hermit-abi" -version = "0.3.9" +name = "itoa" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] -name = "http" -version = "0.2.12" +name = "js-sys" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" dependencies = [ - "bytes", - "fnv", - "itoa", + "wasm-bindgen", ] [[package]] -name = "http-body" -version = "0.4.6" +name = "jsonwebtoken" +version = "9.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +checksum = "b9ae10193d25051e74945f1ea2d0b42e03cc3b890f7e4cc5faa44997d808193f" dependencies = [ - "bytes", - "http", - "pin-project-lite", + "base64 0.21.7", + "js-sys", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", ] [[package]] -name = "httparse" -version = "1.9.4" +name = "lazy-regex" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +checksum = "8d8e41c97e6bc7ecb552016274b99fbb5d035e8de288c582d9b933af6677bfda" +dependencies = [ + "lazy-regex-proc_macros", + "once_cell", + "regex", +] [[package]] -name = "httpdate" -version = "1.0.3" +name = "lazy-regex-proc_macros" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +checksum = "76e1d8b05d672c53cb9c7b920bbba8783845ae4f0b076e02a3db1d02c81b4163" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "syn 2.0.82", +] [[package]] -name = "humantime" -version = "2.1.0" +name = "lexical" +version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +checksum = "c7aefb36fd43fef7003334742cbf77b243fcd36418a1d1bdd480d613a67968f6" +dependencies = [ + "lexical-core", +] [[package]] -name = "hyper" -version = "0.14.29" +name = "lexical-core" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" +checksum = "2cde5de06e8d4c2faabc400238f9ae1c74d5412d03a7bd067645ccbc47070e46" dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", + "lexical-parse-float", + "lexical-parse-integer", + "lexical-util", + "lexical-write-float", + "lexical-write-integer", ] [[package]] -name = "indexmap" -version = "2.2.6" +name = "lexical-parse-float" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "683b3a5ebd0130b8fb52ba0bdc718cc56815b6a097e28ae5a6997d0ad17dc05f" dependencies = [ - "equivalent", - "hashbrown", + "lexical-parse-integer", + "lexical-util", + "static_assertions", ] [[package]] -name = "is_terminal_polyfill" -version = "1.70.0" +name = "lexical-parse-integer" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" +checksum = "6d0994485ed0c312f6d965766754ea177d07f9c00c9b82a5ee62ed5b47945ee9" +dependencies = [ + "lexical-util", + "static_assertions", +] [[package]] -name = "itoa" -version = "1.0.11" +name = "lexical-util" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "5255b9ff16ff898710eb9eb63cb39248ea8a5bb036bea8085b1a767ff6c4e3fc" +dependencies = [ + "static_assertions", +] [[package]] -name = "lazy-regex" -version = "3.1.0" +name = "lexical-write-float" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d12be4595afdf58bd19e4a9f4e24187da2a66700786ff660a418e9059937a4c" +checksum = "accabaa1c4581f05a3923d1b4cfd124c329352288b7b9da09e766b0668116862" dependencies = [ - "lazy-regex-proc_macros", - "once_cell", - "regex", + "lexical-util", + "lexical-write-integer", + "static_assertions", ] [[package]] -name = "lazy-regex-proc_macros" -version = "3.1.0" +name = "lexical-write-integer" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bcd58e6c97a7fcbaffcdc95728b393b8d98933bfadad49ed4097845b57ef0b" +checksum = "e1b6f3d1f4422866b68192d62f77bc5c700bee84f3069f2469d7bc8c77852446" dependencies = [ - "proc-macro2", - "quote", - "regex", - "syn 2.0.66", + "lexical-util", + "static_assertions", ] [[package]] name = "libc" -version = "0.2.155" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "linked-hash-map" @@ -526,11 +1429,58 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + +[[package]] +name = "matchit" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[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" @@ -546,9 +1496,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mime_guess" -version = "2.0.4" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" dependencies = [ "mime", "unicase", @@ -556,22 +1506,115 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.7.4" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ - "adler", + "adler2", ] [[package]] name = "mio" -version = "0.8.11" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ + "hermit-abi", "libc", "wasi", - "windows-sys 0.48.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "mongodb" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c857d71f918b38221baf2fdff7207fec9984b4504901544772b1edf0302d669f" +dependencies = [ + "async-trait", + "base64 0.13.1", + "bitflags 1.3.2", + "bson", + "chrono", + "derivative", + "derive_more", + "futures-core", + "futures-executor", + "futures-io", + "futures-util", + "hex", + "hickory-proto", + "hickory-resolver", + "hmac", + "md-5", + "mongodb-internal-macros", + "once_cell", + "pbkdf2", + "percent-encoding", + "rand", + "rustc_version_runtime", + "rustls 0.21.12", + "rustls-pemfile 1.0.4", + "serde", + "serde_bytes", + "serde_with", + "sha-1", + "sha2", + "socket2", + "stringprep", + "strsim", + "take_mut", + "thiserror", + "tokio", + "tokio-rustls 0.24.1", + "tokio-util", + "typed-builder 0.10.0", + "uuid", + "webpki-roots", +] + +[[package]] +name = "mongodb-internal-macros" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a6dbc533e93429a71c44a14c04547ac783b56d3f22e6c4f12b1b994cf93844e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nonempty" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "303e8749c804ccd6ca3b428de7fe0d86cb86bc7606bc15291f100fd487960bb8" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", ] [[package]] @@ -580,6 +1623,24 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[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-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.16.0" @@ -592,24 +1653,110 @@ dependencies = [ [[package]] name = "object" -version = "0.36.0" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "openssl" +version = "0.10.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" +dependencies = [ + "bitflags 2.6.0", + "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 2.0.82", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[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", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] [[package]] name = "paste" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest", +] + +[[package]] +name = "pem" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" +dependencies = [ + "base64 0.22.1", + "serde", +] [[package]] name = "percent-encoding" @@ -619,22 +1766,22 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project" -version = "1.1.5" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "baf123a161dde1e524adf36f90bc5d8d3462824a9c43553ad07a8183161189ec" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.5" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.82", ] [[package]] @@ -649,6 +1796,18 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[package]] +name = "portable-atomic" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" + [[package]] name = "powerfmt" version = "0.2.0" @@ -657,9 +1816,12 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "proc-macro-hack" @@ -669,22 +1831,34 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.85" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" +checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" dependencies = [ "unicode-ident", ] +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.8.5" @@ -715,11 +1889,20 @@ dependencies = [ "getrandom", ] +[[package]] +name = "redox_syscall" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +dependencies = [ + "bitflags 2.6.0", +] + [[package]] name = "regex" -version = "1.10.5" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", @@ -729,9 +1912,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", @@ -740,9 +1923,77 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "reqwest" +version = "0.12.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.4.6", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.5.0", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile 2.2.0", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-registry", +] + +[[package]] +name = "resolv-conf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" +dependencies = [ + "hostname", + "quick-error", +] + +[[package]] +name = "ring" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] [[package]] name = "rustc-demangle" @@ -756,15 +2007,171 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ - "semver", + "semver 0.9.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver 1.0.23", +] + +[[package]] +name = "rustc_version_runtime" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dd18cd2bae1820af0b6ad5e54f4a51d0f3fcc53b05f845675074efcc7af071d" +dependencies = [ + "rustc_version 0.4.1", + "semver 1.0.23", +] + +[[package]] +name = "rustix" +version = "0.38.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki 0.101.7", + "sct", +] + +[[package]] +name = "rustls" +version = "0.23.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fbb44d7acc4e873d613422379f69f237a1b141928c02f6bc6ccfddddc2d7993" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki 0.102.8", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", ] +[[package]] +name = "rustversion" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" + [[package]] name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "schannel" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" +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 = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "0.9.0" @@ -774,6 +2181,12 @@ dependencies = [ "semver-parser", ] +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + [[package]] name = "semver-parser" version = "0.7.0" @@ -782,35 +2195,119 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.203" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] +[[package]] +name = "serde-querystring" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce88f8e75d0920545b35b78775e0927137337401f65b01326ce299734885fd21" +dependencies = [ + "lexical", + "serde", +] + +[[package]] +name = "serde_bytes" +version = "0.11.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" -version = "1.0.203" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.82", ] [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.132" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +dependencies = [ + "indexmap 2.6.0", + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ + "form_urlencoded", "itoa", "ryu", "serde", ] +[[package]] +name = "serde_with" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.6.0", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time 0.3.36", +] + +[[package]] +name = "serde_with_macros" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "sha-1" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha1" version = "0.6.1" @@ -822,9 +2319,47 @@ dependencies = [ [[package]] name = "sha1_smol" -version = "1.0.0" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "simple_asn1" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror", + "time 0.3.36", +] [[package]] name = "slab" @@ -835,6 +2370,33 @@ dependencies = [ "autocfg", ] +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "snafu" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "223891c85e2a29c3fe8fb900c1fae5e69c2e42415e3177752e8718475efa5019" +dependencies = [ + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c3c6b7927ffe7ecaa769ee0e3994da3b8cafc8f444578982c83ecb161af917" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.82", +] + [[package]] name = "socket2" version = "0.5.7" @@ -845,6 +2407,12 @@ 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" + [[package]] name = "standback" version = "0.2.17" @@ -854,6 +2422,12 @@ 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" @@ -861,7 +2435,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" dependencies = [ "discard", - "rustc_version", + "rustc_version 0.2.3", "stdweb-derive", "stdweb-internal-macros", "stdweb-internal-runtime", @@ -898,10 +2472,33 @@ dependencies = [ ] [[package]] -name = "stdweb-internal-runtime" +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.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] + +[[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" @@ -916,33 +2513,94 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.66" +version = "2.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +checksum = "83540f837a8afc019423a8edb95b52a8effe46957ee402287f4292fae35be021" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +dependencies = [ + "futures-core", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags 2.6.0", + "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 = "take_mut" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.82", ] [[package]] @@ -1014,40 +2672,122 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" -version = "1.38.0" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" dependencies = [ "backtrace", "bytes", "libc", "mio", - "num_cpus", + "parking_lot", "pin-project-lite", + "signal-hook-registry", "socket2", - "windows-sys 0.48.0", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + +[[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.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls 0.21.12", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls 0.23.15", + "rustls-pki-types", + "tokio", ] [[package]] name = "tokio-util" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" dependencies = [ "bytes", "futures-core", + "futures-io", "futures-sink", "pin-project-lite", "tokio", ] +[[package]] +name = "tower" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 0.1.2", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" @@ -1055,10 +2795,23 @@ version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ + "log", "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + [[package]] name = "tracing-core" version = "0.1.32" @@ -1068,6 +2821,16 @@ dependencies = [ "once_cell", ] +[[package]] +name = "try-again" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1006cfd66eadaaa9ec7a3d011336e40b98cd6c4d8f7b1e331080f6145ac00671" +dependencies = [ + "tokio", + "tracing", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -1075,19 +2838,74 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] -name = "unicase" -version = "2.7.0" +name = "typed-builder" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +checksum = "89851716b67b937e393b3daa8423e67ddfc4bbbf1654bcf05488e95e0828db0c" dependencies = [ - "version_check", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "typed-builder" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e14ed59dc8b7b26cacb2a92bad2e8b1f098806063898ab42a3bd121d7d45e75" +dependencies = [ + "typed-builder-macro", ] +[[package]] +name = "typed-builder-macro" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "560b82d656506509d43abe30e0ba64c56b1953ab3d4fe7ba5902747a7a3cedd5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicase" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" + +[[package]] +name = "unicode-bidi" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" + [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[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 = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" [[package]] name = "unindent" @@ -1095,6 +2913,23 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna 0.5.0", + "percent-encoding", +] + [[package]] name = "utf8parse" version = "0.2.2" @@ -1103,18 +2938,25 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.8.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" dependencies = [ "getrandom", + "serde", ] +[[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.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "want" @@ -1133,34 +2975,47 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.82", "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1168,22 +3023,44 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.82", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" + +[[package]] +name = "web-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + +[[package]] +name = "widestring" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" [[package]] name = "winapi" @@ -1207,6 +3084,45 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -1222,7 +3138,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "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]] @@ -1242,18 +3167,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "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.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "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]] @@ -1264,9 +3189,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -1276,9 +3201,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -1288,15 +3213,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -1306,9 +3231,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -1318,9 +3243,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -1330,9 +3255,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -1342,18 +3267,73 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winreg" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] [[package]] name = "wipac-disk-tracking" -version = "0.2.2" +version = "0.4.0" dependencies = [ + "axum", + "axum-extra", + "axum-keycloak-auth", + "chrono", "env_logger", + "futures-util", "gotham", "gotham_restful", "log", + "mongodb", "serde", "serde_json", + "time 0.3.36", + "tokio", + "uuid", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", ] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/Cargo.toml b/Cargo.toml index f60bf59..c3d90cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,13 +1,26 @@ [package] name = "wipac-disk-tracking" -version = "0.2.2" +version = "0.4.0" edition = "2021" publish = false [dependencies] -env_logger = "0.11.3" +axum = "0.7.7" +axum-extra = { version = "0.9.4", features = [ "erased-json" ] } +axum-keycloak-auth = "0.6.0" +chrono = "0.4.38" +env_logger = "0.11.5" +futures-util = "0.3.31" gotham = "0.7.4" gotham_restful = "0.9.0" -log = "0.4.21" -serde = "1.0.203" -serde_json = "1.0.117" +log = "0.4.22" +mongodb = "3.1.0" +serde = "1.0.210" +serde_json = "1.0.132" +time = "0.3.36" +tokio = { version = "1.40.0", features = [ "full" ] } +uuid = { version = "1.11.0", features = [ "serde", "v4" ] } + +[lib] +name = "wipac_disk_tracking" +path = "src/lib.rs" diff --git a/Dockerfile b/Dockerfile index 0f016f8..27ddb29 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,6 +5,8 @@ #---------------------------------------------------------------------------- FROM rust:slim-bookworm AS build +RUN apt-get update && apt-get install -y libssl-dev pkg-config build-essential + COPY Cargo.lock Cargo.toml /build/ COPY src /build/src diff --git a/README.md b/README.md index 7403179..f96e7d7 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,110 @@ # wipac-disk-tracking Archival media tracking service for WIPAC -## API -The REST API for the wipac-disk-tracking service is documented below. +## Routes +The following routes are supported in the wipac-disk-tracking service: -The API is rooted at: +### API / +These routes live at the root of the service. - /api/v# - -Where # is the version number of the API under use. +#### GET /health +This route allows a client to query the health of the service. Mostly, +this is just a check if the service can access the database that stores +the disk events. This route requires no authentication, and can be +checked with a simple `curl` command: -### Disks -Disks represent a unique archival disk. -While the disk entity does carry some immutable identifying information, it -is mostly a container for archival disk events. + curl -v http://localhost:8080/health && echo "" -#### Routes -These routes are implemented to work with disks: +A healthy response is 200, along with some JSON indicating the status +of the service. Here `count` is a simple count of disk events stored +in the backing database: - GET /disks/:disk_id Get the data for a given disk - GET /disks/:disk_id/events Get all of the events for a given disk - GET /disks/:disk_id/events/:event_id Get the data for a given event for a given disk - GET /disks/:disk_id/search?query Find a disk based on a key-value query + { + "status": "ok", + "count": 0 + } -### Events -Events represent a record of an event involving an archival disk. -There are four distinct events that are tracked by the system. +An unhealthy response is 500, along with some JSON indicating the error +the service is experiencing connecting to the database: - sighted This disk was observed to be loaded in a host that processes archival disks - formatted This disk was given a file system to make it ready for archival purposes - opened This disk was given a label and was designated for active archival activity - closed This disk was determined to full/finished and archival activity stopped + { + "status": "error", + "message": "Failed to connect to the database: MongoError(\"Kind: Server selection timeout: No available servers. Topology: { Type: Single, Servers: [ { Address: localhost:27017, Type: Unknown, Error: Kind: I/O error: Connection refused (os error 111), labels: {} } ] }, labels: {}\")" + } -The format for each of these events is specified with a JSON Schema file. +#### GET /token +This route allows a client to query the service's view of its token +issued by Keycloak for authorization. This route exists for debugging +purposes. -#### Routes -These routes are implemented to work with events: +The client can also inspect its token in a service like [jwt.io](https://jwt.io). - POST /events Create a new event - GET /events/:event_id Get the data for a given event - GET /events/search?query Find an event based on key-value query +This route offers a view of 'how did the service parse my token' or +'how does the service see my authorization grants'? + +### API /api/v1 +These routes live under /api/v1. + +#### GET /events/:event_id +Get the data for a given event. This route is actually multi-purpose. + +If you specify the UUID assigned to an event, you can find the record +for a specific event: + + GET /api/v1/events/0badfdd0-963a-4b25-9af6-1acc52c5d334 + +If you specify the serial number of a disk, you can find the records of +every event referencing that serial number: + + GET /api/v1/events/ZRS1NWBL + +In both cases, a successful query will return with the structure like +this one: + + { + "events": [ + { + smartctl-event-here + }, + ... + { + smartctl-event-here + } + ] + } + +#### POST /events/closed +Create a new 'closed' event. This disk was determined to full/finished +and archival activity stopped. + +#### POST /events/formatted +Create a new 'formatted' event. This disk was given a file system to +make it ready for archival purposes. + +#### POST /events/opened +Create a new 'opened' event. This disk was given a label and was +designated for active archival activity. + +#### POST /events/sighted +Create a new 'sighted' event. This disk was observed to be loaded in a +host that processes archival disks. + +## Development +As typical in a Rust project, you can run the unit and integration tests with: + + cargo test -- --show-output + +### MongoDB Tests +The MongoDB integration test will look for the temporary MongoDB container +running locally. If you'd rather run the test against a different MongoDB, +you can supply the URL through this environment variable: + + DESTRUCTIVE_TEST_MONGODB_URL + +Note that the integration test is destructive. It may wipe out your collections, +indexes, or databases! DON'T point this at any data that you want to keep! + +If MongoDB is not available, locally or through the provided URL, then a +message will appear in the output of the tests: + + "MongoDB is not available. Skipping test." diff --git a/bin/build-docker b/bin/build-docker index 00f227d..4e663ea 100755 --- a/bin/build-docker +++ b/bin/build-docker @@ -1,7 +1,13 @@ #!/usr/bin/env bash # build-docker +# use `docker buildx build` to create a Docker image of the application +# by default the image is `wipac-disk-tracking:latest-SNAPSHOT` unless +# you set the BUILD_TAG environment variable before calling the script + +: ${BUILD_TAG:="latest-SNAPSHOT"} + docker buildx build \ --file Dockerfile \ - --tag wipac-disk-tracking:latest-SNAPSHOT \ + --tag wipac-disk-tracking:${BUILD_TAG} \ . diff --git a/bin/run-app b/bin/run-app new file mode 100755 index 0000000..b601e94 --- /dev/null +++ b/bin/run-app @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# run-app + +# use `cargo run` to run an instance of the application locally +# when starting this script, you can provide one of the environment +# variables listed below to override the defaults + +export MONGODB_DATABASE=${MONGODB_DATABASE:="disk_tracking"} +export MONGODB_HOSTNAME=${MONGODB_HOSTNAME:="localhost"} +export MONGODB_PASSWORD=${MONGODB_PASSWORD:="hunter2"} +export MONGODB_PORT_NUMBER=${MONGODB_PORT_NUMBER:="27017"} +export MONGODB_TIMEOUT_SECS=${MONGODB_TIMEOUT_SECS:="5"} +export MONGODB_USERNAME=${MONGODB_USERNAME:="disk_tracking"} +export OAUTH_AUDIENCE=${OAUTH_AUDIENCE:="disk-tracking"} +export OAUTH_REALM=${OAUTH_REALM:="IceCube"} +export OAUTH_URL=${OAUTH_URL:="https://keycloak.icecube.wisc.edu/auth"} +export PORT=${PORT:="8080"} +export RUST_LOG=${RUST_LOG:="debug"} + +cargo run --bin wipac-disk-tracking diff --git a/bin/run-app-docker b/bin/run-app-docker new file mode 100755 index 0000000..16eb0f1 --- /dev/null +++ b/bin/run-app-docker @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +# run-app-docker + +# use `docker run` to run an instance of the application in a Docker +# container. when starting this script, you can provide one of the +# environment variables listed below to override the defaults + +: ${DOCKER_CONTAINER_NAME:="disk_tracking"} +: ${DOCKER_MONGO_CONTAINER_NAME:="disk_tracking_mongo"} +: ${MONGODB_DATABASE:="disk_tracking"} +: ${MONGODB_HOSTNAME:="localhost"} +: ${MONGODB_PASSWORD:="hunter2"} +: ${MONGODB_TIMEOUT_SECS:="5"} +: ${MONGODB_USERNAME:="disk_tracking"} +: ${MONGODB_PORT_NUMBER:="27017"} +: ${OAUTH_AUDIENCE:="disk-tracking"} +: ${OAUTH_REALM:="IceCube"} +: ${OAUTH_URL:="https://keycloak.icecube.wisc.edu/auth"} +: ${PORT:="8080"} + +docker run \ + --detach \ + --env=MONGODB_DATABASE=${MONGODB_DATABASE} \ + --env=MONGODB_HOSTNAME=${DOCKER_MONGO_CONTAINER_NAME} \ + --env=MONGODB_PASSWORD=${MONGODB_PASSWORD} \ + --env=MONGODB_PORT_NUMBER=${MONGODB_PORT_NUMBER} \ + --env=MONGODB_TIMEOUT_SECS=${MONGODB_TIMEOUT_SECS} \ + --env=MONGODB_USERNAME=${MONGODB_USERNAME} \ + --env=OAUTH_AUDIENCE=${OAUTH_AUDIENCE} \ + --env=OAUTH_URL=${OAUTH_URL} \ + --env=PORT=${PORT} \ + --env=RUST_LOG=debug \ + --link ${DOCKER_MONGO_CONTAINER_NAME}:${DOCKER_MONGO_CONTAINER_NAME} \ + --name=${DOCKER_CONTAINER_NAME} \ + --publish ${PORT}:${PORT} \ + --rm \ + wipac-disk-tracking:latest-SNAPSHOT diff --git a/bin/run-docker b/bin/run-docker deleted file mode 100755 index 8d21959..0000000 --- a/bin/run-docker +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash -# run-docker - -docker run \ - --detach \ - --env=RUST_LOG=debug \ - --name=wipac_disk_tracking \ - --publish 8080:8080 \ - --rm \ - wipac-disk-tracking:latest-SNAPSHOT diff --git a/bin/run-mongo-docker b/bin/run-mongo-docker new file mode 100755 index 0000000..13cb75f --- /dev/null +++ b/bin/run-mongo-docker @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# run-mongo-docker + +# use `docker run` to create a TEMPORARY instance of MongoDB in a +# Docker container for development/testing purposes. when starting +# this script, you can provide one of the environment variables listed +# below to override the defaults + +: ${DOCKER_CONTAINER_NAME:="disk_tracking_mongo"} +: ${MONGODB_DATABASE:="disk_tracking"} +: ${MONGODB_PASSWORD:="hunter2"} +: ${MONGODB_USERNAME:="disk_tracking"} +: ${MONGODB_PORT_NUMBER:="27017"} + +docker run \ + --detach \ + --env=MONGODB_DATABASE=${MONGODB_DATABASE} \ + --env=MONGODB_PASSWORD=${MONGODB_PASSWORD} \ + --env=MONGODB_ROOT_PASSWORD=hunter2 \ + --env=MONGODB_USERNAME=${MONGODB_USERNAME} \ + --env=MONGODB_PORT_NUMBER=${MONGODB_PORT_NUMBER} \ + --name=${DOCKER_CONTAINER_NAME} \ + --publish ${MONGODB_PORT_NUMBER}:${MONGODB_PORT_NUMBER} \ + --rm \ + bitnami/mongodb:latest diff --git a/bin/run-mongo-shell-docker b/bin/run-mongo-shell-docker new file mode 100755 index 0000000..4609ae3 --- /dev/null +++ b/bin/run-mongo-shell-docker @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# run-mongo-shell-docker + +# use `docker run` to create an interactive mongoshell that connects to +# the TEMPORARY instance of MongoDB for development/testing purposes. +# when starting this script, you can provide one of the environment +# variables listed below to override the defaults + +: ${MONGODB_DATABASE:="disk_tracking"} +: ${MONGODB_HOSTNAME:="localhost"} +: ${MONGODB_PASSWORD:="hunter2"} +: ${MONGODB_USERNAME:="disk_tracking"} +: ${MONGODB_PORT_NUMBER:="27017"} + +docker run \ + --interactive \ + --network="host" \ + --rm \ + --tty \ + mongo:latest \ + mongosh --host ${MONGODB_HOSTNAME}:${MONGODB_PORT_NUMBER} -u "${MONGODB_USERNAME}" -p "${MONGODB_PASSWORD}" --authenticationDatabase "${MONGODB_DATABASE}" diff --git a/client/README.md b/client/README.md new file mode 100644 index 0000000..e7b9841 --- /dev/null +++ b/client/README.md @@ -0,0 +1,31 @@ +# README.md +wipac-disk-tracking example client (auf Python) + +## Installation +You should first create a Python virtual environment: + + python3 -m venv env + +Then you should activate the Python virtual environment: + + source env/bin/activate + +Then you should install the libraries required by the client: + + pip3 install -r requirements.txt + +Finally, you'll need to create the file `keycloak-client-secret` with the client secret. + + +## Usage +When run, the client will: + +- display information about the keycloak token +- get the /health route to check if the service is healthy +- post an example event to the wipac-disk-tracking service +- get the /health route to check if the service is healthy +- get and display the example event posted to the service + +You can run the client with a helper script: + + client.sh diff --git a/client/client.py b/client/client.py new file mode 100644 index 0000000..6f4e46f --- /dev/null +++ b/client/client.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +# client.py + +import asyncio +import json +import logging +import os +from typing import cast, Dict + +from rest_tools.client import ClientCredentialsAuth, RestClient + + +async def main() -> None: + config: Dict[str, str] = { + "BASE_URL": os.getenv("BASE_URL", None), + "CLIENT_ID": os.getenv("CLIENT_ID", None), + "CLIENT_SECRET": os.getenv("CLIENT_SECRET", None), + "OAUTH_URL": os.getenv("OAUTH_URL", None), + "REST_URL": os.getenv("REST_URL", None), + } + + with open('smartctl.json', 'r') as file: + data = json.load(file) + + # http://localhost:8080/ + base_rc: RestClient = RestClient(config["BASE_URL"]) + + # http://localhost:8080/ + Keycloak Credentials + token_rc: RestClient = ClientCredentialsAuth(address=cast(str, config["BASE_URL"]), + token_url=cast(str, config["OAUTH_URL"]), + client_id=cast(str, config["CLIENT_ID"]), + client_secret=cast(str, config["CLIENT_SECRET"])) + + # http://localhost:8080/api/v1 + Keycloak Credentials + rest_rc: RestClient = ClientCredentialsAuth(address=cast(str, config["REST_URL"]), + token_url=cast(str, config["OAUTH_URL"]), + client_id=cast(str, config["CLIENT_ID"]), + client_secret=cast(str, config["CLIENT_SECRET"])) + + response = await token_rc.request("GET", "/token") + print(response, end="\n\n") + + response = await base_rc.request("GET", "/health") + print(response, end="\n\n") + + response = await rest_rc.request("POST", "/events/sighted", data) + print(response, end="\n\n") + + response = await base_rc.request("GET", "/health") + print(response, end="\n\n") + + response = await rest_rc.request("GET", "/events/ZRS1NWBL") + print(response, end="\n\n") + +if __name__ == '__main__': + logging.basicConfig(level=logging.DEBUG) + asyncio.run(main()) diff --git a/client/client.sh b/client/client.sh new file mode 100755 index 0000000..58b3700 --- /dev/null +++ b/client/client.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +# run the testing client + +export BASE_URL=${HEALTH_URL:="http://localhost:8080"} +export CLIENT_ID=${CLIENT_ID:="long-term-archive"} # you may need a different ID +export CLIENT_SECRET=${CLIENT_SECRET:="$( Result { + // read database connection parameters from the environment + let mongo_user = env::var("MONGODB_USERNAME").unwrap_or_else(|_| "disk_tracking".into()); + let mongo_password = match env::var("MONGODB_PASSWORD") { + Err(_) => { + return Err(ContextError( + "environment variable MONGODB_PASSWORD not defined".to_string(), + )) + } + Ok(x) => x, + }; + let mongo_host = env::var("MONGODB_HOSTNAME").unwrap_or_else(|_| "localhost".into()); + let mongo_port = env::var("MONGODB_PORT_NUMBER").unwrap_or_else(|_| "27017".into()); + let mongo_database = env::var("MONGODB_DATABASE").unwrap_or_else(|_| "disk_tracking".into()); + + // read oauth configuration from the environment + let oauth_audience = env::var("OAUTH_AUDIENCE").unwrap_or_else(|_| "disk-tracking".into()); + let oauth_realm = env::var("OAUTH_REALM").unwrap_or_else(|_| "IceCube".into()); + let oauth_url = + env::var("OAUTH_URL").unwrap_or_else(|_| "https://keycloak.icecube.wisc.edu/auth".into()); + + // read application port from the environment + let port_str = env::var("PORT").unwrap_or_else(|_| "8080".into()); + let port = match port_str.parse::() { + Err(_) => { + return Err(ContextError(format!( + "environment variable PORT contains invalid value: {}", + port_str + ))) + } + Ok(x) => x, + }; + + // read database connection timeout from the environment + let mongodb_timeout_secs_str = env::var("MONGODB_TIMEOUT_SECS").unwrap_or_else(|_| "30".into()); + let mongodb_timeout_secs = match mongodb_timeout_secs_str.parse::() { + Err(_) => { + return Err(ContextError(format!( + "environment variable MONGODB_TIMEOUT_SECS contains invalid value: {}", + mongodb_timeout_secs_str + ))) + } + Ok(x) => x, + }; + + // build the database client + let conn_str = format!( + "mongodb://{}:{}@{}:{}/{}", + mongo_user, mongo_password, mongo_host, mongo_port, mongo_database + ); + let mut client_options = ClientOptions::parse(conn_str).await?; + client_options.connect_timeout = Some(Duration::from_secs(mongodb_timeout_secs)); + client_options.server_selection_timeout = Some(Duration::from_secs(mongodb_timeout_secs)); + let mongo_client = Client::with_options(client_options)?; + + // return the application context to the caller + Ok(ApplicationContext { + mongo_client, + mongo_database, + mongo_host, + mongo_password, + mongo_port, + mongo_user, + oauth_audience, + oauth_realm, + oauth_url, + port, + }) +} + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- + +#[cfg(test)] +mod tests { + // use super::*; + + #[test] + fn test_always_succeed() { + assert!(true); + } +} diff --git a/src/database.rs b/src/database.rs new file mode 100644 index 0000000..0be3fb4 --- /dev/null +++ b/src/database.rs @@ -0,0 +1,87 @@ +// database.rs + +use futures_util::stream::TryStreamExt; +use mongodb::{ + bson::{doc, document::Document}, + results::InsertOneResult, +}; +use uuid::Uuid; + +use crate::context::ApplicationContext; +use crate::error::Result; +use crate::event::DiskEvent; + +pub const DISK_EVENTS_COLLECTION: &str = "disk_events"; + +pub async fn count_disk_events(context: &ApplicationContext) -> Result { + let client = &context.mongo_client; + let database = client.database(&context.mongo_database); + let collection = database.collection::(DISK_EVENTS_COLLECTION); + + let count = collection.count_documents(Document::new()).await?; + + Ok(count) +} + +pub async fn find_disk_events_uuid( + context: &ApplicationContext, + uuid: Uuid, +) -> Result> { + let client = &context.mongo_client; + let database = client.database(&context.mongo_database); + let collection = database.collection::(DISK_EVENTS_COLLECTION); + let filter = doc! { "uuid": uuid.to_string() }; + + let mut cursor = collection.find(filter).await?; + let mut result = Vec::new(); + while let Some(doc) = cursor.try_next().await? { + result.push(doc); + } + + Ok(result) +} + +pub async fn find_disk_events_serial_number( + context: &ApplicationContext, + serial_number: String, +) -> Result> { + let client = &context.mongo_client; + let database = client.database(&context.mongo_database); + let collection = database.collection::(DISK_EVENTS_COLLECTION); + let filter = doc! { "serial_number": serial_number }; + + let mut cursor = collection.find(filter).await?; + let mut result = Vec::new(); + while let Some(doc) = cursor.try_next().await? { + result.push(doc); + } + + Ok(result) +} + +pub async fn save_disk_event( + context: &ApplicationContext, + disk_event: DiskEvent, +) -> Result { + let client = &context.mongo_client; + let database = client.database(&context.mongo_database); + let collection = database.collection::(DISK_EVENTS_COLLECTION); + + let result = collection.insert_one(disk_event).await?; + + Ok(result) +} + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- + +#[cfg(test)] +mod tests { + // use super::*; + + #[test] + fn test_always_succeed() { + assert!(true); + } +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..ae275d2 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,50 @@ +// error.rs + +#[derive(Debug)] +pub enum ApplicationError { + AuthError(String), + ContextError(String), + MongoError(String), +} + +impl From for ApplicationError { + fn from(error: std::env::VarError) -> Self { + ApplicationError::ContextError(error.to_string()) + } +} + +impl From for ApplicationError { + fn from(error: mongodb::error::Error) -> Self { + ApplicationError::MongoError(error.to_string()) + } +} + +pub type Result = std::result::Result; + +pub fn get_error_message(e: ApplicationError) -> String { + match e { + ApplicationError::AuthError(x) => { + format!("disk-tracking: OAuth Error: {x}") + } + ApplicationError::ContextError(x) => { + format!("disk-tracking: Unable to build application context: {x}") + } + ApplicationError::MongoError(x) => { + format!("disk-tracking: Database error: {x}") + } + } +} + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- + +#[cfg(test)] +mod tests { + // use super::*; + + #[test] + fn test_always_succeed() { + assert!(true); + } +} diff --git a/src/event.rs b/src/event.rs new file mode 100644 index 0000000..d221cb3 --- /dev/null +++ b/src/event.rs @@ -0,0 +1,99 @@ +// event.rs + +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +use crate::smartctl::SmartCtl; + +#[derive(Debug, Deserialize, Serialize)] +#[serde(rename_all = "lowercase")] +pub enum Event { + /// this disk was determined to full/finished and archival activity stopped + CLOSED, + /// this disk was given a file system to make it ready for archival purposes + FORMATTED, + /// this disk was given a label and was designated for active archival activity + OPENED, + /// this disk was observed to be loaded in a host that processes archival disks + SIGHTED, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct DiskEvent { + /// unique identifier for this DiskEvent + #[serde(serialize_with = "uuid_to_string", deserialize_with = "string_to_uuid")] + pub uuid: Uuid, + /// iso-8601 timestamp when this DiskEvent was created + pub date_created: String, + /// hardware serial number of the disk this DiskEvent is about + pub serial_number: String, + /// the type of event this DiskEvent describes + pub event: Event, + /// the output from the `smartctl` command for this DiskEvent + pub smartctl: SmartCtl, +} + +fn uuid_to_string(uuid: &Uuid, serializer: S) -> Result +where + S: serde::Serializer, +{ + serializer.serialize_str(&uuid.to_string()) +} + +fn string_to_uuid<'de, D>(deserializer: D) -> Result +where + D: serde::Deserializer<'de>, +{ + let s = String::deserialize(deserializer)?; + Uuid::parse_str(&s).map_err(serde::de::Error::custom) +} + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + #[test] + fn test_always_succeed() { + assert!(true); + } + + #[test] + fn test_serialization() { + // create a uuid + let test_uuid = Uuid::new_v4(); + + // create a disk event + let disk_event = DiskEvent { + uuid: test_uuid, + date_created: "2024-08-14T17:36:31Z".to_string(), + serial_number: "ZRS1NWBL".to_string(), + event: Event::FORMATTED, + smartctl: SmartCtl(json!({ + "serial_number": "ZRS1NWBL" + })), + }; + + // serialize it to JSON + let serialized = serde_json::to_string(&disk_event).unwrap(); + + // expected JSON representation + let expected_json = json!({ + "uuid": test_uuid.to_string(), + "date_created": "2024-08-14T17:36:31Z", + "serial_number": "ZRS1NWBL", + "event": "formatted", + "smartctl": { + "serial_number": "ZRS1NWBL" + } + }) + .to_string(); + + // assert that the serialized JSON matches the expected JSON + assert_eq!(serialized, expected_json); + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..b581add --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,7 @@ +// lib.rs + +pub mod context; +pub mod database; +pub mod error; +pub mod event; +pub mod smartctl; diff --git a/src/main.rs b/src/main.rs index e006e56..57192da 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,62 +1,54 @@ // main.rs +pub mod context; +pub mod database; +pub mod error; +pub mod event; +pub mod middleware; pub mod routes; +pub mod smartctl; -use gotham::{ - router::{build_simple_router, builder::DrawRoutes, Router}, - state::State, -}; -use log::info; +use axum_keycloak_auth::{instance::KeycloakAuthInstance, instance::KeycloakConfig, Url}; +use log::{error, info}; +use std::net::SocketAddr; +use tokio::net::TcpListener; -const HELLO_WORLD: &str = "Hello World!\n"; +use context::build_context; +use error::{get_error_message, Result}; +use routes::build_router; -pub fn say_hello(state: State) -> (State, &'static str) { - (state, HELLO_WORLD) -} - -fn build_router() -> Router { - build_simple_router(|route| { - route.delegate("/api/v1").to_router(routes::v1::router()); - }) -} - -pub fn main() { +#[tokio::main] +async fn main() { // initialize logging, configured by environment env_logger::init(); - // start the service - let addr = "0.0.0.0:8080"; - info!("Listening for requests at http://{}", addr); - // gotham::start(addr, || Ok(say_hello)).unwrap(); - let _ = gotham::start(addr, build_router()); -} - -// -------------------------------------------------------------------------------------------------------------------- -// -------------------------------------------------------------------------------------------------------------------- -// -------------------------------------------------------------------------------------------------------------------- - -#[cfg(test)] -mod tests { - use super::*; - use gotham::hyper::StatusCode; - use gotham::test::TestServer; - - #[test] - fn test_always_succeed() { - assert_eq!(true, true) + // run the application, report any errors + if let Err(e) = do_main().await { + error!("Error: {}", get_error_message(e)) } +} - #[test] - fn test_say_hello() { - let test_server = TestServer::new(|| Ok(say_hello)).unwrap(); - let response = test_server - .client() - .get("http://localhost") - .perform() - .unwrap(); - - assert_eq!(response.status(), StatusCode::OK); - - let body = response.read_utf8_body().unwrap(); - assert_eq!(body, HELLO_WORLD); - } +async fn do_main() -> Result<()> { + // set up the context of the application + let context = build_context() + .await + .expect("Unable to build application context"); + // set up keycloak instance for authentication + let keycloak_auth_instance = KeycloakAuthInstance::new( + KeycloakConfig::builder() + .server(Url::parse(&context.oauth_url).unwrap()) + .realm(String::from(&context.oauth_realm)) + .build(), + ); + // establish our listening port + let listener = TcpListener::bind(format!("0.0.0.0:{}", context.port)) + .await + .unwrap_or_else(|_| panic!("Unable to listen on port {}", context.port)); + // build the application router + let app = build_router(context, keycloak_auth_instance.into()) + .into_make_service_with_connect_info::(); + // start the disk tracking service + info!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); + // tell the caller that there were no errors + Ok(()) } diff --git a/src/middleware.rs b/src/middleware.rs new file mode 100644 index 0000000..3df106f --- /dev/null +++ b/src/middleware.rs @@ -0,0 +1,49 @@ +// middleware.rs + +use axum::{ + extract::ConnectInfo, extract::Request, http::StatusCode, middleware::Next, response::Response, +}; +use core::net::SocketAddr; +use log::{debug, info}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct EmptyExtra; + +pub async fn log_request( + ConnectInfo(addr): ConnectInfo, + req: Request, + next: Next, +) -> Result { + // if we can't figure out the IP address, then we don't know it + let mut client_ip = addr.ip().to_string(); + + // try to get the IP address from the request headers + if let Some(header) = req.headers().get("x-forwarded-for") { + if let Ok(ip) = header.to_str() { + client_ip = ip.to_string(); + } + } + + // log about it + info!("{} {} {}", client_ip, req.method(), req.uri()); + + // call the next middleware in the chain + Ok(next.run(req).await) +} + +pub async fn log_token(req: Request, next: Next) -> Result { + // if we've got an 'Authorization' header in the request + if let Some(header) = req.headers().get("Authorization") { + // and we can obtain the value of that header as a string slice + if let Ok(token) = header.to_str() { + // log about it, and continue in the request processing stack + debug!("Authorization: {}", token); + return Ok(next.run(req).await); + } + } + + // log about the fact that no token was found + debug!("Authorization: None"); + Ok(next.run(req).await) +} diff --git a/src/routes.rs b/src/routes.rs index c7d5b3b..d1b9426 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -1,3 +1,73 @@ // routes.rs +pub mod health; +pub mod token; pub mod v1; + +use axum::{http::StatusCode, middleware, response::IntoResponse, Router}; +use axum_keycloak_auth::instance::KeycloakAuthInstance; +use log::error; +use std::sync::Arc; + +use crate::context::ApplicationContext; +use crate::middleware::{log_request, log_token}; + +pub async fn handle_error(err: E) -> impl IntoResponse +where + E: std::fmt::Display, +{ + eprintln!("Error occurred: {}", err); + error!("Error occurred: {}", err); + ( + StatusCode::INTERNAL_SERVER_ERROR, + "An internal error occurred.", + ) +} + +pub async fn handle_error2() -> impl IntoResponse { + ( + StatusCode::INTERNAL_SERVER_ERROR, + "An internal error occurred", + ) +} + +pub fn build_router(context: ApplicationContext, instance: Arc) -> Router { + Router::new() + .with_state(context.clone()) + .merge(crate::routes::health::build_router(context.clone())) + .merge(crate::routes::token::build_router( + context.clone(), + instance.clone(), + )) + .nest( + "/api/v1", + crate::routes::v1::build_router(context.clone(), instance.clone()), + ) + .layer(middleware::from_fn(log_token)) + .layer(middleware::from_fn(log_request)) +} + +// async fn get_log_error() -> impl IntoResponse { +// let simulated_error = Some("Database on fire!"); + +// if let Some(err_message) = simulated_error { +// error!("An error occurred: {}", err_message); +// return (StatusCode::INTERNAL_SERVER_ERROR, err_message).into_response(); +// } + +// (StatusCode::OK, "Database only smoking; this is fine, everything is fine.").into_response() +// } + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- + +#[cfg(test)] +mod tests { + // use super::*; + + #[test] + fn test_always_succeed() { + assert!(true); + } +} diff --git a/src/routes/health.rs b/src/routes/health.rs new file mode 100644 index 0000000..14535b7 --- /dev/null +++ b/src/routes/health.rs @@ -0,0 +1,45 @@ +// health.rs + +use axum::{extract::State, http::StatusCode, response::IntoResponse, routing::get, Router}; +use axum_extra::response::ErasedJson; +use serde::Serialize; + +use crate::context::ApplicationContext; +use crate::database::count_disk_events; + +#[derive(Serialize)] +struct HealthResponse { + pub status: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub count: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub message: Option, +} + +pub fn build_router(context: ApplicationContext) -> Router { + // build the routes under /health + Router::new() + .route("/health", get(get_health)) + .with_state(context) +} + +pub async fn get_health(context: State) -> impl IntoResponse { + match count_disk_events(&context).await { + Ok(count) => ( + StatusCode::OK, + ErasedJson::pretty(HealthResponse { + status: "ok".to_string(), + count: Some(count), + message: None, + }), + ), + Err(e) => ( + StatusCode::INTERNAL_SERVER_ERROR, + ErasedJson::pretty(HealthResponse { + status: "error".to_string(), + count: None, + message: Some(format!("Failed to connect to the database: {:?}", e)), + }), + ), + } +} diff --git a/src/routes/token.rs b/src/routes/token.rs new file mode 100644 index 0000000..c39124d --- /dev/null +++ b/src/routes/token.rs @@ -0,0 +1,92 @@ +// token.rs + +use axum::{http::StatusCode, response::IntoResponse, routing::get, Extension, Router}; +use axum_extra::response::ErasedJson; +use axum_keycloak_auth::{ + decode::KeycloakToken, instance::KeycloakAuthInstance, layer::KeycloakAuthLayer, + role::KeycloakRole, PassthroughMode, +}; +use serde::Serialize; +use std::sync::Arc; +use time::OffsetDateTime; + +use crate::context::ApplicationContext; +use crate::middleware::EmptyExtra; + +// function to serialize `OffsetDateTime` as a Unix timestamp +fn serialize_offset_date_time_as_unix_timestamp( + datetime: &OffsetDateTime, + serializer: S, +) -> Result +where + S: serde::Serializer, +{ + let unix_timestamp = datetime.unix_timestamp(); + serializer.serialize_i64(unix_timestamp) +} + +#[derive(Serialize)] +pub struct SerializableToken { + /// Expiration time (UTC). + #[serde(serialize_with = "serialize_offset_date_time_as_unix_timestamp")] + pub expires_at: OffsetDateTime, + /// Issued at time (UTC). + #[serde(serialize_with = "serialize_offset_date_time_as_unix_timestamp")] + pub issued_at: OffsetDateTime, + /// JWT ID (unique identifier for this token). + pub jwt_id: String, + /// Issuer (who created and signed this token). + pub issuer: String, + /// Audience (who or what the token is intended for). + pub audience: Vec, + /// Subject (whom the token refers to). This is the UUID which uniquely identifies this user inside Keycloak. + pub subject: String, + /// Authorized party (the party to which this token was issued). + pub authorized_party: String, + // Keycloak: Roles of the user. + pub roles: Vec>, + // addtional fields (actually, no additional fields) + pub extra: EmptyExtra, +} + +impl From> for SerializableToken { + fn from(token: KeycloakToken) -> Self { + SerializableToken { + expires_at: token.expires_at, + issued_at: token.issued_at, + jwt_id: token.jwt_id, + issuer: token.issuer, + audience: token.audience, + subject: token.subject, + authorized_party: token.authorized_party, + roles: token.roles, + extra: EmptyExtra, + } + } +} + +pub fn build_router(context: ApplicationContext, instance: Arc) -> Router { + // save the audience + let oauth_audience = context.oauth_audience.clone(); + + // build the routes under /token + Router::new() + .route("/token", get(get_token)) + .with_state(context) + .layer( + KeycloakAuthLayer::::builder() + .instance(instance) + .passthrough_mode(PassthroughMode::Block) + .persist_raw_claims(false) + .expected_audiences(vec![oauth_audience]) + .required_roles(vec![String::from("system")]) + .build(), + ) +} + +pub async fn get_token( + Extension(token): Extension>, +) -> impl IntoResponse { + let output_token: SerializableToken = token.into(); + (StatusCode::OK, ErasedJson::pretty(output_token)) +} diff --git a/src/routes/v1.rs b/src/routes/v1.rs index 6dd23ce..6a63e98 100644 --- a/src/routes/v1.rs +++ b/src/routes/v1.rs @@ -3,15 +3,40 @@ pub mod disks; pub mod events; -pub use disks::{DiskResource, DiskResources}; -pub use events::{EventResource, EventResources}; +use axum::{ + routing::{get, post}, + Router, +}; +use axum_keycloak_auth::{ + instance::KeycloakAuthInstance, layer::KeycloakAuthLayer, PassthroughMode, +}; +use std::sync::Arc; -use gotham::router::{build_simple_router, Router}; -use gotham_restful::DrawResources; +use crate::context::ApplicationContext; +use crate::middleware::EmptyExtra; +use crate::routes::v1::events::{ + get_events, post_closed, post_formatted, post_opened, post_sighted, +}; -pub fn router() -> Router { - build_simple_router(|route| { - route.resource::("disks"); - route.resource::("events"); - }) +pub fn build_router(context: ApplicationContext, instance: Arc) -> Router { + // save the audience + let oauth_audience = context.oauth_audience.clone(); + + // build the routes under /api/v1 + Router::new() + .route("/events/closed", post(post_closed)) + .route("/events/formatted", post(post_formatted)) + .route("/events/opened", post(post_opened)) + .route("/events/sighted", post(post_sighted)) + .route("/events/:id", get(get_events)) + .with_state(context) + .layer( + KeycloakAuthLayer::::builder() + .instance(instance) + .passthrough_mode(PassthroughMode::Block) + .persist_raw_claims(false) + .expected_audiences(vec![oauth_audience]) + .required_roles(vec![String::from("system")]) + .build(), + ) } diff --git a/src/routes/v1/disks.rs b/src/routes/v1/disks.rs index 403f03b..8a81e73 100644 --- a/src/routes/v1/disks.rs +++ b/src/routes/v1/disks.rs @@ -1,83 +1,3 @@ // disks.rs -use gotham::{router::response::StaticResponseExtender, state::StateData}; -use gotham_restful::{endpoint, gotham::hyper::Method, read, search, Resource, Success}; -use log::info; -use serde::{Deserialize, Serialize}; - -use crate::routes::v1::{EventResource, EventResources}; - -#[derive(Resource, Serialize)] -#[resource(get_disk_by_id)] -#[resource(get_event_by_disk_id_and_event_id)] -#[resource(get_events_by_disk_id)] -#[resource(find_disk_by_query)] -pub struct DiskResource { - pub id: u64, -} - -#[derive(Serialize)] -pub struct DiskResources { - pub disks: Vec, -} - -// -------------------------------------------------------------------------------------------------------------------- - -#[derive(Clone, Deserialize, StateData, StaticResponseExtender)] -struct DiskSearchQuery { - _serial_number: String, -} - -#[search] -fn find_disk_by_query(_query: DiskSearchQuery) -> Success { - info!("find_disk_by_query()"); - DiskResources { disks: Vec::new() }.into() -} - -// -------------------------------------------------------------------------------------------------------------------- - -#[read] -fn get_disk_by_id(disk_id: u64) -> Success { - info!("get_disk_by_id()"); - DiskResource { id: disk_id }.into() -} - -// -------------------------------------------------------------------------------------------------------------------- - -#[derive(Clone, Deserialize, StateData, StaticResponseExtender)] -struct DiskAndEvent { - _disk_id: u64, - event_id: u64, -} - -#[endpoint( - uri = ":disk_id/events/:event_id", - method = "Method::GET", - params = false, - body = false -)] -fn get_event_by_disk_id_and_event_id(disk_and_event: DiskAndEvent) -> Success { - info!("get_event_by_disk_id_and_event_id()"); - EventResource { - id: disk_and_event.event_id, - } - .into() -} - -// -------------------------------------------------------------------------------------------------------------------- - -#[derive(Clone, Deserialize, StateData, StaticResponseExtender)] -struct Disk { - _disk_id: u64, -} - -#[endpoint( - uri = ":disk_id/events", - method = "Method::GET", - params = false, - body = false -)] -fn get_events_by_disk_id(_disk: Disk) -> Success { - info!("get_events_by_disk_id()"); - EventResources { events: Vec::new() }.into() -} +// TODO: Implement more stuff here diff --git a/src/routes/v1/events.rs b/src/routes/v1/events.rs index 82602b0..4923209 100644 --- a/src/routes/v1/events.rs +++ b/src/routes/v1/events.rs @@ -1,53 +1,177 @@ // events.rs -use gotham::{router::response::StaticResponseExtender, state::StateData}; -use gotham_restful::{create, read, search, Resource, Success}; -use log::info; -use serde::{Deserialize, Serialize}; +use axum::{ + extract::{Path, State}, + http::StatusCode, + response::IntoResponse, + Json, +}; +use axum_extra::response::ErasedJson; +use chrono::Utc; +use serde::Serialize; +use uuid::Uuid; -#[derive(Resource, Serialize)] -#[resource(create_event)] -#[resource(find_event_by_query)] -#[resource(get_event_by_id)] -pub struct EventResource { - pub id: u64, +use crate::context::ApplicationContext; +use crate::database::{find_disk_events_serial_number, find_disk_events_uuid, save_disk_event}; +use crate::event::{DiskEvent, Event}; +use crate::smartctl::SmartCtl; + +#[derive(Serialize)] +struct DiskEventCreatedResponse { + pub status: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub message: Option, } #[derive(Serialize)] -pub struct EventResources { - pub events: Vec, +struct DiskEventFindResponse { + pub events: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub message: Option, } -// -------------------------------------------------------------------------------------------------------------------- +pub async fn get_events( + State(context): State, + Path(id): Path, +) -> impl IntoResponse { + // if we can parse the provided id as a UUID + if let Ok(uuid) = Uuid::parse_str(&id) { + // search the database by UUID + let (status, response) = find_events_by_uuid(context, uuid).await; + return (status, ErasedJson::pretty(response)); + } -#[derive(Clone, Deserialize, StateData, StaticResponseExtender)] -struct EventBody { - _serial_number: String, + // look the events up by serial_number + let (status, response) = find_events_by_serial_number(context, id).await; + (status, ErasedJson::pretty(response)) } -#[create] -fn create_event(_body: EventBody) -> Success { - info!("create_event()"); - EventResource { id: 0 }.into() +pub async fn post_closed( + State(context): State, + Json(smartctl): Json, +) -> impl IntoResponse { + let (status, response) = create_event(context, smartctl, Event::CLOSED).await; + (status, ErasedJson::pretty(response)) } -// -------------------------------------------------------------------------------------------------------------------- +pub async fn post_formatted( + State(context): State, + Json(smartctl): Json, +) -> impl IntoResponse { + let (status, response) = create_event(context, smartctl, Event::FORMATTED).await; + (status, ErasedJson::pretty(response)) +} -#[derive(Clone, Deserialize, StateData, StaticResponseExtender)] -struct EventSearchQuery { - _serial_number: String, +pub async fn post_opened( + State(context): State, + Json(smartctl): Json, +) -> impl IntoResponse { + let (status, response) = create_event(context, smartctl, Event::OPENED).await; + (status, ErasedJson::pretty(response)) } -#[search] -fn find_event_by_query(_query: EventSearchQuery) -> Success { - info!("find_event_by_query()"); - EventResources { events: Vec::new() }.into() +pub async fn post_sighted( + State(context): State, + Json(smartctl): Json, +) -> impl IntoResponse { + let (status, response) = create_event(context, smartctl, Event::SIGHTED).await; + (status, ErasedJson::pretty(response)) } -// -------------------------------------------------------------------------------------------------------------------- +async fn create_event( + context: ApplicationContext, + smartctl: SmartCtl, + event: Event, +) -> (StatusCode, DiskEventCreatedResponse) { + // ensure we can extract a serial number + let serial_number = if let Some(serial) = smartctl.get_serial() { + serial + } else { + return ( + StatusCode::UNPROCESSABLE_ENTITY, + DiskEventCreatedResponse { + status: "error".to_string(), + message: Some("provided smartctl data is missing key 'serial_number'".to_string()), + }, + ); + }; + + // extract a creation date, or just use the current one + let date_created = if let Some(date) = smartctl.get_date_created() { + date + } else { + // the user provided smartctl data without a date, so we'll use the current one + Utc::now().to_rfc3339().replace("+00:00", "Z") + }; + + // create the disk event + let disk_event = DiskEvent { + uuid: Uuid::new_v4(), + date_created, + serial_number, + event, + smartctl, + }; + + // store the disk event in the database + match save_disk_event(&context, disk_event).await { + Ok(_) => ( + StatusCode::CREATED, + DiskEventCreatedResponse { + status: "ok".to_string(), + message: None, + }, + ), + Err(e) => ( + StatusCode::INTERNAL_SERVER_ERROR, + DiskEventCreatedResponse { + status: "error".to_string(), + message: Some(format!("{:?}", e)), + }, + ), + } +} + +async fn find_events_by_uuid( + context: ApplicationContext, + uuid: Uuid, +) -> (StatusCode, DiskEventFindResponse) { + match find_disk_events_uuid(&context, uuid).await { + Ok(events) => ( + StatusCode::OK, + DiskEventFindResponse { + events, + message: None, + }, + ), + Err(e) => ( + StatusCode::INTERNAL_SERVER_ERROR, + DiskEventFindResponse { + events: Vec::new(), + message: Some(format!("{:?}", e)), + }, + ), + } +} -#[read] -fn get_event_by_id(id: u64) -> Success { - info!("get_event_by_id()"); - EventResource { id }.into() +async fn find_events_by_serial_number( + context: ApplicationContext, + serial_number: String, +) -> (StatusCode, DiskEventFindResponse) { + match find_disk_events_serial_number(&context, serial_number).await { + Ok(events) => ( + StatusCode::OK, + DiskEventFindResponse { + events, + message: None, + }, + ), + Err(e) => ( + StatusCode::INTERNAL_SERVER_ERROR, + DiskEventFindResponse { + events: Vec::new(), + message: Some(format!("{:?}", e)), + }, + ), + } } diff --git a/src/smartctl.rs b/src/smartctl.rs new file mode 100644 index 0000000..bb69abd --- /dev/null +++ b/src/smartctl.rs @@ -0,0 +1,113 @@ +// smartctl.rs + +use chrono::DateTime; +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +/// the output of a `smartctl --all --json` command; a `serde_json::Value` +/// wrapped up to provide a convenience method for extraction of the +/// `serial_number` field found in smartctl output +#[derive(Debug, Deserialize, Serialize)] +pub struct SmartCtl(pub Value); + +impl SmartCtl { + pub fn get_date_created(&self) -> Option { + // from the wrapped JSON Value + self.0 + // find the `local_time` field + .get("local_time") + // find the `time_t` field + .and_then(|local_time| local_time.get("time_t")) + // convert the integer there to an i64 + .and_then(|time_t| time_t.as_i64()) + // convert that into an ISO8601 date + .and_then(|timestamp| { + // convert the integer into a DateTime + let datetime = DateTime::from_timestamp(timestamp, 0)?; + // format the DateTime as an ISO8601 date + Some(datetime.to_rfc3339().replace("+00:00", "Z")) + }) + } + + pub fn get_serial(&self) -> Option { + // from the wrapped JSON Value + self.0 + // find the `serial_number` field + .get("serial_number") + // and attempt to extract it as an owned String + .and_then(|serial_number| serial_number.as_str().map(String::from)) + } +} + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + #[test] + fn test_always_succeed() { + assert!(true); + } + + #[test] + fn test_get_serial() { + // test that get_serial can extract a serial number from the JSON Value + // hey wait a minute, those funky integers aren't valid JSON! + // See: https://github.com/serde-rs/json/issues/974 + let smartctl_json = json!({ + "json_format_version": [ + 1, + 0 + ], + "smartctl": { + "version": [ + 7, + 1 + ], + "svn_revision": "5080", + "platform_info": "x86_64-linux-4.18.0-305.17.1.el8_4.x86_64", + "build_info": "(local build)", + "argv": [ + "smartctl", + "--all", + "--json", + "/dev/slot1" + ], + "exit_status": 0 + }, + "device": { + "name": "/dev/slot1", + "info_name": "/dev/slot1 [SAT]", + "type": "sat", + "protocol": "ATA" + }, + "model_name": "ST16000NM000J-2TW103", + "serial_number": "ZRS1NWBL", + "wwn": { + "naa": 5, + "oui": 3152, + "id": 3906377770_i64 + }, + "firmware_version": "SN04", + "user_capacity": { + "blocks": 31251759104_i64, + "bytes": 16000900661248_i64 + }, + "logical_block_size": 512, + "physical_block_size": 4096 + }); + let smartctl = SmartCtl(smartctl_json); + assert_eq!(smartctl.get_serial(), Some("ZRS1NWBL".to_string())); + } + + #[test] + fn test_get_serial_none() { + // test that get_serial returns None when serial number is missing from the JSON Value + let smartctl_no_serial = SmartCtl(json!({})); + assert_eq!(smartctl_no_serial.get_serial(), None); + } +} diff --git a/tests/data/smartctl001.json b/tests/data/smartctl001.json new file mode 100644 index 0000000..a37b80a --- /dev/null +++ b/tests/data/smartctl001.json @@ -0,0 +1,679 @@ +{ + "json_format_version": [ + 1, + 0 + ], + "smartctl": { + "version": [ + 7, + 1 + ], + "svn_revision": "5080", + "platform_info": "x86_64-linux-4.18.0-305.17.1.el8_4.x86_64", + "build_info": "(local build)", + "argv": [ + "smartctl", + "--all", + "--json", + "/dev/slot1" + ], + "exit_status": 0 + }, + "device": { + "name": "/dev/slot1", + "info_name": "/dev/slot1 [SAT]", + "type": "sat", + "protocol": "ATA" + }, + "model_name": "ST16000NM000J-2TW103", + "serial_number": "ZRS1NWBL", + "wwn": { + "naa": 5, + "oui": 3152, + "id": 3906377770 + }, + "firmware_version": "SN04", + "user_capacity": { + "blocks": 31251759104, + "bytes": 16000900661248 + }, + "logical_block_size": 512, + "physical_block_size": 4096, + "rotation_rate": 7200, + "form_factor": { + "ata_value": 2, + "name": "3.5 inches" + }, + "in_smartctl_database": false, + "ata_version": { + "string": "ACS-4 (minor revision not indicated)", + "major_value": 4064, + "minor_value": 65535 + }, + "sata_version": { + "string": "SATA 3.3", + "value": 511 + }, + "interface_speed": { + "max": { + "sata_value": 14, + "string": "6.0 Gb/s", + "units_per_second": 60, + "bits_per_unit": 100000000 + }, + "current": { + "sata_value": 3, + "string": "6.0 Gb/s", + "units_per_second": 60, + "bits_per_unit": 100000000 + } + }, + "local_time": { + "time_t": 1722274912, + "asctime": "Mon Jul 29 17:41:52 2024 GMT" + }, + "smart_status": { + "passed": true + }, + "ata_smart_data": { + "offline_data_collection": { + "status": { + "value": 130, + "string": "was completed without error", + "passed": true + }, + "completion_seconds": 567 + }, + "self_test": { + "status": { + "value": 0, + "string": "completed without error", + "passed": true + }, + "polling_minutes": { + "short": 1, + "extended": 1402, + "conveyance": 2 + } + }, + "capabilities": { + "values": [ + 123, + 3 + ], + "exec_offline_immediate_supported": true, + "offline_is_aborted_upon_new_cmd": false, + "offline_surface_scan_supported": true, + "self_tests_supported": true, + "conveyance_self_test_supported": true, + "selective_self_test_supported": true, + "attribute_autosave_enabled": true, + "error_logging_supported": true, + "gp_logging_supported": true + } + }, + "ata_sct_capabilities": { + "value": 28861, + "error_recovery_control_supported": true, + "feature_control_supported": true, + "data_table_supported": true + }, + "ata_smart_attributes": { + "revision": 10, + "table": [ + { + "id": 1, + "name": "Raw_Read_Error_Rate", + "value": 64, + "worst": 64, + "thresh": 44, + "when_failed": "", + "flags": { + "value": 15, + "string": "POSR-- ", + "prefailure": true, + "updated_online": true, + "performance": true, + "error_rate": true, + "event_count": false, + "auto_keep": false + }, + "raw": { + "value": 2540008, + "string": "2540008" + } + }, + { + "id": 3, + "name": "Spin_Up_Time", + "value": 98, + "worst": 98, + "thresh": 0, + "when_failed": "", + "flags": { + "value": 3, + "string": "PO---- ", + "prefailure": true, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": false, + "auto_keep": false + }, + "raw": { + "value": 0, + "string": "0" + } + }, + { + "id": 4, + "name": "Start_Stop_Count", + "value": 100, + "worst": 100, + "thresh": 20, + "when_failed": "", + "flags": { + "value": 50, + "string": "-O--CK ", + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true + }, + "raw": { + "value": 2, + "string": "2" + } + }, + { + "id": 5, + "name": "Reallocated_Sector_Ct", + "value": 100, + "worst": 100, + "thresh": 10, + "when_failed": "", + "flags": { + "value": 51, + "string": "PO--CK ", + "prefailure": true, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true + }, + "raw": { + "value": 0, + "string": "0" + } + }, + { + "id": 7, + "name": "Seek_Error_Rate", + "value": 100, + "worst": 253, + "thresh": 45, + "when_failed": "", + "flags": { + "value": 15, + "string": "POSR-- ", + "prefailure": true, + "updated_online": true, + "performance": true, + "error_rate": true, + "event_count": false, + "auto_keep": false + }, + "raw": { + "value": 11411, + "string": "11411" + } + }, + { + "id": 9, + "name": "Power_On_Hours", + "value": 100, + "worst": 100, + "thresh": 0, + "when_failed": "", + "flags": { + "value": 50, + "string": "-O--CK ", + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true + }, + "raw": { + "value": 0, + "string": "0" + } + }, + { + "id": 10, + "name": "Spin_Retry_Count", + "value": 100, + "worst": 100, + "thresh": 97, + "when_failed": "", + "flags": { + "value": 19, + "string": "PO--C- ", + "prefailure": true, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": false + }, + "raw": { + "value": 0, + "string": "0" + } + }, + { + "id": 12, + "name": "Power_Cycle_Count", + "value": 100, + "worst": 100, + "thresh": 20, + "when_failed": "", + "flags": { + "value": 50, + "string": "-O--CK ", + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true + }, + "raw": { + "value": 2, + "string": "2" + } + }, + { + "id": 18, + "name": "Unknown_Attribute", + "value": 100, + "worst": 100, + "thresh": 50, + "when_failed": "", + "flags": { + "value": 11, + "string": "PO-R-- ", + "prefailure": true, + "updated_online": true, + "performance": false, + "error_rate": true, + "event_count": false, + "auto_keep": false + }, + "raw": { + "value": 0, + "string": "0" + } + }, + { + "id": 187, + "name": "Reported_Uncorrect", + "value": 100, + "worst": 100, + "thresh": 0, + "when_failed": "", + "flags": { + "value": 50, + "string": "-O--CK ", + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true + }, + "raw": { + "value": 0, + "string": "0" + } + }, + { + "id": 188, + "name": "Command_Timeout", + "value": 100, + "worst": 100, + "thresh": 0, + "when_failed": "", + "flags": { + "value": 50, + "string": "-O--CK ", + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true + }, + "raw": { + "value": 0, + "string": "0" + } + }, + { + "id": 190, + "name": "Airflow_Temperature_Cel", + "value": 78, + "worst": 78, + "thresh": 0, + "when_failed": "", + "flags": { + "value": 34, + "string": "-O---K ", + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": false, + "auto_keep": true + }, + "raw": { + "value": 370540566, + "string": "22 (Min/Max 22/22)" + } + }, + { + "id": 192, + "name": "Power-Off_Retract_Count", + "value": 100, + "worst": 100, + "thresh": 0, + "when_failed": "", + "flags": { + "value": 50, + "string": "-O--CK ", + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true + }, + "raw": { + "value": 1, + "string": "1" + } + }, + { + "id": 193, + "name": "Load_Cycle_Count", + "value": 100, + "worst": 100, + "thresh": 0, + "when_failed": "", + "flags": { + "value": 50, + "string": "-O--CK ", + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": true + }, + "raw": { + "value": 5, + "string": "5" + } + }, + { + "id": 194, + "name": "Temperature_Celsius", + "value": 22, + "worst": 40, + "thresh": 0, + "when_failed": "", + "flags": { + "value": 34, + "string": "-O---K ", + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": false, + "auto_keep": true + }, + "raw": { + "value": 81604378646, + "string": "22 (0 19 0 0 0)" + } + }, + { + "id": 197, + "name": "Current_Pending_Sector", + "value": 100, + "worst": 100, + "thresh": 0, + "when_failed": "", + "flags": { + "value": 18, + "string": "-O--C- ", + "prefailure": false, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": false + }, + "raw": { + "value": 0, + "string": "0" + } + }, + { + "id": 198, + "name": "Offline_Uncorrectable", + "value": 100, + "worst": 100, + "thresh": 0, + "when_failed": "", + "flags": { + "value": 16, + "string": "----C- ", + "prefailure": false, + "updated_online": false, + "performance": false, + "error_rate": false, + "event_count": true, + "auto_keep": false + }, + "raw": { + "value": 0, + "string": "0" + } + }, + { + "id": 199, + "name": "UDMA_CRC_Error_Count", + "value": 200, + "worst": 200, + "thresh": 0, + "when_failed": "", + "flags": { + "value": 62, + "string": "-OSRCK ", + "prefailure": false, + "updated_online": true, + "performance": true, + "error_rate": true, + "event_count": true, + "auto_keep": true + }, + "raw": { + "value": 0, + "string": "0" + } + }, + { + "id": 200, + "name": "Multi_Zone_Error_Rate", + "value": 100, + "worst": 100, + "thresh": 1, + "when_failed": "", + "flags": { + "value": 35, + "string": "PO---K ", + "prefailure": true, + "updated_online": true, + "performance": false, + "error_rate": false, + "event_count": false, + "auto_keep": true + }, + "raw": { + "value": 0, + "string": "0" + } + }, + { + "id": 240, + "name": "Head_Flying_Hours", + "value": 100, + "worst": 100, + "thresh": 0, + "when_failed": "", + "flags": { + "value": 0, + "string": "------ ", + "prefailure": false, + "updated_online": false, + "performance": false, + "error_rate": false, + "event_count": false, + "auto_keep": false + }, + "raw": { + "value": 153210073382912, + "string": "0 (139 88 0)" + } + }, + { + "id": 241, + "name": "Total_LBAs_Written", + "value": 100, + "worst": 253, + "thresh": 0, + "when_failed": "", + "flags": { + "value": 0, + "string": "------ ", + "prefailure": false, + "updated_online": false, + "performance": false, + "error_rate": false, + "event_count": false, + "auto_keep": false + }, + "raw": { + "value": 2531344, + "string": "2531344" + } + }, + { + "id": 242, + "name": "Total_LBAs_Read", + "value": 100, + "worst": 253, + "thresh": 0, + "when_failed": "", + "flags": { + "value": 0, + "string": "------ ", + "prefailure": false, + "updated_online": false, + "performance": false, + "error_rate": false, + "event_count": false, + "auto_keep": false + }, + "raw": { + "value": 8664, + "string": "8664" + } + } + ] + }, + "power_on_time": { + "hours": 0 + }, + "power_cycle_count": 2, + "temperature": { + "current": 22 + }, + "ata_smart_error_log": { + "summary": { + "revision": 1, + "count": 0 + } + }, + "ata_smart_self_test_log": { + "standard": { + "revision": 1, + "count": 0 + } + }, + "ata_smart_selective_self_test_log": { + "revision": 1, + "table": [ + { + "lba_min": 0, + "lba_max": 0, + "status": { + "value": 0, + "string": "Not_testing" + } + }, + { + "lba_min": 0, + "lba_max": 0, + "status": { + "value": 0, + "string": "Not_testing" + } + }, + { + "lba_min": 0, + "lba_max": 0, + "status": { + "value": 0, + "string": "Not_testing" + } + }, + { + "lba_min": 0, + "lba_max": 0, + "status": { + "value": 0, + "string": "Not_testing" + } + }, + { + "lba_min": 0, + "lba_max": 0, + "status": { + "value": 0, + "string": "Not_testing" + } + } + ], + "flags": { + "value": 0, + "remainder_scan_enabled": false + }, + "power_up_scan_resume_minutes": 0 + } +} diff --git a/tests/mongodb_test.rs b/tests/mongodb_test.rs new file mode 100644 index 0000000..5c3ff75 --- /dev/null +++ b/tests/mongodb_test.rs @@ -0,0 +1,90 @@ +// mongodb_test.rs + +use mongodb::{bson::doc, options::ClientOptions, Client, Collection}; +use serde_json::Value; +use std::fs::read_to_string; +use std::time::Duration; +use uuid::Uuid; +use wipac_disk_tracking::database::DISK_EVENTS_COLLECTION; +use wipac_disk_tracking::event::{DiskEvent, Event}; +use wipac_disk_tracking::smartctl::SmartCtl; + +async fn get_mongodb_collection() -> Option> { + // determine which database to use for integration testing + // this will be a destructive test, so hopefully you didn't provide credentials to production! + let mongo_url = std::env::var("DESTRUCTIVE_TEST_MONGODB_URL").unwrap_or_else(|_| { + "mongodb://disk_tracking:hunter2@localhost:27017/disk_tracking".to_string() + }); + + // create a client options object and attempt to connect to MongoDB + let mut client_options = ClientOptions::parse(&mongo_url).await.ok()?; + client_options.connect_timeout = Some(Duration::from_secs(1)); + client_options.server_selection_timeout = Some(Duration::from_secs(1)); + let client = Client::with_options(client_options).ok()?; + + // check connection by pinging the database + let database = client.database("admin"); + let ping_result = database.run_command(doc! {"ping": 1}).await.map(|_| ()); + if ping_result.is_err() { + // if we couldn't ping the DB, we don't really have a connection + return None; + } + + // return the disk events collection to the caller + let database = client.database("disk_tracking"); + let collection = database.collection::(DISK_EVENTS_COLLECTION); + Some(collection) +} + +#[tokio::test] +async fn test_mongodb_crud_operations() { + // check if the disk_events collection is available + let collection = match get_mongodb_collection().await { + Some(coll) => coll, + None => { + dbg!("MongoDB is not available. Skipping test."); + return; + } + }; + + // create a random UUID + let test_uuid = Uuid::new_v4(); + + // create a SmartCtl from the test data + let json_str = read_to_string("tests/data/smartctl001.json").expect("Unable to read file"); + let smartctl_json: Value = serde_json::from_str(&json_str).expect("Unable to deserialize JSON"); + let test_smartctl = SmartCtl(smartctl_json); + + // create a disk event + let new_event = DiskEvent { + uuid: test_uuid, + date_created: "2024-08-14T17:36:31Z".to_string(), + serial_number: "ZRS1NWBL".to_string(), + event: Event::FORMATTED, + smartctl: test_smartctl, + }; + + // insert the disk event + let insert_result = collection.insert_one(&new_event).await; + assert!(insert_result.is_ok()); + + // read the disk event + let filter = doc! { "serial_number": "ZRS1NWBL" }; + let found_event = collection + .find_one(filter.clone()) + .await + .expect("Error finding document"); + assert!(found_event.is_some()); + assert_eq!(found_event.unwrap().serial_number, "ZRS1NWBL"); + + // update the disk event + let update = doc! { "$set": { "event": "closed" } }; // we would never actually do this, but what the heck + let update_result = collection.update_one(filter.clone(), update).await; + assert!(update_result.is_ok()); + assert_eq!(update_result.unwrap().modified_count, 1); + + // delete the disk event + let delete_result = collection.delete_one(filter.clone()).await; + assert!(delete_result.is_ok()); + assert_eq!(delete_result.unwrap().deleted_count, 1); +} diff --git a/tests/smartctl_serde_test.rs b/tests/smartctl_serde_test.rs new file mode 100644 index 0000000..366a8ea --- /dev/null +++ b/tests/smartctl_serde_test.rs @@ -0,0 +1,19 @@ +// smartctl_serde_test.rs + +use serde_json::Value; +use std::fs::read_to_string; +use wipac_disk_tracking::smartctl::SmartCtl; + +#[test] +fn test_deserialize_smartctl() { + // read JSON data + let json_str = read_to_string("tests/data/smartctl001.json").expect("Unable to read file"); + // deserialize JSON data + let smartctl_json: Value = serde_json::from_str(&json_str).expect("Unable to deserialize JSON"); + // wrap in a SmartCtl struct + let smartctl = SmartCtl(smartctl_json); + // obtain the serial number of the disk + let serial = smartctl.get_serial(); + // ensure we got the correct serial number from the test data + assert_eq!(serial, Some("ZRS1NWBL".to_string())); +}