diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..ba5434a --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,4775 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "actix-codec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-sink", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "actix-cors" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daa239b93927be1ff123eebada5a3ff23e89f0124ccb8609234e5103d5a5ae6d" +dependencies = [ + "actix-utils", + "actix-web", + "derive_more 2.0.1", + "futures-util", + "log", + "once_cell", + "smallvec", +] + +[[package]] +name = "actix-http" +version = "3.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44cceded2fb55f3c4b67068fa64962e2ca59614edc5b03167de9ff82ae803da0" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "base64", + "bitflags", + "brotli", + "bytes", + "bytestring", + "derive_more 2.0.1", + "encoding_rs", + "flate2", + "foldhash", + "futures-core", + "h2 0.3.27", + "http 0.2.12", + "httparse", + "httpdate", + "itoa", + "language-tags", + "local-channel", + "mime", + "percent-encoding", + "pin-project-lite", + "rand 0.9.2", + "sha1", + "smallvec", + "tokio", + "tokio-util", + "tracing", + "zstd", +] + +[[package]] +name = "actix-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "actix-multipart" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5118a26dee7e34e894f7e85aa0ee5080ae4c18bf03c0e30d49a80e418f00a53" +dependencies = [ + "actix-multipart-derive", + "actix-utils", + "actix-web", + "derive_more 0.99.20", + "futures-core", + "futures-util", + "httparse", + "local-waker", + "log", + "memchr", + "mime", + "rand 0.8.5", + "serde", + "serde_json", + "serde_plain", + "tempfile", + "tokio", +] + +[[package]] +name = "actix-multipart-derive" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e11eb847f49a700678ea2fa73daeb3208061afa2b9d1a8527c03390f4c4a1c6b" +dependencies = [ + "darling", + "parse-size", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "actix-router" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8" +dependencies = [ + "bytestring", + "cfg-if", + "http 0.2.12", + "regex", + "regex-lite", + "serde", + "tracing", +] + +[[package]] +name = "actix-rt" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92589714878ca59a7626ea19734f0e07a6a875197eec751bb5d3f99e64998c63" +dependencies = [ + "futures-core", + "tokio", +] + +[[package]] +name = "actix-server" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a65064ea4a457eaf07f2fba30b4c695bf43b721790e9530d26cb6f9019ff7502" +dependencies = [ + "actix-rt", + "actix-service", + "actix-utils", + "futures-core", + "futures-util", + "mio", + "socket2 0.5.10", + "tokio", + "tracing", +] + +[[package]] +name = "actix-service" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e46f36bf0e5af44bdc4bdb36fbbd421aa98c79a9bce724e1edeb3894e10dc7f" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "actix-utils" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" +dependencies = [ + "local-waker", + "pin-project-lite", +] + +[[package]] +name = "actix-web" +version = "4.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a597b77b5c6d6a1e1097fddde329a83665e25c5437c696a3a9a4aa514a614dea" +dependencies = [ + "actix-codec", + "actix-http", + "actix-macros", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-utils", + "actix-web-codegen", + "bytes", + "bytestring", + "cfg-if", + "cookie", + "derive_more 2.0.1", + "encoding_rs", + "foldhash", + "futures-core", + "futures-util", + "impl-more", + "itoa", + "language-tags", + "log", + "mime", + "once_cell", + "pin-project-lite", + "regex", + "regex-lite", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "socket2 0.5.10", + "time", + "tracing", + "url", +] + +[[package]] +name = "actix-web-codegen" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f591380e2e68490b5dfaf1dd1aa0ebe78d84ba7067078512b4ea6e4492d622b8" +dependencies = [ + "actix-router", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "adsb" +version = "0.1.0" +dependencies = [ + "clap", + "ctrlc", + "env_logger", + "log", + "rusb", +] + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "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.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" +dependencies = [ + "windows-sys 0.60.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.60.2", +] + +[[package]] +name = "api" +version = "0.1.3" +dependencies = [ + "actix-cors", + "actix-multipart", + "actix-web", + "argon2", + "chrono", + "dotenv", + "env_logger", + "futures-util", + "handlebars", + "lettre", + "lib", + "log", + "serde", + "serde_json", + "tokio", + "utoipa", + "utoipa-actix-web", + "utoipa-swagger-ui", +] + +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + +[[package]] +name = "argon2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" +dependencies = [ + "base64ct", + "blake2", + "cpufeatures", + "password-hash", +] + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "attohttpc" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e2cdb6d5ed835199484bb92bb8b3edd526effe995c61732580439c1a67e2e9" +dependencies = [ + "base64", + "http 1.3.1", + "log", + "native-tls", + "serde", + "serde_json", + "url", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "aws-creds" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b13804829a843b3f26e151c97acbb315ee1177a2724690edfcd28f1894146200" +dependencies = [ + "attohttpc", + "home", + "log", + "quick-xml", + "rust-ini", + "serde", + "thiserror", + "time", + "url", +] + +[[package]] +name = "aws-region" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5532f65342f789f9c1b7078ea9c9cd9293cd62dcc284fa99adc4a1c9ba43469c" +dependencies = [ + "thiserror", +] + +[[package]] +name = "backon" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "592277618714fbcecda9a02ba7a8781f319d26532a88553bbacc77ba5d2b3a8d" +dependencies = [ + "fastrand", +] + +[[package]] +name = "backtrace" +version = "0.3.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" + +[[package]] +name = "bitflags" +version = "2.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" +dependencies = [ + "serde", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[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 = "brotli" +version = "8.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "bytestring" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "113b4343b5f6617e7ad401ced8de3cc8b012e73a594347c307b90db3e9271289" +dependencies = [ + "bytes", +] + +[[package]] +name = "castaway" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" +dependencies = [ + "rustversion", +] + +[[package]] +name = "cc" +version = "1.2.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80f41ae168f955c12fb8960b057d70d0ca153fb83182b57d86380443527be7e9" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-link 0.2.0", +] + +[[package]] +name = "chumsky" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eebd66744a15ded14960ab4ccdbfb51ad3b81f51f3f04a80adac98c985396c9" +dependencies = [ + "hashbrown 0.14.5", + "stacker", +] + +[[package]] +name = "clap" +version = "4.5.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "compact_str" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "ryu", + "static_assertions", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom 0.2.16", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cookie" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ctrlc" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "881c5d0a13b2f1498e2306e82cbada78390e152d4b1378fb28a84f4dcd0dc4f3" +dependencies = [ + "dispatch", + "nix", + "windows-sys 0.61.0", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "derive_arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn", +] + +[[package]] +name = "derive_more" +version = "0.99.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", +] + +[[package]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dlv-list" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" +dependencies = [ + "const-random", +] + +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +dependencies = [ + "serde", +] + +[[package]] +name = "email-encoding" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9298e6504d9b9e780ed3f7dfd43a61be8cd0e09eb07f7706a945b0072b6670b6" +dependencies = [ + "base64", + "memchr", +] + +[[package]] +name = "email_address" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "env_filter" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.0", +] + +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "find-msvc-tools" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959" + +[[package]] +name = "flate2" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +dependencies = [ + "crc32fast", + "libz-rs-sys", + "miniz_oxide", +] + +[[package]] +name = "flume" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +dependencies = [ + "futures-core", + "futures-sink", + "spin", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +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-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "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.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasi 0.14.7+wasi-0.2.4", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "governor" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "444405bbb1a762387aa22dd569429533b54a1d8759d35d3b64cb39b0293eaa19" +dependencies = [ + "cfg-if", + "dashmap", + "futures-sink", + "futures-timer", + "futures-util", + "getrandom 0.3.3", + "hashbrown 0.15.5", + "nonzero_ext", + "parking_lot", + "portable-atomic", + "quanta", + "rand 0.9.2", + "smallvec", + "spinning_top", + "web-time", +] + +[[package]] +name = "h2" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.3.1", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "handlebars" +version = "6.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759e2d5aea3287cb1190c8ec394f42866cb5bf74fcbf213f354e3c856ea26098" +dependencies = [ + "derive_builder", + "log", + "num-order", + "pest", + "pest_derive", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.5", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "hostname" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56f203cd1c76362b69e3863fd987520ac36cf70a8c92627449b2f64a8cf7d65" +dependencies = [ + "cfg-if", + "libc", + "windows-link 0.1.3", +] + +[[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.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.3.1", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http 1.3.1", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2 0.4.12", + "http 1.3.1", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http 1.3.1", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "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", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http 1.3.1", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2 0.6.0", + "system-configuration", + "tokio", + "tower-service", + "tracing", + "windows-registry", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core 0.62.0", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "impl-more" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2" + +[[package]] +name = "indexmap" +version = "2.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" +dependencies = [ + "equivalent", + "hashbrown 0.16.0", + "serde", + "serde_core", +] + +[[package]] +name = "io-uring" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" +dependencies = [ + "bitflags", + "cfg-if", + "libc", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jiff" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde", +] + +[[package]] +name = "jiff-static" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.3", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852f13bec5eba4ba9afbeb93fd7c13fe56147f055939ae21c43a29a0ecb2702e" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "language-tags" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + +[[package]] +name = "lettre" +version = "0.11.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cb54db6ff7a89efac87dba5baeac57bb9ccd726b49a9b6f21fb92b3966aaf56" +dependencies = [ + "async-trait", + "base64", + "chumsky", + "email-encoding", + "email_address", + "fastrand", + "futures-io", + "futures-util", + "hostname", + "httpdate", + "idna", + "mime", + "native-tls", + "nom", + "percent-encoding", + "quoted_printable", + "socket2 0.6.0", + "tokio", + "tokio-native-tls", + "url", +] + +[[package]] +name = "lib" +version = "0.1.0" +dependencies = [ + "argon2", + "chrono", + "flate2", + "futures-util", + "governor", + "log", + "rand 0.9.2", + "rand_chacha 0.9.0", + "redis", + "regex", + "reqwest", + "rust-s3", + "serde", + "serde_json", + "sqlx", + "tokio", + "utoipa", + "uuid", +] + +[[package]] +name = "libc" +version = "0.2.175" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" + +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + +[[package]] +name = "libredox" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" +dependencies = [ + "bitflags", + "libc", + "redox_syscall", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "pkg-config", + "vcpkg", +] + +[[package]] +name = "libusb1-sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da050ade7ac4ff1ba5379af847a10a10a8e284181e060105bf8d86960ce9ce0f" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "libz-rs-sys" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "840db8cf39d9ec4dd794376f38acc40d0fc65eec2a8f484f7fd375b84602becd" +dependencies = [ + "zlib-rs", +] + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + +[[package]] +name = "local-channel" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8" +dependencies = [ + "futures-core", + "futures-sink", + "local-waker", +] + +[[package]] +name = "local-waker" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" + +[[package]] +name = "lock_api" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "maybe-async" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[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 = "md5" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae960838283323069879657ca3de837e9f7bbb4c7bf6ea7f1b290d5e9476d2e0" + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minidom" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e394a0e3c7ccc2daea3dffabe82f09857b6b510cb25af87d54bf3e910ac1642d" +dependencies = [ + "rxml", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +dependencies = [ + "libc", + "log", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", +] + +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + +[[package]] +name = "nonzero_ext" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21" + +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + +[[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]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.5", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-conv" +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-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-modular" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17bb261bf36fa7d83f4c294f834e91256769097b3cb505d44831e0a179ac647f" + +[[package]] +name = "num-order" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "537b596b97c40fcf8056d153049eb22f481c17ebce72a513ec9286e4986d1bb6" +dependencies = [ + "num-modular", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" +dependencies = [ + "bitflags", +] + +[[package]] +name = "objc2-io-kit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71c1c64d6120e51cd86033f67176b1cb66780c2efe34dec55176f77befd93c0a" +dependencies = [ + "libc", + "objc2-core-foundation", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + +[[package]] +name = "openssl" +version = "0.10.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "ordered-multimap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79" +dependencies = [ + "dlv-list", + "hashbrown 0.14.5", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "parse-size" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487f2ccd1e17ce8c1bfab3a65c89525af41cfad4c8659021a1e9a2aacd73b89b" + +[[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pest" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21e0a3a33733faeaf8651dfee72dd0f388f0c8e5ad496a3478fa5a922f49cfa8" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc58706f770acb1dbd0973e6530a3cff4746fb721207feb3a8a6064cd0b6c663" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d4f36811dfe07f7b8573462465d5cb8965fffc2e71ae377a33aecf14c2c9a2f" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42919b05089acbd0a5dcd5405fb304d17d1053847b81163d09c4ad18ce8e8420" +dependencies = [ + "pest", + "sha2", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "potential_utf" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "psm" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e944464ec8536cd1beb0bbfd96987eb5e3b72f2ecdafdc5c769a37f1fa2ae1f" +dependencies = [ + "cc", +] + +[[package]] +name = "quanta" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3ab5a9d756f0d97bdc89019bd2e4ea098cf9cde50ee7564dde6b81ccc8f06c7" +dependencies = [ + "crossbeam-utils", + "libc", + "once_cell", + "raw-cpuid", + "wasi 0.11.1+wasi-snapshot-preview1", + "web-sys", + "winapi", +] + +[[package]] +name = "quick-xml" +version = "0.38.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a232e7487fc2ef313d96dde7948e7a3c05101870d8985e4fd8d26aedd27b89" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "quoted_printable" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "640c9bd8497b02465aeef5375144c26062e0dcd5939dfcbb0f5db76cb8c17c73" + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r2d2" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93" +dependencies = [ + "log", + "parking_lot", + "scheduled-thread-pool", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", +] + +[[package]] +name = "raw-cpuid" +version = "11.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redis" +version = "0.32.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd3650deebc68526b304898b192fa4102a4ef0b9ada24da096559cb60e0eef8" +dependencies = [ + "arc-swap", + "backon", + "bytes", + "cfg-if", + "combine", + "futures-channel", + "futures-util", + "itoa", + "num-bigint", + "percent-encoding", + "pin-project-lite", + "r2d2", + "ryu", + "serde", + "serde_json", + "sha1_smol", + "socket2 0.6.0", + "tokio", + "tokio-util", + "url", +] + +[[package]] +name = "redox_syscall" +version = "0.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-lite" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943f41321c63ef1c92fd763bfe054d2668f7f225a5c29f0105903dc2fc04ba30" + +[[package]] +name = "regex-syntax" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" + +[[package]] +name = "reqwest" +version = "0.12.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.4.12", + "http 1.3.1", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "js-sys", + "log", + "mime", + "native-tls", + "percent-encoding", + "pin-project-lite", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rsa" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[package]] +name = "rusb" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab9f9ff05b63a786553a4c02943b74b34a988448671001e9a27e2f0565cc05a4" +dependencies = [ + "libc", + "libusb1-sys", +] + +[[package]] +name = "rust-embed" +version = "8.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "025908b8682a26ba8d12f6f2d66b987584a4a87bc024abc5bbc12553a8cd178a" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "8.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6065f1a4392b71819ec1ea1df1120673418bf386f50de1d6f54204d836d4349c" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "8.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6cc0c81648b20b70c491ff8cce00c1c3b223bb8ed2b5d41f0e54c6c4c0a3594" +dependencies = [ + "sha2", + "walkdir", +] + +[[package]] +name = "rust-ini" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "796e8d2b6696392a43bea58116b667fb4c29727dc5abd27d6acf338bb4f688c7" +dependencies = [ + "cfg-if", + "ordered-multimap", +] + +[[package]] +name = "rust-s3" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f9b973bd4097f5bb47e5827dcb9fb5dc17e93879e46badc27d2a4e9a4e5588" +dependencies = [ + "async-trait", + "aws-creds", + "aws-region", + "base64", + "bytes", + "cfg-if", + "futures-util", + "hex", + "hmac", + "http 1.3.1", + "log", + "maybe-async", + "md5", + "minidom", + "percent-encoding", + "quick-xml", + "reqwest", + "serde", + "serde_derive", + "serde_json", + "sha2", + "sysinfo", + "thiserror", + "time", + "tokio", + "tokio-stream", + "url", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.0", +] + +[[package]] +name = "rustls" +version = "0.23.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd3c25631629d034ce7cd9940adc9d45762d46de2b0f57193c4443b92c6d4d40" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8572f3c2cb9934231157b45499fc41e1f58c589fdfb81a844ba873265e80f8eb" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "rxml" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc94b580d0f5a6b7a2d604e597513d3c673154b52ddeccd1d5c32360d945ee" +dependencies = [ + "bytes", + "rxml_validation", +] + +[[package]] +name = "rxml_validation" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e80413b9a35e9d33217b3dcac04cf95f6559d15944b93887a08be5496c4a4" +dependencies = [ + "compact_str", +] + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +dependencies = [ + "windows-sys 0.61.0", +] + +[[package]] +name = "scheduled-thread-pool" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "scheduler" +version = "0.1.0" +dependencies = [ + "chrono", + "env_logger", + "lib", + "log", + "tokio", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.226" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.226" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.226" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", + "serde_core", +] + +[[package]] +name = "serde_plain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1fc6db65a611022b23a0dec6975d63fb80a302cb3388835ff02c097258d50" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha1_smol" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +dependencies = [ + "serde", +] + +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socket2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spinning_top" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "sqlx" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", +] + +[[package]] +name = "sqlx-core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6" +dependencies = [ + "base64", + "bytes", + "chrono", + "crc", + "crossbeam-queue", + "either", + "event-listener", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashbrown 0.15.5", + "hashlink", + "indexmap", + "log", + "memchr", + "once_cell", + "percent-encoding", + "serde", + "serde_json", + "sha2", + "smallvec", + "thiserror", + "tokio", + "tokio-stream", + "tracing", + "url", + "uuid", +] + +[[package]] +name = "sqlx-macros" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn", +] + +[[package]] +name = "sqlx-macros-core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b" +dependencies = [ + "dotenvy", + "either", + "heck", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", + "syn", + "tokio", + "url", +] + +[[package]] +name = "sqlx-mysql" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" +dependencies = [ + "atoi", + "base64", + "bitflags", + "byteorder", + "bytes", + "chrono", + "crc", + "digest", + "dotenvy", + "either", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "generic-array", + "hex", + "hkdf", + "hmac", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "percent-encoding", + "rand 0.8.5", + "rsa", + "serde", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "tracing", + "uuid", + "whoami", +] + +[[package]] +name = "sqlx-postgres" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" +dependencies = [ + "atoi", + "base64", + "bitflags", + "byteorder", + "chrono", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "rand 0.8.5", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "tracing", + "uuid", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea" +dependencies = [ + "atoi", + "chrono", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "serde_urlencoded", + "sqlx-core", + "thiserror", + "tracing", + "url", + "uuid", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "stacker" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cddb07e32ddb770749da91081d8d0ac3a16f1a569a18b20348cd371f5dead06b" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys 0.59.0", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sysinfo" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07cec4dc2d2e357ca1e610cfb07de2fa7a10fc3e9fe89f72545f3d244ea87753" +dependencies = [ + "libc", + "memchr", + "ntapi", + "objc2-core-foundation", + "objc2-io-kit", + "windows", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84fa4d11fadde498443cca10fd3ac23c951f0dc59e080e9f4b93d4df4e4eea53" +dependencies = [ + "fastrand", + "getrandom 0.3.3", + "once_cell", + "rustix", + "windows-sys 0.61.0", +] + +[[package]] +name = "thiserror" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" + +[[package]] +name = "time-macros" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +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.47.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +dependencies = [ + "backtrace", + "bytes", + "io-uring", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "slab", + "socket2 0.6.0", + "tokio-macros", + "windows-sys 0.59.0", +] + +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f63835928ca123f1bef57abbcd23bb2ba0ac9ae1235f1e65bda0d06e7786bd" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http 1.3.1", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "unicase" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" + +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + +[[package]] +name = "unicode-ident" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" + +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-properties" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[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.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "utoipa" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fcc29c80c21c31608227e0912b2d7fddba57ad76b606890627ba8ee7964e993" +dependencies = [ + "indexmap", + "serde", + "serde_json", + "utoipa-gen", +] + +[[package]] +name = "utoipa-actix-web" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7eda9c23c05af0fb812f6a177514047331dac4851a2c8e9c4b895d6d826967f" +dependencies = [ + "actix-service", + "actix-web", + "utoipa", +] + +[[package]] +name = "utoipa-gen" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d79d08d92ab8af4c5e8a6da20c47ae3f61a0f1dabc1997cdf2d082b757ca08b" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "syn", + "uuid", +] + +[[package]] +name = "utoipa-swagger-ui" +version = "9.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d047458f1b5b65237c2f6dc6db136945667f40a7668627b3490b9513a3d43a55" +dependencies = [ + "actix-web", + "base64", + "mime_guess", + "regex", + "rust-embed", + "serde", + "serde_json", + "url", + "utoipa", + "zip", +] + +[[package]] +name = "uuid" +version = "1.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +dependencies = [ + "getrandom 0.3.3", + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasi" +version = "0.14.7+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" +dependencies = [ + "wasip2", +] + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab10a69fbd0a177f5f649ad4d8d3305499c42bab9aef2f7ff592d0ec8f833819" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb702423545a6007bbc368fde243ba47ca275e549c8a28617f56f6ba53b1d1c" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0b221ff421256839509adbb55998214a70d829d3a28c69b4a6672e9d2a42f67" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc65f4f411d91494355917b605e1480033152658d71f722a90647f56a70c88a0" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc003a991398a8ee604a401e194b6b3a39677b3173d6e74495eb51b82e99a32" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "293c37f4efa430ca14db3721dfbe48d8c33308096bd44d80ebaa775ab71ba1cf" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbe734895e869dc429d78c4b433f8d17d95f8d05317440b4fad5ab2d33e596dc" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "whoami" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d" +dependencies = [ + "libredox", + "wasite", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections", + "windows-core 0.61.2", + "windows-future", + "windows-link 0.1.3", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-core" +version = "0.62.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57fe7168f7de578d2d8a05b07fd61870d2e73b4020e9f49aa00da8471723497c" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.2.0", + "windows-result 0.4.0", + "windows-strings 0.5.0", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-link" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-registry" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" +dependencies = [ + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-result" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f" +dependencies = [ + "windows-link 0.2.0", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-strings" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda" +dependencies = [ + "windows-link 0.2.0", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", +] + +[[package]] +name = "windows-sys" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa" +dependencies = [ + "windows-link 0.2.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link 0.1.3", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zip" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12598812502ed0105f607f941c386f43d441e00148fce9dec3ca5ffb0bde9308" +dependencies = [ + "arbitrary", + "crc32fast", + "flate2", + "indexmap", + "memchr", + "zopfli", +] + +[[package]] +name = "zlib-rs" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f06ae92f42f5e5c42443fd094f245eb656abf56dd7cce9b8b263236565e00f2" + +[[package]] +name = "zopfli" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edfc5ee405f504cd4984ecc6f14d02d55cfda60fa4b689434ef4102aae150cd7" +dependencies = [ + "bumpalo", + "crc32fast", + "log", + "simd-adler32", +] + +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.16+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..b16758c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,4 @@ +[workspace] + +members = [ "crates/adsb", "crates/api", "crates/lib", "crates/scheduler" ] +resolver = "2" diff --git a/Taskfile.yml b/Taskfile.yml index 6724cae..b0c876f 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -4,6 +4,28 @@ version: '3' dotenv: ['.env.local', '.env'] +vars: + version: '{{ coalesce .version .v "latest" }}' + folder: '{{ coalesce .folder .f "nginx" }}' + registry: '{{ coalesce .registry .r "gitea.bensherriff.com/bsherriff" }}' + platform: '{{ coalesce .platform .p "linux/amd64,linux/arm64" }}' + image: '{{.registry}}/aviation-{{.folder}}:{{.version}}' + build_date: + sh: date -u +%Y-%m-%dT%H:%M:%SZ + vcs_ref: + sh: git rev-parse HEAD + context: '{{ coalesce .context .ctx "." }}' + dockerfile: > + {{- if or (eq .folder "nginx") (eq .folder "ui") -}} + {{.folder}}/Dockerfile + {{- else if eq .folder "api" -}} + crates/api/Dockerfile + {{- else if eq .folder "scheduler" -}} + crates/scheduler/Dockerfile + {{- else -}} + {{ fail (printf "Invalid folder '%s'. Valid: nginx, ui, api, scheduler" .folder) }} + {{- end -}} + tasks: default: cmds: @@ -18,66 +40,101 @@ tasks: dev-servers: deps: - task: run-api + - task: run-scheduler - task: run-ui # API Commands build-api: - dir: api - cmds: - - cargo build + dir: crates/api + cmd: cargo build format-api: - dir: api - cmds: - - cargo fmt + dir: crates/api + cmd: cargo fmt run-api: - dir: api - cmds: - - cargo run + dir: crates/api + cmd: cargo run + silent: true + + # Scheduler Commands + build-scheduler: + dir: crates/scheduler + cmd: cargo build + format-scheduler: + dir: crates/scheduler + cmd: cargo fmt + run-scheduler: + dir: crates/scheduler + cmd: cargo run silent: true # UI Commands build-ui: dir: ui - cmds: - - npm run build + cmd: npm run build format-ui: dir: ui - cmds: - - npm run format + cmd: npm run format clean-ui: dir: ui - cmds: - - rm -rf node_modules dist stats.html + cmd: rm -rf node_modules dist stats.html run-ui: dir: ui - cmds: - - npm run dev + cmd: npm run dev silent: true # Docker Commands docker-backend: - cmds: - - docker compose --profile backend up -d + cmd: docker compose --profile backend up -d docker-up: - cmds: - - docker compose --profile backend --profile api up -d + cmd: docker compose --profile backend --profile api up -d docker-down: - cmds: - - docker compose --profile backend --profile api down + cmd: docker compose --profile backend --profile api down docker-clean: - cmds: - - docker compose --profile backend --profile api down -v + cmd: docker compose --profile backend --profile api down -v docker-refresh: cmds: - task: docker-clean - task: docker-up - docker-build: + + build: + desc: Build a specific docker image from a folder cmds: - - docker compose build + - | + docker buildx build \ + -f {{.dockerfile}} \ + -t {{.image}} \ + --load \ + --build-arg BUILD_DATE={{.build_date}} \ + --build-arg BUILD_VERSION={{.version}} \ + --build-arg VCS_REF={{.vcs_ref}} \ + {{.context}} + + push: + desc: Build and push a specific docker image from a folder + cmds: + - | + docker buildx create \ + --use \ + --driver docker-container \ + --bootstrap \ + --name default-builder \ + --platform {{.platform}} \ + || true + ignore_error: true + - | + docker buildx build \ + -f {{.dockerfile}} \ + --platform {{.platform}} \ + -t {{.image}} \ + --push \ + --build-arg BUILD_DATE={{.build_date}} \ + --build-arg BUILD_VERSION={{.version}} \ + --build-arg VCS_REF={{.vcs_ref}} \ + {{.context}} + . psql: - cmds: - - docker exec -it aviation-postgres psql -U ${POSTGRES_USER} -d ${POSTGRES_DB} -P pager=off + cmd: docker exec -it aviation-postgres psql -U ${POSTGRES_USER} -d ${POSTGRES_DB} -P pager=off cert: cmds: diff --git a/api/src/airports/mod.rs b/api/src/airports/mod.rs deleted file mode 100644 index 8dc27fa..0000000 --- a/api/src/airports/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod model; -pub mod routes; - -pub use model::*; -pub use routes::init_routes; diff --git a/api/src/error.rs b/api/src/error.rs deleted file mode 100644 index 4a92c15..0000000 --- a/api/src/error.rs +++ /dev/null @@ -1,245 +0,0 @@ -use actix_web::http::StatusCode; -use actix_web::{HttpResponse, ResponseError}; -use log::warn; -use regex::Regex; -use serde::{Deserialize, Serialize}; -use serde_json::json; -use std::fmt; -use std::sync::{MutexGuard, PoisonError}; - -pub type ApiResult = Result; - -#[derive(Debug, Deserialize, Serialize)] -pub struct Error { - pub status: u16, - pub details: String, -} - -impl Error { - pub fn new(status: u16, details: String) -> Self { - Self { - status, - details, - } - } - - pub fn to_http_response(&self) -> HttpResponse { - let status = StatusCode::from_u16(self.status).unwrap_or_else(|err| { - warn!("{}", err); - StatusCode::INTERNAL_SERVER_ERROR - }); - HttpResponse::build(status).body(self.details.to_string()) - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str(self.details.as_str()) - } -} - -impl std::error::Error for Error { - fn description(&self) -> &str { - &self.details - } -} - -impl ResponseError for Error { - fn error_response(&self) -> HttpResponse { - let status = - StatusCode::from_u16(self.status).unwrap_or_else(|_| StatusCode::INTERNAL_SERVER_ERROR); - - let status_code = status.as_u16(); - let details = match status_code { - 401 => String::from("Unauthorized"), - code if code < 500 => self.details.clone(), - _ => { - log::error!("Internal server error: {}", self.details); - String::from("Internal Server Error") - } - }; - - HttpResponse::build(status).json(json!({ "status": status_code, "details": details })) - } -} - -impl From for Error { - fn from(error: std::io::Error) -> Self { - Self::new(500, format!("Unknown IO error: {:?}", error)) - } -} - -impl From for Error { - fn from(error: chrono::ParseError) -> Self { - Self::new(500, format!("Chrono parse error: {:?}", error)) - } -} - -impl From for Error { - fn from(error: core::num::ParseIntError) -> Self { - Self::new(500, format!("Integer parse error: {:?}", error)) - } -} - -impl From for Error { - fn from(error: core::num::ParseFloatError) -> Self { - Self::new(500, format!("Float parse error: {:?}", error)) - } -} - -impl From for Error { - fn from(error: std::env::VarError) -> Self { - Self::new( - 500, - format!("Unknown environment variable error: {:?}", error), - ) - } -} - -impl From for Error { - fn from(error: reqwest::Error) -> Self { - match error.status() { - Some(status_code) => { - if status_code.is_client_error() { - Self::new(500, format!("Client reqwest error: {:?}", error)) - } else if status_code.is_server_error() { - Self::new(500, format!("Server reqwest error: {:?}", error)) - } else { - Self::new(500, format!("Unknown reqwest error: {:?}", error)) - } - } - _ => Self::new(500, format!("Unknown reqwest error: {:?}", error)), - } - } -} - -impl From for Error { - fn from(error: serde_json::Error) -> Self { - Self::new(500, format!("Unknown serde_json error: {:?}", error)) - } -} - -impl From for Error { - fn from(error: argon2::password_hash::Error) -> Self { - Self::new(500, format!("Unknown argon2 error: {:?}", error)) - } -} - -impl From for Error { - fn from(error: redis::RedisError) -> Self { - Self::new(500, format!("Unknown redis error: {:?}", error)) - } -} - -impl From for Error { - fn from(error: s3::error::S3Error) -> Self { - match error { - s3::error::S3Error::Credentials(err) => { - Self::new(500, format!("Unknown s3 credentials error: {:?}", err)) - } - s3::error::S3Error::FromUtf8(err) => { - Self::new(500, format!("Unknown s3 from utf8 error: {:?}", err)) - } - s3::error::S3Error::FmtError(err) => { - Self::new(500, format!("Unknown s3 fmt error: {:?}", err)) - } - s3::error::S3Error::HeaderToStr(err) => { - Self::new(500, format!("Unknown s3 header to str error: {:?}", err)) - } - s3::error::S3Error::HmacInvalidLength(err) => Self::new( - 500, - format!("Unknown s3 hmac invalid length error: {:?}", err), - ), - s3::error::S3Error::Http(error) => Self::new(error.status_code().as_u16(), error.to_string()), - _ => { - let re = Regex::new(r"HTTP (\d{3})").unwrap(); - // Apply the regex to the input string - if let Some(captures) = re.captures(&error.to_string()) { - if let Some(http_code_str) = captures.get(1) { - if let Ok(http_code) = http_code_str.as_str().parse::() { - return Self::new(http_code, error.to_string()); - } - } - } - Self::new(500, format!("Unknown s3 error: {:?}", error)) - } - } - } -} - -impl From for Error { - fn from(error: sqlx::Error) -> Self { - match error { - sqlx::Error::RowNotFound => Error::new(404, "Not found".to_string()), - sqlx::Error::ColumnIndexOutOfBounds { .. } => Error::new(422, error.to_string()), - sqlx::Error::ColumnNotFound { .. } => Error::new(422, error.to_string()), - sqlx::Error::ColumnDecode { .. } => Error::new(422, error.to_string()), - sqlx::Error::Decode(_) => Error::new(422, error.to_string()), - sqlx::Error::PoolTimedOut => Error::new(503, error.to_string()), - sqlx::Error::PoolClosed => Error::new(503, error.to_string()), - sqlx::Error::Tls(_) => Error::new(500, error.to_string()), - sqlx::Error::Io(_) => Error::new(500, error.to_string()), - sqlx::Error::Protocol(_) => Error::new(500, error.to_string()), - sqlx::Error::Configuration(_) => Error::new(500, error.to_string()), - sqlx::Error::AnyDriverError(_) => Error::new(500, error.to_string()), - sqlx::Error::Database(err) => { - if let Some(code) = err.code() { - match code.trim() { - // Unique violation - "23505" => return Error::new(409, err.to_string()), - _ => (), - } - } - Error::new(500, err.to_string()) - } - sqlx::Error::Migrate(_) => Error::new(500, error.to_string()), - sqlx::Error::TypeNotFound { type_name } => { - Error::new(500, format!("Type not found: {}", type_name)) - } - sqlx::Error::WorkerCrashed => Error::new(500, error.to_string()), - _ => Error::new(500, error.to_string()), - } - } -} - -impl From for Error { - fn from(error: sqlx::migrate::MigrateError) -> Self { - Error::new(500, error.to_string()) - } -} - -impl From for Error { - fn from(error: lettre::address::AddressError) -> Self { - Error::new(500, error.to_string()) - } -} - -impl From for Error { - fn from(error: lettre::error::Error) -> Self { - Error::new(500, error.to_string()) - } -} - -impl From for Error { - fn from(error: lettre::transport::smtp::Error) -> Self { - Error::new(500, error.to_string()) - } -} - -impl From for Error { - fn from(error: String) -> Self { - Self::new(500, error) - } -} - -impl From for Error { - fn from(error: regex::Error) -> Self { - Self::new(500, error.to_string()) - } -} - -impl<'a, T> From>> for Error { - fn from(_: PoisonError>) -> Self { - Self::new(500, "Failed to acquire lock".to_string()) - } -} diff --git a/api/src/scheduler.rs b/api/src/scheduler.rs deleted file mode 100644 index 3f814b2..0000000 --- a/api/src/scheduler.rs +++ /dev/null @@ -1,43 +0,0 @@ -use crate::metars::Metar; -use chrono::{DateTime, Utc}; -use std::env; -use std::sync::Arc; -use std::time::{Duration, Instant}; -use tokio::time::interval; -use crate::state::AppState; - -pub fn run(state: Arc) { - tokio::spawn(async move { - let seconds = env::var("METAR_INTERVAL") - .unwrap_or("300".to_string()) - .parse::() - .unwrap_or(300); - - // Create an interval ticker - let mut interval = interval(Duration::from_secs(seconds)); - let mut etag = None; - - loop { - interval.tick().await; - - // Record start times - let start_monotonic = Instant::now(); - let start_utc: DateTime = Utc::now(); - log::debug!("METAR update started at {}", start_utc); - - // Run the update - match Metar::update_metars(&state, etag.clone()).await { - Ok(new_etag) => etag = Some(new_etag), - Err(err) => log::error!("METAR update failed: {}", err), - } - - let elapsed = start_monotonic.elapsed(); - let next_utc = Utc::now() + chrono::Duration::from_std(Duration::from_secs(seconds)).unwrap(); - log::info!( - "METAR update finished in {:.2?}; next run at {}", - elapsed, - next_utc - ); - } - }); -} diff --git a/bruno/Account/Change Password.bru b/bruno/Account/Change Password.bru index 46bfd1c..ca964f7 100644 --- a/bruno/Account/Change Password.bru +++ b/bruno/Account/Change Password.bru @@ -1,7 +1,7 @@ meta { name: Change Password type: http - seq: 6 + seq: 7 } put { diff --git a/bruno/Account/Confirm Password Reset.bru b/bruno/Account/Confirm Password Reset.bru index b0dc9d9..09cfb43 100644 --- a/bruno/Account/Confirm Password Reset.bru +++ b/bruno/Account/Confirm Password Reset.bru @@ -1,7 +1,7 @@ meta { name: Confirm Password Reset type: http - seq: 8 + seq: 9 } post { diff --git a/bruno/Account/Get Profile.bru b/bruno/Account/Get Profile.bru index c5f1931..5dd965f 100644 --- a/bruno/Account/Get Profile.bru +++ b/bruno/Account/Get Profile.bru @@ -1,7 +1,7 @@ meta { name: Get Profile type: http - seq: 10 + seq: 11 } get { diff --git a/bruno/Account/Login Admin (Default).bru b/bruno/Account/Login Admin (Default).bru new file mode 100644 index 0000000..02bb30b --- /dev/null +++ b/bruno/Account/Login Admin (Default).bru @@ -0,0 +1,18 @@ +meta { + name: Login Admin (Default) + type: http + seq: 5 +} + +post { + url: {{API_URL}}/account/login + body: json + auth: none +} + +body:json { + { + "username": "admin", + "password": "changeme" + } +} diff --git a/bruno/Account/Logout.bru b/bruno/Account/Logout.bru index 196fb5f..4dd9289 100644 --- a/bruno/Account/Logout.bru +++ b/bruno/Account/Logout.bru @@ -1,7 +1,7 @@ meta { name: Logout type: http - seq: 5 + seq: 6 } post { diff --git a/bruno/Account/Refresh Session.bru b/bruno/Account/Refresh Session.bru index 38b14ba..6f790c2 100644 --- a/bruno/Account/Refresh Session.bru +++ b/bruno/Account/Refresh Session.bru @@ -1,7 +1,7 @@ meta { name: Refresh Session type: http - seq: 9 + seq: 10 } get { diff --git a/bruno/Account/Reset Password.bru b/bruno/Account/Reset Password.bru index fc64ea3..8ef0bc0 100644 --- a/bruno/Account/Reset Password.bru +++ b/bruno/Account/Reset Password.bru @@ -1,7 +1,7 @@ meta { name: Reset Password type: http - seq: 7 + seq: 8 } post { diff --git a/adsb/Cargo.lock b/crates/adsb/Cargo.lock similarity index 100% rename from adsb/Cargo.lock rename to crates/adsb/Cargo.lock diff --git a/adsb/Cargo.toml b/crates/adsb/Cargo.toml similarity index 100% rename from adsb/Cargo.toml rename to crates/adsb/Cargo.toml diff --git a/adsb/README.md b/crates/adsb/README.md similarity index 100% rename from adsb/README.md rename to crates/adsb/README.md diff --git a/adsb/rust-toolchain.toml b/crates/adsb/rust-toolchain.toml similarity index 100% rename from adsb/rust-toolchain.toml rename to crates/adsb/rust-toolchain.toml diff --git a/adsb/rustfmt.toml b/crates/adsb/rustfmt.toml similarity index 100% rename from adsb/rustfmt.toml rename to crates/adsb/rustfmt.toml diff --git a/adsb/src/constants.rs b/crates/adsb/src/constants.rs similarity index 100% rename from adsb/src/constants.rs rename to crates/adsb/src/constants.rs diff --git a/adsb/src/device.rs b/crates/adsb/src/device.rs similarity index 100% rename from adsb/src/device.rs rename to crates/adsb/src/device.rs diff --git a/adsb/src/error.rs b/crates/adsb/src/error.rs similarity index 100% rename from adsb/src/error.rs rename to crates/adsb/src/error.rs diff --git a/adsb/src/frame.rs b/crates/adsb/src/frame.rs similarity index 100% rename from adsb/src/frame.rs rename to crates/adsb/src/frame.rs diff --git a/adsb/src/hex.rs b/crates/adsb/src/hex.rs similarity index 100% rename from adsb/src/hex.rs rename to crates/adsb/src/hex.rs diff --git a/adsb/src/main.rs b/crates/adsb/src/main.rs similarity index 100% rename from adsb/src/main.rs rename to crates/adsb/src/main.rs diff --git a/api/Cargo.lock b/crates/api/Cargo.lock similarity index 96% rename from api/Cargo.lock rename to crates/api/Cargo.lock index 03ac794..d08d639 100644 --- a/api/Cargo.lock +++ b/crates/api/Cargo.lock @@ -444,10 +444,11 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "attohttpc" -version = "0.28.5" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07a9b245ba0739fc90935094c29adbaee3f977218b5fb95e822e261cda7f56a3" +checksum = "16e2cdb6d5ed835199484bb92bb8b3edd526effe995c61732580439c1a67e2e9" dependencies = [ + "base64", "http 1.3.1", "log", "native-tls", @@ -464,9 +465,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "aws-creds" -version = "0.37.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f84143206b9c72b3c5cb65415de60c7539c79cd1559290fddec657939131be0" +checksum = "b13804829a843b3f26e151c97acbb315ee1177a2724690edfcd28f1894146200" dependencies = [ "attohttpc", "home", @@ -474,18 +475,18 @@ dependencies = [ "quick-xml", "rust-ini", "serde", - "thiserror 1.0.69", + "thiserror", "time", "url", ] [[package]] name = "aws-region" -version = "0.25.5" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9aed3f9c7eac9be28662fdb3b0f4d1951e812f7c64fed4f0327ba702f459b3b" +checksum = "5532f65342f789f9c1b7078ea9c9cd9293cd62dcc284fa99adc4a1c9ba43469c" dependencies = [ - "thiserror 1.0.69", + "thiserror", ] [[package]] @@ -599,6 +600,15 @@ dependencies = [ "bytes", ] +[[package]] +name = "castaway" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" +dependencies = [ + "rustversion", +] + [[package]] name = "cc" version = "1.2.23" @@ -660,6 +670,19 @@ dependencies = [ "tokio-util", ] +[[package]] +name = "compact_str" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "ryu", + "static_assertions", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -1362,7 +1385,7 @@ dependencies = [ "pest_derive", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror", ] [[package]] @@ -1467,17 +1490,6 @@ dependencies = [ "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" @@ -1497,7 +1509,7 @@ dependencies = [ "bytes", "futures-core", "http 1.3.1", - "http-body 1.0.1", + "http-body", "pin-project-lite", ] @@ -1513,29 +1525,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "hyper" -version = "0.14.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "http 0.2.12", - "http-body 0.4.6", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2 0.5.9", - "tokio", - "tower-service", - "tracing", - "want", -] - [[package]] name = "hyper" version = "1.6.0" @@ -1547,7 +1536,7 @@ dependencies = [ "futures-util", "h2 0.4.10", "http 1.3.1", - "http-body 1.0.1", + "http-body", "httparse", "itoa", "pin-project-lite", @@ -1564,7 +1553,7 @@ checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ "futures-util", "http 1.3.1", - "hyper 1.6.0", + "hyper", "hyper-util", "rustls", "rustls-pki-types", @@ -1573,19 +1562,6 @@ dependencies = [ "tower-service", ] -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper 0.14.32", - "native-tls", - "tokio", - "tokio-native-tls", -] - [[package]] name = "hyper-tls" version = "0.6.0" @@ -1594,7 +1570,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.6.0", + "hyper", "hyper-util", "native-tls", "tokio", @@ -1613,8 +1589,8 @@ dependencies = [ "futures-channel", "futures-util", "http 1.3.1", - "http-body 1.0.1", - "hyper 1.6.0", + "http-body", + "hyper", "ipnet", "libc", "percent-encoding", @@ -2006,9 +1982,9 @@ dependencies = [ [[package]] name = "md5" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" +checksum = "ae960838283323069879657ca3de837e9f7bbb4c7bf6ea7f1b290d5e9476d2e0" [[package]] name = "memchr" @@ -2034,9 +2010,9 @@ dependencies = [ [[package]] name = "minidom" -version = "0.15.2" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f45614075738ce1b77a1768912a60c0227525971b03e09122a05b8a34a2a6278" +checksum = "e394a0e3c7ccc2daea3dffabe82f09857b6b510cb25af87d54bf3e910ac1642d" dependencies = [ "rxml", ] @@ -2094,6 +2070,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21" +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -2172,6 +2157,25 @@ dependencies = [ "libm", ] +[[package]] +name = "objc2-core-foundation" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" +dependencies = [ + "bitflags", +] + +[[package]] +name = "objc2-io-kit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71c1c64d6120e51cd86033f67176b1cb66780c2efe34dec55176f77befd93c0a" +dependencies = [ + "libc", + "objc2-core-foundation", +] + [[package]] name = "object" version = "0.36.7" @@ -2309,7 +2313,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" dependencies = [ "memchr", - "thiserror 2.0.12", + "thiserror", "ucd-trie", ] @@ -2460,9 +2464,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.32.0" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" +checksum = "42a232e7487fc2ef313d96dde7948e7a3c05101870d8985e4fd8d26aedd27b89" dependencies = [ "memchr", "serde", @@ -2650,13 +2654,14 @@ dependencies = [ "bytes", "encoding_rs", "futures-core", + "futures-util", "h2 0.4.10", "http 1.3.1", - "http-body 1.0.1", + "http-body", "http-body-util", - "hyper 1.6.0", + "hyper", "hyper-rustls", - "hyper-tls 0.6.0", + "hyper-tls", "hyper-util", "js-sys", "log", @@ -2671,12 +2676,14 @@ dependencies = [ "sync_wrapper", "tokio", "tokio-native-tls", + "tokio-util", "tower", "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", ] @@ -2761,9 +2768,9 @@ dependencies = [ [[package]] name = "rust-s3" -version = "0.35.1" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3df3f353b1f4209dcf437d777cda90279c397ab15a0cd6fd06bd32c88591533" +checksum = "94f9b973bd4097f5bb47e5827dcb9fb5dc17e93879e46badc27d2a4e9a4e5588" dependencies = [ "async-trait", "aws-creds", @@ -2771,27 +2778,25 @@ dependencies = [ "base64", "bytes", "cfg-if", - "futures", + "futures-util", "hex", "hmac", - "http 0.2.12", - "hyper 0.14.32", - "hyper-tls 0.5.0", + "http 1.3.1", "log", "maybe-async", "md5", "minidom", - "native-tls", "percent-encoding", "quick-xml", + "reqwest", "serde", "serde_derive", "serde_json", "sha2", - "thiserror 1.0.69", + "sysinfo", + "thiserror", "time", "tokio", - "tokio-native-tls", "tokio-stream", "url", ] @@ -2865,20 +2870,22 @@ checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "rxml" -version = "0.9.1" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a98f186c7a2f3abbffb802984b7f1dfd65dac8be1aafdaabbca4137f53f0dff7" +checksum = "65bc94b580d0f5a6b7a2d604e597513d3c673154b52ddeccd1d5c32360d945ee" dependencies = [ "bytes", "rxml_validation", - "smartstring", ] [[package]] name = "rxml_validation" -version = "0.9.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a197350ece202f19a166d1ad6d9d6de145e1d2a8ef47db299abe164dbd7530" +checksum = "826e80413b9a35e9d33217b3dcac04cf95f6559d15944b93887a08be5496c4a4" +dependencies = [ + "compact_str", +] [[package]] name = "ryu" @@ -3078,17 +3085,6 @@ dependencies = [ "serde", ] -[[package]] -name = "smartstring" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" -dependencies = [ - "autocfg", - "static_assertions", - "version_check", -] - [[package]] name = "socket2" version = "0.5.9" @@ -3178,7 +3174,7 @@ dependencies = [ "serde_json", "sha2", "smallvec", - "thiserror 2.0.12", + "thiserror", "tokio", "tokio-stream", "tracing", @@ -3262,7 +3258,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.12", + "thiserror", "tracing", "uuid", "whoami", @@ -3301,7 +3297,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.12", + "thiserror", "tracing", "uuid", "whoami", @@ -3327,7 +3323,7 @@ dependencies = [ "serde", "serde_urlencoded", "sqlx-core", - "thiserror 2.0.12", + "thiserror", "tracing", "url", "uuid", @@ -3412,6 +3408,20 @@ dependencies = [ "syn", ] +[[package]] +name = "sysinfo" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07cec4dc2d2e357ca1e610cfb07de2fa7a10fc3e9fe89f72545f3d244ea87753" +dependencies = [ + "libc", + "memchr", + "ntapi", + "objc2-core-foundation", + "objc2-io-kit", + "windows", +] + [[package]] name = "system-configuration" version = "0.6.1" @@ -3446,33 +3456,13 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - [[package]] name = "thiserror" version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ - "thiserror-impl 2.0.12", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "thiserror-impl", ] [[package]] @@ -3651,7 +3641,7 @@ dependencies = [ "bytes", "futures-util", "http 1.3.1", - "http-body 1.0.1", + "http-body", "iri-string", "pin-project-lite", "tower", @@ -3984,6 +3974,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "web-sys" version = "0.3.77" @@ -4054,6 +4057,28 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.61.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-link 0.1.1", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core", +] + [[package]] name = "windows-core" version = "0.61.2" @@ -4067,6 +4092,17 @@ dependencies = [ "windows-strings 0.4.2", ] +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core", + "windows-link 0.1.1", + "windows-threading", +] + [[package]] name = "windows-implement" version = "0.60.0" @@ -4101,6 +4137,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core", + "windows-link 0.1.1", +] + [[package]] name = "windows-registry" version = "0.4.0" @@ -4213,6 +4259,15 @@ dependencies = [ "windows_x86_64_msvc 0.53.0", ] +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.1", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" diff --git a/api/Cargo.toml b/crates/api/Cargo.toml similarity index 63% rename from api/Cargo.toml rename to crates/api/Cargo.toml index f104b64..3dd69f8 100644 --- a/api/Cargo.toml +++ b/crates/api/Cargo.toml @@ -4,37 +4,26 @@ version = "0.1.3" edition = "2024" authors = ["Ben Sherriff "] repository = "https://gitea.bensherriff.com/bsherriff/aviation" -readme = "../README.md" +readme = "../../README.md" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +lib = { path = "../lib" } actix-web = "4.11.0" actix-cors = "0.7.1" actix-multipart = "0.7.2" -chrono = { version = "0.4.41", features = ["serde"] } +chrono = { version = "0.4.42", features = ["serde"] } dotenv = "0.15.0" -sqlx = { version = "0.8.6", features = ["runtime-tokio", "postgres", "chrono", "uuid"] } env_logger = "0.11.8" -reqwest = "0.12.23" serde = {version = "1.0.219", features = ["derive"]} serde_json = "1.0.142" tokio = { version = "1.47.1", features = ["macros", "rt", "time"] } -uuid = { version = "1.18.0", features = ["serde", "v4"] } -log = "0.4.27" +log = "0.4.28" argon2 = "0.5.3" -redis = { version = "0.32.5", features = ["tokio-comp", "connection-manager", "r2d2", "json"] } -regex = "1.11.1" futures-util = "0.3.31" -rust-s3 = "0.35.1" -rand = "0.9.2" -rand_chacha = "0.9.0" -futures = "0.3.31" utoipa = { version = "5.4.0", features = ["chrono", "uuid", "actix_extras"] } utoipa-swagger-ui = { version = "9.0.2", features = ["actix-web"] } utoipa-actix-web = "0.1.2" -webpki-roots = "1.0.0" lettre = { version = "0.11.18", features = ["builder", "smtp-transport", "tokio1-native-tls"] } handlebars = "6.3.2" -governor = "0.10.1" -flate2 = "1.1.2" diff --git a/api/Dockerfile b/crates/api/Dockerfile similarity index 84% rename from api/Dockerfile rename to crates/api/Dockerfile index a5c6325..f234292 100644 --- a/api/Dockerfile +++ b/crates/api/Dockerfile @@ -4,9 +4,9 @@ FROM rust:bookworm AS builder WORKDIR /builder -COPY api/migrations ./migrations -COPY api/src ./src -COPY api/Cargo.toml ./ +COPY crates/lib /lib +COPY crates/api/src ./src +COPY crates/api/Cargo.toml ./ RUN apt-get update && apt-get install -y cmake RUN cargo build --release diff --git a/api/src/account/auth.rs b/crates/api/src/accounts/auth.rs similarity index 89% rename from api/src/account/auth.rs rename to crates/api/src/accounts/auth.rs index 07bb1d1..e428a1a 100644 --- a/api/src/account/auth.rs +++ b/crates/api/src/accounts/auth.rs @@ -2,11 +2,11 @@ use std::future::Future; use std::pin::Pin; use std::sync::Arc; use super::{SESSION_COOKIE_NAME, Session}; -use crate::account::user::User; -use crate::error::Error; +use crate::error::{ApiResult, Error}; use actix_web::{Error as ActixError, FromRequest, HttpRequest, dev::Payload, http, web}; use serde::{Deserialize, Serialize}; -use crate::state::AppState; +use lib::accounts::User; +use lib::state::AppState; #[derive(Debug, Serialize, Deserialize)] pub struct Auth { @@ -104,3 +104,16 @@ impl FromRequest for Auth { Box::pin(fut) } } + +impl Auth { + pub fn verify_role(&self, role: &str) -> ApiResult<()> { + if self.user.role == role { + Ok(()) + } else { + Err(Error { + status: 403, + details: "User does not have permission to perform this action.".to_string(), + }) + } + } +} diff --git a/api/src/account/email_token.rs b/crates/api/src/accounts/email_token.rs similarity index 95% rename from api/src/account/email_token.rs rename to crates/api/src/accounts/email_token.rs index ee74197..0431638 100644 --- a/api/src/account/email_token.rs +++ b/crates/api/src/accounts/email_token.rs @@ -1,12 +1,11 @@ -use crate::account::{csprng, hash}; +use lib::accounts::{csprng, hash}; use crate::error::{ApiResult, Error}; use crate::smtp; use chrono::{Datelike, Utc}; use serde::{Deserialize, Serialize}; use std::path::Path; use std::{env, fs}; -use redis::aio::ConnectionManager; -use crate::state::AppState; +use lib::state::AppState; #[derive(Debug, Serialize, Deserialize)] pub struct EmailToken { @@ -30,7 +29,9 @@ impl EmailToken { let now = Utc::now(); let expires_at = now + chrono::Duration::seconds(ttl_secs); let ttl = expires_at.timestamp() - now.timestamp(); - state.set_ex(&key, &value, ttl as u64).await + let _ = state.set_ex(&key, &value, ttl as u64).await?; + + Ok(()) } pub async fn get(state: &AppState, token: &str) -> ApiResult { @@ -42,7 +43,8 @@ impl EmailToken { } pub async fn delete(state: &AppState, token: &str) -> ApiResult<()> { - state.del(token).await + let _ = state.del(token).await; + Ok(()) } } diff --git a/crates/api/src/accounts/mod.rs b/crates/api/src/accounts/mod.rs new file mode 100644 index 0000000..641ca8c --- /dev/null +++ b/crates/api/src/accounts/mod.rs @@ -0,0 +1,7 @@ +mod auth; +mod email_token; +mod session; + +pub use auth::*; +pub use email_token::*; +pub use session::*; diff --git a/api/src/account/session.rs b/crates/api/src/accounts/session.rs similarity index 94% rename from api/src/account/session.rs rename to crates/api/src/accounts/session.rs index 983d88f..16e067c 100644 --- a/api/src/account/session.rs +++ b/crates/api/src/accounts/session.rs @@ -1,12 +1,10 @@ -use super::{csprng, hash, verify_hash}; +use lib::accounts::{csprng, hash, verify_hash}; use crate::error::{ApiResult, Error}; use actix_web::cookie::{Cookie, time::Duration}; use chrono::{DateTime, Utc}; -use redis::{AsyncCommands, RedisResult}; -use redis::aio::ConnectionManager; use serde::{Deserialize, Serialize}; -use tokio::task; -use crate::state::AppState; +use lib::error::CoreResult; +use lib::state::AppState; const DEFAULT_SESSION_TTL: i64 = 86400; // (In seconds) 24 hours pub const SESSION_COOKIE_NAME: &str = "session"; @@ -42,7 +40,7 @@ impl Session { pub async fn store(&self, state: &AppState) -> ApiResult<()> { let key = self.session_id.clone(); let value = serde_json::to_string(self)?; - let result: ApiResult<()> = match self.expires_at { + let result: CoreResult<()> = match self.expires_at { Some(expires_at) => { let ttl = expires_at.timestamp() - Utc::now().timestamp(); state.set_ex(&key, &value, ttl as u64).await @@ -73,7 +71,7 @@ impl Session { } pub async fn delete(state: &AppState, session_id: &str) -> ApiResult<()> { - let result: ApiResult<()> = state.del(session_id).await; + let result: CoreResult<()> = state.del(session_id).await; match result { Ok(_) => Ok(()), Err(err) => Err(err.into()), diff --git a/crates/api/src/error.rs b/crates/api/src/error.rs new file mode 100644 index 0000000..1e63947 --- /dev/null +++ b/crates/api/src/error.rs @@ -0,0 +1,133 @@ +use actix_web::http::StatusCode; +use actix_web::{HttpResponse, ResponseError}; +use log::warn; +use serde::{Deserialize, Serialize}; +use serde_json::json; +use std::fmt; +use lib::error::{CoreError, CoreErrorKind}; + +pub type ApiResult = Result; + +#[derive(Debug, Deserialize, Serialize)] +pub struct Error { + pub status: u16, + pub details: String, +} + +impl Error { + pub fn new(status: u16, details: String) -> Self { + Self { + status, + details, + } + } + + pub fn to_http_response(&self) -> HttpResponse { + let status = StatusCode::from_u16(self.status).unwrap_or_else(|err| { + warn!("{}", err); + StatusCode::INTERNAL_SERVER_ERROR + }); + HttpResponse::build(status).body(self.details.to_string()) + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(self.details.as_str()) + } +} + +impl std::error::Error for Error { + fn description(&self) -> &str { + &self.details + } +} + +impl ResponseError for Error { + fn error_response(&self) -> HttpResponse { + let status = + StatusCode::from_u16(self.status).unwrap_or_else(|_| StatusCode::INTERNAL_SERVER_ERROR); + + let status_code = status.as_u16(); + let details = match status_code { + 401 => String::from("Unauthorized"), + code if code < 500 => self.details.clone(), + _ => { + log::error!("Internal server error: {}", self.details); + String::from("Internal Server Error") + } + }; + + HttpResponse::build(status).json(json!({ "status": status_code, "details": details })) + } +} + +impl From for Error { + fn from(error: std::io::Error) -> Self { + Self::new(500, format!("Unknown IO error: {:?}", error)) + } +} + +impl From for Error { + fn from(error: std::env::VarError) -> Self { + Self::new( + 500, + format!("Unknown environment variable error: {:?}", error), + ) + } +} + +impl From for Error { + fn from(error: serde_json::Error) -> Self { + Self::new(500, format!("Unknown serde_json error: {:?}", error)) + } +} + +impl From for Error { + fn from(error: argon2::password_hash::Error) -> Self { + Self::new(500, format!("Unknown argon2 error: {:?}", error)) + } +} + +impl From for Error { + fn from(error: lettre::address::AddressError) -> Self { + Error::new(500, error.to_string()) + } +} + +impl From for Error { + fn from(error: lettre::error::Error) -> Self { + Error::new(500, error.to_string()) + } +} + +impl From for Error { + fn from(error: lettre::transport::smtp::Error) -> Self { + Error::new(500, error.to_string()) + } +} + +impl From for Error { + fn from(error: String) -> Self { + Self::new(500, error) + } +} + +impl From for Error { + fn from(error: CoreError) -> Self { + match error.kind { + CoreErrorKind::NotFound => Self::new(404, error.to_string()), + CoreErrorKind::InvalidInput => Self::new(400, error.to_string()), + CoreErrorKind::Conflict => Self::new(409, error.to_string()), + CoreErrorKind::Unauthorized => Self::new(401, error.to_string()), + CoreErrorKind::Forbidden => Self::new(403, error.to_string()), + CoreErrorKind::PreconditionFailed => Self::new(412, error.to_string()), + CoreErrorKind::Timeout => Self::new(408, error.to_string()), + CoreErrorKind::Cancelled => Self::new(499, error.to_string()), + CoreErrorKind::Unavailable => Self::new(503, error.to_string()), + CoreErrorKind::Internal => Self::new(500, error.to_string()), + CoreErrorKind::External => Self::new(502, error.to_string()), + _ => Self::new(500, error.to_string()), + } + } +} diff --git a/api/src/main.rs b/crates/api/src/main.rs similarity index 91% rename from api/src/main.rs rename to crates/api/src/main.rs index 380ecf4..1171ad3 100644 --- a/api/src/main.rs +++ b/crates/api/src/main.rs @@ -1,32 +1,26 @@ -use crate::account::User; -use crate::account::{ADMIN_ROLE, hash}; +use lib::accounts::{User, ADMIN_ROLE, hash}; use actix_cors::Cors; use actix_web::{App, HttpServer, middleware::Logger, web}; use dotenv::from_filename; use std::env; -use std::sync::Arc; use utoipa::openapi::SecurityRequirement; use utoipa::openapi::security::{ApiKey, ApiKeyValue, SecurityScheme}; use utoipa_actix_web::{AppExt, scope}; use utoipa_swagger_ui::{Config, SwaggerUi}; -use crate::state::AppState; +use lib::state::AppState; -mod account; -mod airports; +mod accounts; mod error; -mod http_client; -mod metars; -mod scheduler; +mod routes; mod smtp; mod system; -mod state; mod utils; #[actix_web::main] async fn main() -> Result<(), Box> { initialize_environment()?; - let state = Arc::new(AppState::new().await?); - scheduler::run(state.clone()); + + let state = AppState::new().await?; // Initialize admin user let admin_username = env::var("ADMIN_USERNAME"); @@ -82,9 +76,7 @@ async fn main() -> Result<(), Box> { .into_utoipa_app() .service( scope::scope("/api") - .configure(airports::init_routes) - .configure(metars::init_routes) - .configure(account::init_routes) + .configure(routes::init_routes) .configure(system::init_routes), ) .split_for_parts(); diff --git a/api/src/account/routes.rs b/crates/api/src/routes/accounts.rs similarity index 95% rename from api/src/account/routes.rs rename to crates/api/src/routes/accounts.rs index 09703df..beae22c 100644 --- a/api/src/account/routes.rs +++ b/crates/api/src/routes/accounts.rs @@ -1,18 +1,13 @@ -use crate::{ - account::{SESSION_COOKIE_NAME, Session, verify_hash}, - error::Error, -}; +use lib::accounts::{csprng, LoginRequest, RegisterRequest, UpdateUser, User, UserResponse, UserFavorite, verify_hash}; use actix_web::{HttpRequest, HttpResponse, ResponseError, delete, get, post, put, web}; use serde::Deserialize; use utoipa::ToSchema; use utoipa_actix_web::scope; use utoipa_actix_web::service_config::ServiceConfig; - -use crate::account::email_token::{EmailToken, send_confirm_email, send_password_reset_email}; -use crate::account::user::{LoginRequest, RegisterRequest, UpdateUser, User, UserResponse}; -use crate::account::user_favorites::UserFavorite; -use crate::account::{Auth, csprng}; -use crate::state::AppState; +use lib::error::CoreErrorKind; +use lib::state::AppState; +use crate::accounts::{send_confirm_email, send_password_reset_email, Auth, EmailToken, Session, SESSION_COOKIE_NAME}; +use crate::error::Error; #[utoipa::path( tag = "account", @@ -30,12 +25,12 @@ async fn register(state: web::Data, user: web::Json, let username = register_user.username.clone(); let email = register_user.email.clone(); let ip_address = req.peer_addr().unwrap().ip().to_string(); - let insert_user: User = match register_user.to_user() { + let insert_user: User = match register_user.to_user().map_err(Error::from) { Ok(user) => user, Err(err) => return ResponseError::error_response(&err), }; - match insert_user.insert(&state.pool).await { + match insert_user.insert(&state.pool).await.map_err(Error::from) { Ok(user) => { let user_response: UserResponse = user.into(); log::info!( @@ -123,7 +118,7 @@ async fn confirm_email_registration( avatar: None, }; - match update_user.update(&state.pool, &user.username).await { + match update_user.update(&state.pool, &user.username).await.map_err(Error::from) { Ok(user) => { let response: UserResponse = user.into(); log::info!( @@ -451,7 +446,7 @@ async fn change_password( avatar: None, }; - match update_user.update(&state.pool, &username).await { + match update_user.update(&state.pool, &username).await.map_err(Error::from) { Ok(user) => { let response: UserResponse = user.into(); log::info!( @@ -532,10 +527,10 @@ async fn confirm_password_reset( req: HttpRequest, ) -> HttpResponse { // TODO - let ip_address = req.peer_addr().unwrap().ip().to_string(); + let _ip_address = req.peer_addr().unwrap().ip().to_string(); let token = &request.token; - let email_token = match EmailToken::get(&state, token).await { + let _email_token = match EmailToken::get(&state, token).await { Ok(password_reset) => { if let Err(err) = EmailToken::delete(&state, &password_reset.token).await { return ResponseError::error_response(&err); @@ -563,7 +558,7 @@ async fn confirm_password_reset( #[get("/profile/favorites")] async fn get_favorites(state: web::Data, auth: Auth) -> HttpResponse { let username = auth.user.username; - match UserFavorite::select_all(&state.pool, &username).await { + match UserFavorite::select_all(&state.pool, &username).await.map_err(Error::from) { Ok(favorites) => HttpResponse::Ok().json(favorites), Err(err) => ResponseError::error_response(&err), } @@ -582,7 +577,7 @@ async fn get_favorites(state: web::Data, auth: Auth) -> HttpResponse { #[post("/profile/favorites/{icao}")] async fn add_favorite(state: web::Data, icao: web::Path, auth: Auth) -> HttpResponse { let username = auth.user.username; - match UserFavorite::insert(&state.pool, &username, &icao.into_inner()).await { + match UserFavorite::insert(&state.pool, &username, &icao.into_inner()).await.map_err(Error::from) { Ok(_) => HttpResponse::Ok().finish(), Err(err) => ResponseError::error_response(&err), } @@ -601,7 +596,7 @@ async fn add_favorite(state: web::Data, icao: web::Path, auth: #[delete("/profile/favorites/{icao}")] async fn remove_favorite(state: web::Data, icao: web::Path, auth: Auth) -> HttpResponse { let username = auth.user.username; - match UserFavorite::delete(&state.pool, &username, &icao.into_inner()).await { + match UserFavorite::delete(&state.pool, &username, &icao.into_inner()).await.map_err(Error::from) { Ok(_) => HttpResponse::Ok().finish(), Err(err) => ResponseError::error_response(&err), } diff --git a/api/src/airports/routes.rs b/crates/api/src/routes/airports.rs similarity index 83% rename from api/src/airports/routes.rs rename to crates/api/src/routes/airports.rs index 460159a..cef061c 100644 --- a/api/src/airports/routes.rs +++ b/crates/api/src/routes/airports.rs @@ -1,17 +1,14 @@ use futures_util::stream::StreamExt as _; -use crate::account::ADMIN_ROLE; -use crate::airports::{AirportQuery, UpdateAirport}; -use crate::{ - account::{Auth, verify_role}, - airports::Airport, -}; +use lib::{accounts::ADMIN_ROLE, airports::{Airport, AirportQuery, UpdateAirport}}; use actix_multipart::Multipart; use actix_web::{HttpRequest, HttpResponse, ResponseError, delete, get, post, put, web}; use utoipa::ToSchema; use utoipa_actix_web::scope; use utoipa_actix_web::service_config::ServiceConfig; -use crate::state::AppState; +use lib::state::AppState; +use crate::accounts::Auth; +use crate::error::Error; use crate::utils::Paged; #[derive(ToSchema)] @@ -36,8 +33,8 @@ struct FileUpload { )] #[post("/import")] async fn import_airports(state: web::Data, mut payload: Multipart, auth: Auth) -> HttpResponse { - if let Err(err) = verify_role(&auth, ADMIN_ROLE) { - return ResponseError::error_response(&err); + if let Err(err) = &auth.verify_role(ADMIN_ROLE).map_err(Error::from) { + return ResponseError::error_response(err); }; while let Some(item) = payload.next().await { @@ -68,7 +65,7 @@ async fn import_airports(state: web::Data, mut payload: Multipart, aut } }; - match Airport::insert_all(&state.pool, airports).await { + match Airport::insert_all(&state.pool, airports).await.map_err(Error::from) { Ok(_) => {} Err(err) => return ResponseError::error_response(&err), }; @@ -104,7 +101,7 @@ async fn get_airports(state: web::Data, req: HttpRequest) -> HttpRespo query.limit = Some(limit); query.page = Some(page); - match Airport::select_all(&state.pool, &query).await { + match Airport::select_all(&state.pool, &query).await.map_err(Error::from) { Ok(airports) => HttpResponse::Ok().json(Paged { data: airports, page, @@ -154,11 +151,11 @@ async fn get_airport(state: web::Data, icao: web::Path, req: H )] #[post("")] async fn insert_airport(state: web::Data, airport: web::Json, auth: Auth) -> HttpResponse { - let _ = match verify_role(&auth, ADMIN_ROLE) { + let _ = match &auth.verify_role(ADMIN_ROLE) { Ok(_) => {} - Err(err) => return ResponseError::error_response(&err), + Err(err) => return ResponseError::error_response(err), }; - match airport.insert(&state.pool).await { + match airport.insert(&state.pool).await.map_err(Error::from) { Ok(a) => HttpResponse::Ok().json(a), Err(err) => { log::error!("{}", err); @@ -184,11 +181,11 @@ async fn update_airport( airport: web::Json, auth: Auth, ) -> HttpResponse { - let _ = match verify_role(&auth, ADMIN_ROLE) { + let _ = match &auth.verify_role(ADMIN_ROLE) { Ok(_) => {} - Err(err) => return ResponseError::error_response(&err), + Err(err) => return ResponseError::error_response(err), }; - match Airport::update(&state.pool, &icao.into_inner(), &airport.into_inner()).await { + match Airport::update(&state.pool, &icao.into_inner(), &airport.into_inner()).await.map_err(Error::from) { Ok(a) => HttpResponse::Ok().json(a), Err(err) => { log::error!("{}", err); @@ -209,11 +206,11 @@ async fn update_airport( )] #[delete("")] async fn delete_airports(state: web::Data, auth: Auth) -> HttpResponse { - let _ = match verify_role(&auth, ADMIN_ROLE) { + let _ = match &auth.verify_role(ADMIN_ROLE) { Ok(_) => {} - Err(err) => return ResponseError::error_response(&err), + Err(err) => return ResponseError::error_response(err), }; - match Airport::delete_all(&state.pool).await { + match Airport::delete_all(&state.pool).await.map_err(Error::from) { Ok(_) => HttpResponse::NoContent().finish(), Err(err) => { log::error!("{}", err); @@ -234,11 +231,11 @@ async fn delete_airports(state: web::Data, auth: Auth) -> HttpResponse )] #[delete("/{icao}")] async fn delete_airport(state: web::Data, icao: web::Path, auth: Auth) -> HttpResponse { - let _ = match verify_role(&auth, ADMIN_ROLE) { + let _ = match &auth.verify_role(ADMIN_ROLE) { Ok(_) => {} - Err(err) => return ResponseError::error_response(&err), + Err(err) => return ResponseError::error_response(err), }; - match Airport::delete(&state.pool, &icao.into_inner()).await { + match Airport::delete(&state.pool, &icao.into_inner()).await.map_err(Error::from) { Ok(_) => HttpResponse::NoContent().finish(), Err(err) => { log::error!("{}", err); diff --git a/api/src/metars/routes.rs b/crates/api/src/routes/metars.rs similarity index 94% rename from api/src/metars/routes.rs rename to crates/api/src/routes/metars.rs index 6edfde5..aef741e 100644 --- a/api/src/metars/routes.rs +++ b/crates/api/src/routes/metars.rs @@ -1,12 +1,13 @@ use crate::AppState; -use crate::account::Auth; -use crate::metars::Metar; use actix_web::{HttpRequest, HttpResponse, get, put, web}; use log::error; use serde::Deserialize; use utoipa::{IntoParams, ToSchema}; use utoipa_actix_web::scope; use utoipa_actix_web::service_config::ServiceConfig; +use lib::metars::Metar; +use crate::accounts::Auth; +use crate::error::Error; #[derive(Debug, Deserialize, ToSchema, IntoParams)] #[into_params(parameter_in = Query)] @@ -37,7 +38,7 @@ async fn find_all(state: web::Data, req: HttpRequest) -> HttpResponse }; let icaos: Vec = icao_string.split(',').map(|s| s.to_uppercase()).collect(); - let metars = match Metar::get_all_distinct(&state.pool, &icaos).await { + let metars = match Metar::get_all_distinct(&state.pool, &icaos).await.map_err(Error::from) { Ok(a) => a, Err(err) => { error!("{}", err); @@ -62,7 +63,6 @@ async fn find_all(state: web::Data, req: HttpRequest) -> HttpResponse )] #[put("")] async fn refresh_metars(state: web::Data, req: HttpRequest, _auth: Auth) -> HttpResponse { - let client = state.client.clone(); let parameters = web::Query::::from_query(req.query_string()).unwrap(); let icao_option = ¶meters.icaos; if let None = icao_option { @@ -75,7 +75,7 @@ async fn refresh_metars(state: web::Data, req: HttpRequest, _auth: Aut }; let icaos: Vec = icao_string.split(',').map(|s| s.to_uppercase()).collect(); - let metars = match Metar::get_or_update_metars(&state, &icaos).await { + let metars = match Metar::get_or_update_metars(&state, &icaos).await.map_err(Error::from) { Ok(a) => a, Err(err) => { error!("{}", err); diff --git a/crates/api/src/routes/mod.rs b/crates/api/src/routes/mod.rs new file mode 100644 index 0000000..bce613e --- /dev/null +++ b/crates/api/src/routes/mod.rs @@ -0,0 +1,12 @@ +use utoipa_actix_web::service_config::ServiceConfig; + +mod accounts; +mod airports; +mod metars; + +pub fn init_routes(config: &mut ServiceConfig) { + config + .configure(accounts::init_routes) + .configure(airports::init_routes) + .configure(metars::init_routes); +} diff --git a/api/src/smtp/mod.rs b/crates/api/src/smtp/mod.rs similarity index 100% rename from api/src/smtp/mod.rs rename to crates/api/src/smtp/mod.rs diff --git a/api/src/system/mod.rs b/crates/api/src/system/mod.rs similarity index 100% rename from api/src/system/mod.rs rename to crates/api/src/system/mod.rs diff --git a/api/src/utils.rs b/crates/api/src/utils.rs similarity index 100% rename from api/src/utils.rs rename to crates/api/src/utils.rs diff --git a/crates/lib/Cargo.toml b/crates/lib/Cargo.toml new file mode 100644 index 0000000..56be929 --- /dev/null +++ b/crates/lib/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "lib" +version = "0.1.0" +edition = "2024" + +[dependencies] +argon2 = "0.5.3" +chrono = { version = "0.4.42", features = ["serde"] } +log = "0.4.28" +rand = "0.9.2" +rand_chacha = "0.9.0" +serde = { version = "1.0.226", features = ["derive"] } +serde_json = "1.0.142" +sqlx = { version = "0.8.6", features = ["runtime-tokio", "postgres", "chrono", "uuid"] } +utoipa = { version = "5.4.0", features = ["chrono", "uuid", "actix_extras"] } +uuid = { version = "1.18.1", features = ["serde", "v4"] } +futures-util = "0.3.31" +flate2 = "1.1.2" +reqwest = "0.12.23" +regex = "1.11.2" +redis = { version = "0.32.5", features = ["tokio-comp", "connection-manager", "r2d2", "json"] } +governor = "0.10.1" +tokio = { version = "1.47.1", features = ["macros", "rt", "time"] } +rust-s3 = "0.37.0" diff --git a/api/migrations/20250513_initial.sql b/crates/lib/migrations/20250513_initial.sql similarity index 100% rename from api/migrations/20250513_initial.sql rename to crates/lib/migrations/20250513_initial.sql diff --git a/crates/lib/src/accounts/mod.rs b/crates/lib/src/accounts/mod.rs new file mode 100644 index 0000000..b119192 --- /dev/null +++ b/crates/lib/src/accounts/mod.rs @@ -0,0 +1,9 @@ +mod password_requirements; +mod user; +mod user_favorites; +mod utils; + +pub use password_requirements::*; +pub use user::*; +pub use user_favorites::*; +pub use utils::*; \ No newline at end of file diff --git a/api/src/account/model.rs b/crates/lib/src/accounts/password_requirements.rs similarity index 100% rename from api/src/account/model.rs rename to crates/lib/src/accounts/password_requirements.rs diff --git a/api/src/account/user.rs b/crates/lib/src/accounts/user.rs similarity index 97% rename from api/src/account/user.rs rename to crates/lib/src/accounts/user.rs index 64e69e7..3e166c4 100644 --- a/api/src/account/user.rs +++ b/crates/lib/src/accounts/user.rs @@ -1,10 +1,11 @@ -use crate::{account::hash, error::ApiResult}; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; #[allow(unused_imports)] // Import is used in schema examples use serde_json::json; use sqlx::{Pool, Postgres, QueryBuilder}; use utoipa::ToSchema; +use crate::accounts::hash; +use crate::error::CoreResult; pub const ADMIN_ROLE: &str = "ADMIN"; pub const USER_ROLE: &str = "USER"; @@ -33,7 +34,7 @@ pub struct RegisterRequest { } impl RegisterRequest { - pub fn to_user(self) -> ApiResult { + pub fn to_user(self) -> CoreResult { let password_hash = hash(&self.password)?; Ok(User { username: self.username, @@ -106,7 +107,7 @@ pub struct UpdateUser { } impl UpdateUser { - pub async fn update(&self, pool: &Pool, username: &str) -> ApiResult { + pub async fn update(&self, pool: &Pool, username: &str) -> CoreResult { let mut query_builder: QueryBuilder = QueryBuilder::new(&format!("UPDATE {} SET ", TABLE_NAME)); @@ -235,7 +236,7 @@ impl User { .unwrap_or_else(|_| 0) } - pub async fn insert(&self, pool: &Pool) -> ApiResult { + pub async fn insert(&self, pool: &Pool) -> CoreResult { let user: User = sqlx::query_as::<_, Self>(&format!( r#" INSERT INTO {} ( diff --git a/api/src/account/user_favorites.rs b/crates/lib/src/accounts/user_favorites.rs similarity index 91% rename from api/src/account/user_favorites.rs rename to crates/lib/src/accounts/user_favorites.rs index 98c9560..6ef9ee1 100644 --- a/api/src/account/user_favorites.rs +++ b/crates/lib/src/accounts/user_favorites.rs @@ -1,4 +1,4 @@ -use crate::error::ApiResult; +use crate::error::CoreResult; use serde::Deserialize; use sqlx::{Pool, Postgres}; @@ -11,7 +11,7 @@ pub struct UserFavorite { } impl UserFavorite { - pub async fn select_all(pool: &Pool, username: &str) -> ApiResult> { + pub async fn select_all(pool: &Pool, username: &str) -> CoreResult> { let user_favorites: Vec = sqlx::query_as::<_, UserFavorite>(&format!( r#" SELECT * FROM {} WHERE username = $1 @@ -27,7 +27,7 @@ impl UserFavorite { Ok(favorites) } - pub async fn insert(pool: &Pool, username: &str, icao: &str) -> ApiResult<()> { + pub async fn insert(pool: &Pool, username: &str, icao: &str) -> CoreResult<()> { sqlx::query(&format!( r#" INSERT INTO {} ( @@ -44,7 +44,7 @@ impl UserFavorite { Ok(()) } - pub async fn delete(pool: &Pool, username: &str, icao: &str) -> ApiResult<()> { + pub async fn delete(pool: &Pool, username: &str, icao: &str) -> CoreResult<()> { sqlx::query(&format!( r#" DELETE FROM {} WHERE username = $1 AND icao = $2 diff --git a/api/src/account/mod.rs b/crates/lib/src/accounts/utils.rs similarity index 62% rename from api/src/account/mod.rs rename to crates/lib/src/accounts/utils.rs index bc621fa..2925df0 100644 --- a/api/src/account/mod.rs +++ b/crates/lib/src/accounts/utils.rs @@ -1,25 +1,11 @@ -use argon2::{ - Argon2, PasswordHash, PasswordHasher, PasswordVerifier, - password_hash::{SaltString, rand_core::OsRng}, -}; +use argon2::{Argon2, PasswordHash, PasswordHasher, PasswordVerifier}; +use argon2::password_hash::rand_core::OsRng; +use argon2::password_hash::SaltString; use rand::distr::Alphanumeric; -use rand::prelude::*; +use rand::Rng; use rand_chacha::ChaCha20Rng; - -mod auth; -mod email_token; -mod model; -mod routes; -mod session; -mod user; -mod user_favorites; - -pub use auth::*; -pub use routes::init_routes; -pub use session::*; -pub use user::*; - -use crate::error::{ApiResult, Error}; +use rand_chacha::rand_core::SeedableRng; +use crate::error::CoreResult; pub fn csprng(take: usize) -> String { // Generate a CSPRNG 128-bit (16 byte) ID using alphanumeric characters (a-z, A-Z, 0-9) @@ -31,7 +17,7 @@ pub fn csprng(take: usize) -> String { .collect() } -pub fn hash(string: &str) -> ApiResult { +pub fn hash(string: &str) -> CoreResult { let salt = SaltString::generate(&mut OsRng); let hash = Argon2::default() .hash_password(string.as_bytes(), &salt)? @@ -57,17 +43,6 @@ pub fn verify_hash(string: &str, hashed_string: &str) -> bool { .is_ok() } -pub fn verify_role(auth: &Auth, role: &str) -> ApiResult<()> { - if auth.user.role == role { - Ok(()) - } else { - Err(Error { - status: 403, - details: "User does not have permission to perform this action.".to_string(), - }) - } -} - #[cfg(test)] mod tests { use super::*; @@ -78,4 +53,4 @@ mod tests { assert!(!verify_hash(&password, "bad_password")); assert!(verify_hash("password", &password)); } -} +} \ No newline at end of file diff --git a/api/src/airports/model/airport.rs b/crates/lib/src/airports/airport.rs similarity index 97% rename from api/src/airports/model/airport.rs rename to crates/lib/src/airports/airport.rs index 9b0b922..fd5586e 100644 --- a/api/src/airports/model/airport.rs +++ b/crates/lib/src/airports/airport.rs @@ -2,8 +2,6 @@ use crate::airports::{ AirportCategory, Communication, CommunicationRow, Runway, RunwayRow, UpdateCommunication, UpdateRunway, }; -use crate::error::{ApiResult, Error}; -use crate::metars::Metar; use chrono::{DateTime, Utc}; use futures_util::try_join; use serde::{Deserialize, Serialize}; @@ -11,7 +9,8 @@ use sqlx::{Pool, Postgres, QueryBuilder}; use std::collections::HashMap; use std::str::FromStr; use utoipa::{IntoParams, ToSchema}; -use crate::state::AppState; +use crate::error::{CoreError, CoreErrorKind, CoreResult}; +use crate::metars::Metar; const TABLE_NAME: &str = "airports"; const DEFAULT_COLUMNS: &str = "icao, iata, local, name, category, iso_country, \ @@ -137,11 +136,11 @@ pub struct Bounds { } impl Bounds { - fn parse(input: &str) -> ApiResult { + fn parse(input: &str) -> CoreResult { let parts: Vec<&str> = input.split(',').collect(); if parts.len() != 4 { - return Err(Error::new( - 400, + return Err(CoreError::new( + CoreErrorKind::InvalidInput, format!("Expected 4 fields in bounds but received {}", parts.len()), )); } @@ -332,7 +331,7 @@ impl Airport { }) } - pub async fn select_all(pool: &Pool, query: &AirportQuery) -> ApiResult> { + pub async fn select_all(pool: &Pool, query: &AirportQuery) -> CoreResult> { let mut builder = QueryBuilder::::new(format!("SELECT {} FROM {}", DEFAULT_COLUMNS, TABLE_NAME)); @@ -487,7 +486,7 @@ impl Airport { sql_query.fetch_one(pool).await.unwrap_or_else(|_| 0) } - pub async fn insert(&self, pool: &Pool) -> ApiResult { + pub async fn insert(&self, pool: &Pool) -> CoreResult { let mut all_runway_rows: Vec = Vec::new(); let mut all_frequency_rows: Vec = Vec::new(); for runway in &self.runways { @@ -535,7 +534,7 @@ impl Airport { Ok(airport.into()) } - pub async fn insert_all(pool: &Pool, airports: Vec) -> ApiResult<()> { + pub async fn insert_all(pool: &Pool, airports: Vec) -> CoreResult<()> { let chunk_size = 1000; let mut all_runway_rows: Vec = Vec::new(); let mut all_frequency_rows: Vec = Vec::new(); @@ -592,7 +591,7 @@ impl Airport { } // TODO - pub async fn update(pool: &Pool, icao: &str, airport: &UpdateAirport) -> ApiResult<()> { + pub async fn update(pool: &Pool, icao: &str, airport: &UpdateAirport) -> CoreResult<()> { let mut query_builder: QueryBuilder = QueryBuilder::new(format!("UPDATE {} SET ", TABLE_NAME)); if let Some(latest_metar_observation) = airport.latest_metar_observation { @@ -607,7 +606,7 @@ impl Airport { Ok(()) } - pub async fn delete(pool: &Pool, icao: &str) -> ApiResult<()> { + pub async fn delete(pool: &Pool, icao: &str) -> CoreResult<()> { sqlx::query(&format!( r#" DELETE FROM {} WHERE icao = $1 @@ -621,7 +620,7 @@ impl Airport { Ok(()) } - pub async fn delete_all(pool: &Pool) -> ApiResult<()> { + pub async fn delete_all(pool: &Pool) -> CoreResult<()> { sqlx::query(&format!( r#" DELETE FROM {} WHERE true @@ -688,7 +687,7 @@ impl Airport { builder: &mut QueryBuilder<'a, Postgres>, has_where: &mut bool, field: &'a Option, - ) -> ApiResult<()> { + ) -> CoreResult<()> { // Query bounds if let Some(bounds_string) = field { if !*has_where { diff --git a/api/src/airports/model/airport_category.rs b/crates/lib/src/airports/airport_category.rs similarity index 100% rename from api/src/airports/model/airport_category.rs rename to crates/lib/src/airports/airport_category.rs diff --git a/api/src/airports/model/communication.rs b/crates/lib/src/airports/communication.rs similarity index 95% rename from api/src/airports/model/communication.rs rename to crates/lib/src/airports/communication.rs index fd1afc6..dd4ca44 100644 --- a/api/src/airports/model/communication.rs +++ b/crates/lib/src/airports/communication.rs @@ -1,9 +1,9 @@ -use crate::error::ApiResult; use serde::{Deserialize, Serialize}; use sqlx::{Pool, Postgres, QueryBuilder}; use std::collections::HashMap; use utoipa::ToSchema; use uuid::Uuid; +use crate::error::CoreResult; const TABLE_NAME: &str = "communications"; @@ -64,7 +64,7 @@ impl Communication { } } - pub async fn select_all_map(pool: &Pool, icaos: &Vec) -> ApiResult>> { + pub async fn select_all_map(pool: &Pool, icaos: &Vec) -> CoreResult>> { let frequency_rows: Vec = sqlx::query_as(&format!( r#"SELECT * FROM {} WHERE icao = ANY($1)"#, TABLE_NAME @@ -86,7 +86,7 @@ impl Communication { Ok(frequency_map) } - pub async fn select_all(pool: &Pool, icao: &str) -> ApiResult> { + pub async fn select_all(pool: &Pool, icao: &str) -> CoreResult> { let frequency_row: Vec = sqlx::query_as(&format!( r#" SELECT * FROM {} WHERE icao = $1 @@ -99,7 +99,7 @@ impl Communication { Ok(frequency_row.into_iter().map(From::from).collect()) } - pub async fn insert_all(pool: &Pool, communications: &Vec) -> ApiResult<()> { + pub async fn insert_all(pool: &Pool, communications: &Vec) -> CoreResult<()> { let chunk_size = 1000; for chunk in communications.chunks(chunk_size) { diff --git a/api/src/airports/model/mod.rs b/crates/lib/src/airports/mod.rs similarity index 100% rename from api/src/airports/model/mod.rs rename to crates/lib/src/airports/mod.rs diff --git a/api/src/airports/model/runway.rs b/crates/lib/src/airports/runway.rs similarity index 95% rename from api/src/airports/model/runway.rs rename to crates/lib/src/airports/runway.rs index 3910103..df544af 100644 --- a/api/src/airports/model/runway.rs +++ b/crates/lib/src/airports/runway.rs @@ -1,4 +1,4 @@ -use crate::error::ApiResult; +use crate::error::CoreResult; use serde::{Deserialize, Serialize}; use sqlx::{Pool, Postgres, QueryBuilder}; use std::collections::HashMap; @@ -63,7 +63,7 @@ impl Runway { } } - pub async fn select_all_map(pool: &Pool, icaos: &Vec) -> ApiResult>> { + pub async fn select_all_map(pool: &Pool, icaos: &Vec) -> CoreResult>> { let runway_rows: Vec = sqlx::query_as(&format!( r#"SELECT * FROM {} WHERE icao = ANY($1)"#, TABLE_NAME @@ -82,7 +82,7 @@ impl Runway { Ok(runway_map) } - pub async fn select_all(pool: &Pool, icao: &str) -> ApiResult> { + pub async fn select_all(pool: &Pool, icao: &str) -> CoreResult> { let runway_rows: Vec = sqlx::query_as(&format!( r#" SELECT * FROM {} WHERE icao = $1 @@ -95,7 +95,7 @@ impl Runway { Ok(runway_rows.into_iter().map(From::from).collect()) } - pub async fn insert_all(pool: &Pool, runways: &Vec) -> ApiResult<()> { + pub async fn insert_all(pool: &Pool, runways: &Vec) -> CoreResult<()> { let chunk_size = 1000; for chunk in runways.chunks(chunk_size) { diff --git a/crates/lib/src/error.rs b/crates/lib/src/error.rs new file mode 100644 index 0000000..1c05a12 --- /dev/null +++ b/crates/lib/src/error.rs @@ -0,0 +1,220 @@ +use std::fmt::{Display, Formatter}; +use std::sync::{MutexGuard, PoisonError}; +use regex::Regex; +use serde::de::StdError; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[non_exhaustive] +pub enum CoreErrorKind { + NotFound, + InvalidInput, + Conflict, + Unauthorized, + Forbidden, + PreconditionFailed, + Timeout, + Cancelled, + Unavailable, + Internal, + External, +} + + +#[derive(Debug)] +pub struct CoreError { + pub kind: CoreErrorKind, + pub message: String, + pub context: Vec<(&'static str, String)>, + source: Option> +} + +impl CoreError { + pub fn new(kind: CoreErrorKind, message: impl Into) -> Self { + Self { + kind, + message: message.into(), + context: vec![], + source: None, + } + } + + pub fn with_source(kind: CoreErrorKind, message: impl Into, source: impl StdError + Send + Sync + 'static) -> Self { + Self { + kind, + message: message.into(), + context: vec![], + source: Some(Box::new(source)), + } + } + + pub fn context(mut self, context: Vec<(&'static str, String)>) -> Self { + self.context = context; + self + } +} + +impl Display for CoreError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?} - {}", self.kind, self.message) + } +} + +impl StdError for CoreError { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + self.source.as_deref() + } +} + +pub type CoreResult = Result; + +pub fn not_found(entity: &'static str, id: impl Into) -> CoreError { + CoreError::new(CoreErrorKind::NotFound, format!("{entity} not found: {}", id.into())) +} + + +impl From for CoreError { + fn from(error: argon2::password_hash::Error) -> Self { + Self::new(CoreErrorKind::External, error.to_string()) + } +} + +impl From for CoreError { + fn from(error: std::io::Error) -> Self { + Self::new(CoreErrorKind::External, format!("Unknown IO error: {:?}", error)) + } +} + +impl From for CoreError { + fn from(error: redis::RedisError) -> Self { + Self::new(CoreErrorKind::External, format!("Unknown redis error: {:?}", error)) + } +} + +impl From for CoreError { + fn from(error: sqlx::Error) -> Self { + match error { + sqlx::Error::RowNotFound => CoreError::new(CoreErrorKind::NotFound, "Not found".to_string()), + sqlx::Error::ColumnIndexOutOfBounds { .. } => CoreError::new(CoreErrorKind::InvalidInput, error.to_string()), + sqlx::Error::ColumnNotFound { .. } => CoreError::new(CoreErrorKind::NotFound, error.to_string()), + sqlx::Error::ColumnDecode { .. } => CoreError::new(CoreErrorKind::External, error.to_string()), + sqlx::Error::Decode(_) => CoreError::new(CoreErrorKind::External, error.to_string()), + sqlx::Error::PoolTimedOut => CoreError::new(CoreErrorKind::External, error.to_string()), + sqlx::Error::PoolClosed => CoreError::new(CoreErrorKind::External, error.to_string()), + sqlx::Error::Tls(_) => CoreError::new(CoreErrorKind::External, error.to_string()), + sqlx::Error::Io(_) => CoreError::new(CoreErrorKind::External, error.to_string()), + sqlx::Error::Protocol(_) => CoreError::new(CoreErrorKind::External, error.to_string()), + sqlx::Error::Configuration(_) => CoreError::new(CoreErrorKind::External, error.to_string()), + sqlx::Error::AnyDriverError(_) => CoreError::new(CoreErrorKind::External, error.to_string()), + sqlx::Error::Database(err) => { + if let Some(code) = err.code() { + match code.trim() { + // Unique violation + "23505" => return CoreError::new(CoreErrorKind::External, err.to_string()), + _ => (), + } + } + CoreError::new(CoreErrorKind::External, err.to_string()) + } + sqlx::Error::Migrate(_) => CoreError::new(CoreErrorKind::External, error.to_string()), + sqlx::Error::TypeNotFound { type_name } => { + CoreError::new(CoreErrorKind::External, format!("Type not found: {}", type_name)) + } + sqlx::Error::WorkerCrashed => CoreError::new(CoreErrorKind::External, error.to_string()), + _ => CoreError::new(CoreErrorKind::External, error.to_string()), + } + } +} + +impl From for CoreError { + fn from(error: reqwest::Error) -> Self { + match error.status() { + Some(status_code) => { + if status_code.is_client_error() { + Self::new(CoreErrorKind::External, format!("Client reqwest error: {:?}", error)) + } else if status_code.is_server_error() { + Self::new(CoreErrorKind::External, format!("Server reqwest error: {:?}", error)) + } else { + Self::new(CoreErrorKind::External, format!("Unknown reqwest error: {:?}", error)) + } + } + _ => Self::new(CoreErrorKind::External, format!("Unknown reqwest error: {:?}", error)), + } + } +} + +impl From for CoreError { + fn from(error: s3::error::S3Error) -> Self { + match error { + s3::error::S3Error::Credentials(err) => { + Self::new(CoreErrorKind::External, format!("Unknown s3 credentials error: {:?}", err)) + } + s3::error::S3Error::FromUtf8(err) => { + Self::new(CoreErrorKind::External, format!("Unknown s3 from utf8 error: {:?}", err)) + } + s3::error::S3Error::FmtError(err) => { + Self::new(CoreErrorKind::External, format!("Unknown s3 fmt error: {:?}", err)) + } + s3::error::S3Error::HmacInvalidLength(err) => Self::new( + CoreErrorKind::External, + format!("Unknown s3 hmac invalid length error: {:?}", err), + ), + _ => { + let re = Regex::new(r"HTTP (\d{3})").unwrap(); + // Apply the regex to the input string + if let Some(captures) = re.captures(&error.to_string()) { + if let Some(http_code_str) = captures.get(1) { + if let Ok(http_code) = http_code_str.as_str().parse::() { + return Self::new(CoreErrorKind::External, error.to_string()).context(vec![("http_code", http_code.to_string())]); + } + } + } + Self::new(CoreErrorKind::External, format!("Unknown s3 error: {:?}", error)) + } + } + } +} + +impl From for CoreError { + fn from(error: std::env::VarError) -> Self { + Self::new( + CoreErrorKind::External, + format!("Unknown environment variable error: {:?}", error), + ) + } +} + +impl From for CoreError { + fn from(error: serde_json::Error) -> Self { + Self::new(CoreErrorKind::External, format!("Unknown serde_json error: {:?}", error)) + } +} + +impl<'a, T> From>> for CoreError { + fn from(_: PoisonError>) -> Self { + Self::new(CoreErrorKind::External, "Failed to acquire lock".to_string()) + } +} + +impl From for CoreError { + fn from(error: core::num::ParseIntError) -> Self { + Self::new(CoreErrorKind::External, format!("Integer parse error: {:?}", error)) + } +} + +impl From for CoreError { + fn from(error: core::num::ParseFloatError) -> Self { + Self::new(CoreErrorKind::External, format!("Float parse error: {:?}", error)) + } +} + +impl From for CoreError { + fn from(error: regex::Error) -> Self { + Self::new(CoreErrorKind::External, error.to_string()) + } +} + +impl From for CoreError { + fn from(error: chrono::ParseError) -> Self { + Self::new(CoreErrorKind::External, format!("Chrono parse error: {:?}", error)) + } +} diff --git a/api/src/http_client.rs b/crates/lib/src/http_client.rs similarity index 88% rename from api/src/http_client.rs rename to crates/lib/src/http_client.rs index 2271219..56f2623 100644 --- a/api/src/http_client.rs +++ b/crates/lib/src/http_client.rs @@ -1,4 +1,4 @@ -use crate::error::{ApiResult, Error}; +use crate::error::{CoreResult, CoreError, CoreErrorKind}; use governor::clock::DefaultClock; use governor::state::{InMemoryState, NotKeyed}; use governor::{Quota, RateLimiter}; @@ -18,7 +18,7 @@ pub struct HttpClient { } impl HttpClient { - pub fn new(default_retry_after: u64) -> ApiResult { + pub fn new(default_retry_after: u64) -> CoreResult { let mut client_builder = Client::builder() .timeout(Duration::from_secs(10)) .tls_built_in_root_certs(true); @@ -45,11 +45,11 @@ impl HttpClient { }) } - pub fn default() -> ApiResult { + pub fn default() -> CoreResult { Self::new(60) } - pub async fn get(&self, url: &str, etag: Option) -> ApiResult { + pub async fn get(&self, url: &str, etag: Option) -> CoreResult { self.limiter.until_ready().await; let mut request = self.client.get(url); @@ -81,8 +81,8 @@ impl HttpClient { } if response.status() != 200 { - return Err(Error::new( - response.status().as_u16(), + return Err(CoreError::new( + CoreErrorKind::External, format!("Request returned status {}", response.status()), )); } diff --git a/crates/lib/src/lib.rs b/crates/lib/src/lib.rs new file mode 100644 index 0000000..5c87f3e --- /dev/null +++ b/crates/lib/src/lib.rs @@ -0,0 +1,6 @@ +pub mod accounts; +pub mod airports; +pub mod metars; +pub mod http_client; +pub mod state; +pub mod error; diff --git a/api/src/metars/metar_check.rs b/crates/lib/src/metars/metar_check.rs similarity index 86% rename from api/src/metars/metar_check.rs rename to crates/lib/src/metars/metar_check.rs index 76203f8..039e3c0 100644 --- a/api/src/metars/metar_check.rs +++ b/crates/lib/src/metars/metar_check.rs @@ -1,7 +1,7 @@ -use crate::error::ApiResult; -use crate::metars::Metar; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; +use crate::error::CoreResult; +use crate::metars::model::Metar; use crate::state::AppState; #[derive(Debug, Serialize, Deserialize)] @@ -31,7 +31,7 @@ impl MetarCheck { } pub async fn get(state: &AppState, icao: &str) -> Option { - let result: ApiResult> = state.get(icao).await; + let result: CoreResult> = state.get(icao).await; match result { Ok(Some(value)) => match serde_json::from_str(&value) { Ok(result) => Some(result), @@ -48,7 +48,7 @@ impl MetarCheck { } } - pub async fn insert(&self, state: &AppState) -> ApiResult<()> { + pub async fn insert(&self, state: &AppState) -> CoreResult<()> { let value = serde_json::to_string(&self)?; state.set(self.icao.as_str(), &value).await?; diff --git a/api/src/metars/mod.rs b/crates/lib/src/metars/mod.rs similarity index 66% rename from api/src/metars/mod.rs rename to crates/lib/src/metars/mod.rs index 810a57a..ff66918 100644 --- a/api/src/metars/mod.rs +++ b/crates/lib/src/metars/mod.rs @@ -1,8 +1,7 @@ mod metar_check; mod model; -mod routes; mod utils; pub use metar_check::*; pub use model::*; -pub use routes::init_routes; +pub use utils::*; \ No newline at end of file diff --git a/api/src/metars/model.rs b/crates/lib/src/metars/model.rs similarity index 94% rename from api/src/metars/model.rs rename to crates/lib/src/metars/model.rs index 4e8b538..e1bd1fd 100644 --- a/api/src/metars/model.rs +++ b/crates/lib/src/metars/model.rs @@ -1,8 +1,6 @@ use crate::airports::{Airport, UpdateAirport}; -use crate::error::Error; -use crate::metars::MetarCheck; +use crate::error::{CoreError, CoreErrorKind, CoreResult}; use crate::metars::utils::parse_metar_time; -use crate::error::ApiResult; use chrono::{DateTime, Utc}; use flate2::read::GzDecoder; use reqwest::header::ETAG; @@ -16,6 +14,7 @@ use std::str::FromStr; use std::sync::OnceLock; use regex::Regex; use utoipa::ToSchema; +use crate::metars::metar_check::MetarCheck; use crate::state::AppState; static TIME_OFFSET: OnceLock = OnceLock::new(); @@ -87,12 +86,12 @@ pub enum ReportModifier { } impl FromStr for ReportModifier { - type Err = Error; + type Err = CoreError; fn from_str(s: &str) -> Result { match s { "AUTO" => Ok(ReportModifier::Auto), "COR" => Ok(ReportModifier::Corrected), - _ => Err(Error::new(400, format!("Invalid report modifier '{}'", s))), + _ => Err(CoreError::new(CoreErrorKind::InvalidInput, format!("Invalid report modifier '{}'", s))), } } } @@ -137,13 +136,13 @@ pub enum AutomatedStationType { } impl FromStr for AutomatedStationType { - type Err = Error; + type Err = CoreError; fn from_str(s: &str) -> Result { match s { "AO1" => Ok(AutomatedStationType::WithoutPrecipitationDiscriminator), "AO2" => Ok(AutomatedStationType::WithPrecipitationDiscriminator), - _ => Err(Error::new( - 400, + _ => Err(CoreError::new( + CoreErrorKind::InvalidInput, format!("Invalid automated station type '{}'", s), )), } @@ -279,7 +278,7 @@ struct MetarRow { } impl MetarRow { - async fn insert(&self, pool: &Pool) -> ApiResult<()> { + async fn insert(&self, pool: &Pool) -> CoreResult<()> { sqlx::query(&format!( r#" INSERT INTO {} ( @@ -305,7 +304,7 @@ impl MetarRow { Ok(()) } - async fn insert_all(pool: &Pool, metars: Vec) -> ApiResult<()> { + async fn insert_all(pool: &Pool, metars: Vec) -> CoreResult<()> { let chunk_size = 1000; for chunk in metars.chunks(chunk_size) { @@ -341,7 +340,7 @@ impl MetarRow { } impl Metar { - fn parse_multiple(pool: &Pool, metar_strings: &Vec<&str>) -> ApiResult> { + fn parse_multiple(pool: &Pool, metar_strings: &Vec<&str>) -> CoreResult> { let mut metars: Vec = vec![]; for metar_string in metar_strings { match Self::parse(pool, metar_string) { @@ -356,25 +355,26 @@ impl Metar { Ok(metars) } - fn parse(pool: &Pool, metar_string: &str) -> ApiResult { + fn parse(pool: &Pool, metar_string: &str) -> CoreResult { if metar_string.is_empty() { - return Err(Error::new( - 404, + return Err(CoreError::new( + CoreErrorKind::InvalidInput, "Unable to parse empty METAR data".to_string(), )); } + let metar_string = metar_string + .trim() + .trim_matches(|c| c == '"' || c == '\'' || c == '“' || c == '”' || c == '‘' || c == '’') + .trim(); + log::trace!("Parsing METAR data: {}", metar_string); let mut metar: Self = Self::default(); metar.raw_text = metar_string.to_owned(); - let mut metar_parts: Vec<&str> = metar_string - .trim() - .trim_matches(|c| c == '"' || c == '\'' || c == '“' || c == '”' || c == '‘' || c == '’') - .trim() - .split_whitespace().collect(); + let mut metar_parts: Vec<&str> = metar_string.split_whitespace().collect(); if metar_parts.len() < 4 { - return Err(Error::new( - 500, + return Err(CoreError::new( + CoreErrorKind::InvalidInput, format!( "Unable to parse METAR data in an unexpected format: {}", metar_string @@ -390,7 +390,8 @@ impl Metar { if metar_re.is_match(token) { metar_parts.remove(0); } else if speci_re.is_match(token) { - return Err(Error::new(500, format!("Unable to parse SPECI data: {}", metar_string))); + // TODO: Handle SPECI data + return Err(CoreError::new(CoreErrorKind::InvalidInput, format!("Unable to parse SPECI data: {}", metar_string))); } // Station Identifier @@ -408,8 +409,8 @@ impl Metar { }; } Err(err) => { - return Err(Error::new( - err.status, + return Err(CoreError::new( + CoreErrorKind::InvalidInput, format!( "Unexpected observation time field '{}': {}; {}", observation_time, metar_string, err @@ -722,8 +723,8 @@ impl Metar { if metar_parts.is_empty() { break; } - let slp_re = regex::Regex::new(r"^SLP([0-9]{3})$")?; - let hourly_temp_re = regex::Regex::new(r"^T[01][0-9]{3}[01][0-9]{3}$")?; + let slp_re = Regex::new(r"^SLP([0-9]{3})$")?; + let hourly_temp_re = Regex::new(r"^T[01][0-9]{3}[01][0-9]{3}$")?; let remark = metar_parts[0]; metar_parts.remove(0); if remark == "AO1" || remark == "AO2" { @@ -757,8 +758,8 @@ impl Metar { minutes, }); } else { - return Err(Error::new( - 500, + return Err(CoreError::new( + CoreErrorKind::InvalidInput, "Input string format is invalid".to_string(), )); } @@ -995,49 +996,45 @@ impl Metar { pub async fn get_cached_remote_metars( state: &AppState, etag: Option, - ) -> ApiResult<(Vec, String)> { + ) -> CoreResult<(Vec, String)> { let base_url = env::var("AVIATION_WEATHER_URL").expect("AVIATION_WEATHER_URL must be set"); let url = format!("{}/data/cache/metars.cache.csv.gz", base_url); - match state.client.get(&url, etag.clone()).await { - Ok(r) => { - let new_etag = r - .headers() - .get(ETAG) - .and_then(|h| h.to_str().ok()) - .map(|s| s.to_string()); + let response = state.client.get(&url, etag.clone()).await?; + let new_etag = response + .headers() + .get(ETAG) + .and_then(|h| h.to_str().ok()) + .map(|s| s.to_string()); - let bytes = r.bytes().await?; - let mut gz = GzDecoder::new(Cursor::new(bytes)); - let mut text = String::new(); - gz.read_to_string(&mut text)?; + let bytes = response.bytes().await?; + let mut gz = GzDecoder::new(Cursor::new(bytes)); + let mut text = String::new(); + gz.read_to_string(&mut text)?; - let mut output: Vec = Vec::new(); + let mut output: Vec = Vec::new(); - for line in text.lines() { - // Split off the first column - let raw_text = line.splitn(2, ',').next().unwrap(); - match Metar::parse(&state.pool, raw_text) { - Ok(m) => output.push(m), - Err(err) => { - log::warn!("{}", err); - } - }; + for line in text.lines() { + // Split off the first column + let raw_text = line.splitn(2, ',').next().unwrap(); + match Metar::parse(&state.pool, raw_text) { + Ok(m) => output.push(m), + Err(err) => { + log::warn!("{}", err); } + }; + } - match new_etag { - Some(etag) => Ok((output, etag)), - None => match etag { - Some(etag) => Ok((output, etag.to_string())), - None => Ok((output, String::new())), - }, - } - } - Err(err) => Err(err.into()), + match new_etag { + Some(etag) => Ok((output, etag)), + None => match etag { + Some(etag) => Ok((output, etag.to_string())), + None => Ok((output, String::new())), + }, } } - pub async fn get_remote_metars(state: &AppState, icaos: &Vec) -> ApiResult> { + pub async fn get_remote_metars(state: &AppState, icaos: &Vec) -> CoreResult> { let base_url = env::var("AVIATION_WEATHER_URL").expect("AVIATION_WEATHER_URL must be set"); // Query the remote API for the missing METAR data 10 at a time let icao_chunks = icaos @@ -1063,7 +1060,7 @@ impl Metar { Err(err) => return Err(err), } } - Err(err) => return Err(Error::new(500, format!("METAR parse failed: {}", err))), + Err(err) => return Err(CoreError::new(CoreErrorKind::InvalidInput, format!("METAR parse failed: {}", err))), }, Err(err) => return Err(err.into()), }; @@ -1072,12 +1069,12 @@ impl Metar { Ok(metars) } - fn from_row(row: MetarRow) -> ApiResult { + fn from_row(row: MetarRow) -> CoreResult { let metar: Self = serde_json::from_value(row.data)?; Ok(metar) } - fn to_row(&self) -> ApiResult { + fn to_row(&self) -> CoreResult { let data = serde_json::to_value(self)?; Ok(MetarRow { icao: self.icao.to_uppercase(), @@ -1087,7 +1084,7 @@ impl Metar { }) } - pub async fn get_all_distinct(pool: &Pool, icao_list: &Vec) -> ApiResult> { + pub async fn get_all_distinct(pool: &Pool, icao_list: &Vec) -> CoreResult> { if icao_list.is_empty() { return Ok(Vec::new()); } @@ -1113,7 +1110,7 @@ impl Metar { pub async fn get_or_update_metars( state: &AppState, icaos: &Vec, - ) -> ApiResult> { + ) -> CoreResult> { let metars = Self::get_all_distinct(&state.pool, &icaos).await?; let current_time = Utc::now().timestamp(); @@ -1215,7 +1212,7 @@ impl Metar { Ok(updated_metars) } - pub async fn update_metars(state: &AppState, etag: Option) -> ApiResult { + pub async fn update_metars(state: &AppState, etag: Option) -> CoreResult { let (remote_metars, etag) = Self::get_cached_remote_metars(state, etag) .await .unwrap_or_else(|err| { @@ -1227,7 +1224,7 @@ impl Metar { Ok(etag) } - pub async fn insert(&self, pool: &Pool) -> ApiResult<()> { + pub async fn insert(&self, pool: &Pool) -> CoreResult<()> { log::trace!( "Inserting metar {} with observation time {}", self.icao, diff --git a/api/src/metars/utils.rs b/crates/lib/src/metars/utils.rs similarity index 88% rename from api/src/metars/utils.rs rename to crates/lib/src/metars/utils.rs index edd50f0..ebcd968 100644 --- a/api/src/metars/utils.rs +++ b/crates/lib/src/metars/utils.rs @@ -1,10 +1,10 @@ -use crate::error::{ApiResult, Error}; +use crate::error::{CoreError, CoreErrorKind, CoreResult}; use chrono::{Datelike, NaiveDate, Utc}; -pub fn parse_metar_time(observation_time: &str) -> ApiResult { +pub fn parse_metar_time(observation_time: &str) -> CoreResult { if observation_time.len() != 7 { - return Err(Error::new( - 500, + return Err(CoreError::new( + CoreErrorKind::InvalidInput, format!("Unable to parse observation time in {}", observation_time), )); } @@ -25,8 +25,8 @@ pub fn parse_metar_time(observation_time: &str) -> ApiResult { let current_month = current_time.month(); let candidate_date = NaiveDate::from_ymd_opt(current_year, current_month, observation_day) .ok_or_else(|| { - Error::new( - 500, + CoreError::new( + CoreErrorKind::InvalidInput, format!( "Invalid date with day {} for current month", observation_day @@ -36,8 +36,8 @@ pub fn parse_metar_time(observation_time: &str) -> ApiResult { let candidate_date = match candidate_date.and_hms_opt(observation_hour, observation_minute, 0) { Some(date) => date, None => { - return Err(Error::new( - 500, + return Err(CoreError::new( + CoreErrorKind::InvalidInput, format!( "Invalid time for time '{}': hour {}, minute {}", observation_time, observation_hour, observation_minute @@ -55,8 +55,8 @@ pub fn parse_metar_time(observation_time: &str) -> ApiResult { }; let adjusted_date = NaiveDate::from_ymd_opt(year, month, observation_day).ok_or_else(|| { - Error::new( - 500, + CoreError::new( + CoreErrorKind::InvalidInput, format!( "Invalid date with day {} for month {}", observation_day, month diff --git a/api/src/state.rs b/crates/lib/src/state.rs similarity index 94% rename from api/src/state.rs rename to crates/lib/src/state.rs index 133ef8d..a711e19 100644 --- a/api/src/state.rs +++ b/crates/lib/src/state.rs @@ -7,7 +7,7 @@ use s3::{Bucket, BucketConfiguration, Region}; use s3::creds::Credentials; use sqlx::{Pool, Postgres}; use sqlx::postgres::PgPoolOptions; -use crate::error::ApiResult; +use crate::error::CoreResult; use crate::http_client::HttpClient; #[derive(Clone)] @@ -19,7 +19,7 @@ pub struct AppState { } impl AppState { - pub async fn new() -> ApiResult { + pub async fn new() -> CoreResult { let client = HttpClient::default()?; let pool: Pool = { @@ -139,19 +139,19 @@ impl AppState { }) } - pub async fn set(&self, key: &str, value: &str) -> ApiResult<()> { + pub async fn set(&self, key: &str, value: &str) -> CoreResult<()> { let mut connection_manager = self.connection_manager.lock()?; connection_manager.set(key, value).await?; Ok(()) } - pub async fn set_ex(&self, key: &str, value: &str, seconds: u64) -> ApiResult<()> { + pub async fn set_ex(&self, key: &str, value: &str, seconds: u64) -> CoreResult<()> { let mut connection_manager = self.connection_manager.lock()?; connection_manager.set_ex(key, value, seconds).await?; Ok(()) } - pub async fn get(&self, key: &str) -> ApiResult> { + pub async fn get(&self, key: &str) -> CoreResult> { let mut connection_manager = self.connection_manager.lock()?; match connection_manager.get(key).await { Ok(value) => Ok(value), @@ -159,7 +159,7 @@ impl AppState { } } - pub async fn del(&self, key: &str) -> ApiResult<()> { + pub async fn del(&self, key: &str) -> CoreResult<()> { let mut connection_manager = self.connection_manager.lock()?; connection_manager.del(key).await?; Ok(()) diff --git a/crates/scheduler/Cargo.toml b/crates/scheduler/Cargo.toml new file mode 100644 index 0000000..ae263a0 --- /dev/null +++ b/crates/scheduler/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "scheduler" +version = "0.1.0" +edition = "2024" + +[dependencies] +lib = { path = "../lib" } +chrono = "0.4.42" +tokio = { version = "1.47.1", features = ["rt", "rt-multi-thread"] } +log = "0.4.28" +env_logger = "0.11.8" diff --git a/crates/scheduler/Dockerfile b/crates/scheduler/Dockerfile new file mode 100644 index 0000000..23ec751 --- /dev/null +++ b/crates/scheduler/Dockerfile @@ -0,0 +1,24 @@ +# ========= +# Builder +# ========= +FROM rust:bookworm AS builder +WORKDIR /builder + +COPY crates/lib /lib +COPY crates/scheduler/src ./src +COPY crates/scheduler/Cargo.toml ./ + +RUN apt-get update && apt-get install -y cmake +RUN cargo build --release + +# ========= +# Runtime +# ========= +FROM debian:bookworm-slim AS runtime +WORKDIR /scheduler +RUN apt-get update && apt-get install -y openssl libpq-dev ca-certificates +USER root + +COPY --from=builder /builder/target/release/scheduler /usr/local/bin/scheduler + +CMD ["scheduler"] diff --git a/crates/scheduler/src/main.rs b/crates/scheduler/src/main.rs new file mode 100644 index 0000000..170e96a --- /dev/null +++ b/crates/scheduler/src/main.rs @@ -0,0 +1,56 @@ +use chrono::{DateTime, Utc}; +use std::env; +use std::time::{Duration, Instant}; +use env_logger::Builder; +use log::LevelFilter; +use tokio::time::interval; +use lib::metars::Metar; +use lib::state::AppState; + +#[tokio::main] +pub async fn main() { + Builder::new() + .filter_level(LevelFilter::Info) // Set a default log level + .filter_module("scheduler", LevelFilter::Trace) + .filter_module("lib", LevelFilter::Trace) + .init(); + + let state = match AppState::new().await { + Ok(state) => state, + Err(err) => { + log::error!("Failed to create state: {}", err); + return; + } + }; + let seconds = env::var("METAR_INTERVAL") + .unwrap_or("300".to_string()) + .parse::() + .unwrap_or(300); + + // Create an interval ticker + let mut interval = interval(Duration::from_secs(seconds)); + let mut etag = None; + + loop { + interval.tick().await; + + // Record start times + let start_monotonic = Instant::now(); + let start_utc: DateTime = Utc::now(); + log::debug!("METAR update started at {}", start_utc); + + // Run the update + match Metar::update_metars(&state, etag.clone()).await { + Ok(new_etag) => etag = Some(new_etag), + Err(err) => log::error!("METAR update failed: {}", err), + } + + let elapsed = start_monotonic.elapsed(); + let next_utc = Utc::now() + chrono::Duration::from_std(Duration::from_secs(seconds)).unwrap(); + log::info!( + "METAR update finished in {:.2?}; next run at {}", + elapsed, + next_utc + ); + } +} diff --git a/docker-compose.yml b/docker-compose.yml index 7f90047..b6b0a93 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,7 +14,7 @@ services: container_name: aviation-nginx build: context: . - dockerfile: Dockerfile + dockerfile: nginx/Dockerfile env_file: *env environment: SSL_CERT_PATH: /etc/nginx/ssl/localhost.crt @@ -84,7 +84,7 @@ services: container_name: aviation-api build: context: . - dockerfile: Dockerfile + dockerfile: crates/api/Dockerfile env_file: *env environment: SSL_CA_PATH: /ssl/ca.pem @@ -109,6 +109,22 @@ services: - api <<: *default_restart + scheduler: + image: gitea.bensherriff.com/bsherriff/aviation-scheduler:latest + container_name: aviation-scheduler + build: + context: . + dockerfile: crates/scheduler/Dockerfile + env_file: *env + environment: + POSTGRES_HOST: aviation-postgres + POSTGRES_PORT: 5432 + depends_on: + - postgres + profiles: + - api + <<: *default_restart + mailpit: image: axllent/mailpit container_name: mailpit diff --git a/api/rust-toolchain.toml b/rust-toolchain.toml similarity index 100% rename from api/rust-toolchain.toml rename to rust-toolchain.toml diff --git a/api/rustfmt.toml b/rustfmt.toml similarity index 100% rename from api/rustfmt.toml rename to rustfmt.toml