diff options
author | rtkay123 <dev@kanjala.com> | 2025-08-10 12:55:43 +0200 |
---|---|---|
committer | rtkay123 <dev@kanjala.com> | 2025-08-10 12:55:43 +0200 |
commit | bd31dc85f8e9cb01c1e1a4e49fd4735d24a6da04 (patch) | |
tree | 50b63525480da0bee2ce713d69f02617c20bee8d | |
parent | 8deeab3e11f707677609047f5577a256cf28ed63 (diff) | |
download | warden-bd31dc85f8e9cb01c1e1a4e49fd4735d24a6da04.tar.bz2 warden-bd31dc85f8e9cb01c1e1a4e49fd4735d24a6da04.zip |
chore: collapse stack-up
25 files changed, 2715 insertions, 52 deletions
@@ -39,6 +39,12 @@ dependencies = [ ] [[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] name = "anstream" version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -104,6 +110,48 @@ dependencies = [ ] [[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + +[[package]] +name = "async-nats" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f6da6d49a956424ca4e28fe93656f790d748b469eaccbc7488fec545315180" +dependencies = [ + "base64", + "bytes", + "futures", + "memchr", + "nkeys", + "nuid", + "once_cell", + "pin-project", + "portable-atomic", + "rand 0.8.5", + "regex", + "ring", + "rustls-native-certs", + "rustls-pemfile", + "rustls-webpki 0.102.8", + "serde", + "serde_json", + "serde_nanos", + "serde_repr", + "thiserror 1.0.69", + "time", + "tokio", + "tokio-rustls", + "tokio-util", + "tokio-websockets", + "tracing", + "tryhard", + "url", +] + +[[package]] name = "async-trait" version = "0.1.88" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -115,12 +163,27 @@ dependencies = [ ] [[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + +[[package]] name = "atomic-waker" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] name = "axum" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -187,6 +250,15 @@ dependencies = [ ] [[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" @@ -208,10 +280,40 @@ 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 = "bb8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212d8b8e1a22743d9241575c6ba822cf9c8fef34771c86ab7e477a4fbfd254e5" +dependencies = [ + "futures-util", + "parking_lot", + "tokio", +] + +[[package]] +name = "bb8-redis" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5143936af5e1eea1a881e3e3d21b6777da6315e5e307bc3d0c2301c44fa37da9" +dependencies = [ + "bb8", + "redis", +] + +[[package]] name = "bitflags" version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +dependencies = [ + "serde", +] [[package]] name = "block-buffer" @@ -254,10 +356,19 @@ 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" +dependencies = [ + "serde", +] [[package]] name = "cc" @@ -327,6 +438,29 @@ 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 = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] name = "config" version = "0.15.13" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -340,6 +474,12 @@ dependencies = [ ] [[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] name = "convert_case" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -349,6 +489,22 @@ dependencies = [ ] [[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" @@ -358,6 +514,27 @@ dependencies = [ ] [[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 = "crc16" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "338089f42c427b86394a5ee60ff321da23a5c89c9d89514c829687b26359fcff" + +[[package]] name = "crc32fast" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -376,6 +553,15 @@ dependencies = [ ] [[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" @@ -392,6 +578,32 @@ dependencies = [ ] [[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version", + "subtle", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "darling" version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -427,6 +639,23 @@ dependencies = [ ] [[package]] +name = "data-encoding" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] name = "deranged" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -454,7 +683,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", + "const-oid", "crypto-common", + "subtle", ] [[package]] @@ -469,10 +700,41 @@ dependencies = [ ] [[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "sha2", + "signature", + "subtle", +] + +[[package]] name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +dependencies = [ + "serde", +] [[package]] name = "equivalent" @@ -491,12 +753,40 @@ dependencies = [ ] [[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 = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] name = "fixedbitset" version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -514,6 +804,17 @@ dependencies = [ ] [[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" @@ -535,12 +836,27 @@ dependencies = [ ] [[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] name = "futures-channel" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -561,6 +877,23 @@ dependencies = [ ] [[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" @@ -589,10 +922,13 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ + "futures-channel", "futures-core", + "futures-io", "futures-macro", "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", "slab", @@ -666,16 +1002,60 @@ version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ + "allocator-api2", + "equivalent", "foldhash", ] [[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown", +] + +[[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] name = "http" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -756,7 +1136,7 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", - "webpki-roots", + "webpki-roots 1.0.2", ] [[package]] @@ -983,6 +1363,9 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] [[package]] name = "libc" @@ -991,6 +1374,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + +[[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 = "libz-rs-sys" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1012,6 +1411,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[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.27" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1049,6 +1458,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + +[[package]] name = "memchr" version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1075,7 +1494,7 @@ dependencies = [ "metrics", "metrics-util", "quanta", - "thiserror", + "thiserror 2.0.12", ] [[package]] @@ -1089,7 +1508,7 @@ dependencies = [ "hashbrown", "metrics", "quanta", - "rand", + "rand 0.9.2", "rand_xoshiro", "sketches-ddsketch", ] @@ -1137,6 +1556,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" [[package]] +name = "nkeys" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879011babc47a1c7fdf5a935ae3cfe94f34645ca0cac1c7f6424b36fc743d1bf" +dependencies = [ + "data-encoding", + "ed25519", + "ed25519-dalek", + "getrandom 0.2.16", + "log", + "rand 0.8.5", + "signatory", +] + +[[package]] name = "nu-ansi-term" version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1147,12 +1581,78 @@ dependencies = [ ] [[package]] +name = "nuid" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc895af95856f929163a0aa20c26a78d26bfdc839f51b9d5aa7a5b79e52b7e83" +dependencies = [ + "rand 0.8.5", +] + +[[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-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] name = "object" version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1174,6 +1674,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" [[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] name = "opentelemetry" version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1183,7 +1689,7 @@ dependencies = [ "futures-sink", "js-sys", "pin-project-lite", - "thiserror", + "thiserror 2.0.12", "tracing", ] @@ -1211,7 +1717,7 @@ dependencies = [ "opentelemetry-proto", "opentelemetry_sdk", "prost 0.13.5", - "thiserror", + "thiserror 2.0.12", "tokio", "tonic 0.13.1", ] @@ -1245,8 +1751,8 @@ dependencies = [ "futures-util", "opentelemetry", "percent-encoding", - "rand", - "thiserror", + "rand 0.9.2", + "thiserror 2.0.12", "tokio", "tokio-stream", ] @@ -1258,6 +1764,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[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 = "paste" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1270,6 +1805,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" [[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1318,6 +1862,33 @@ 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" @@ -1501,7 +2072,7 @@ dependencies = [ "rustc-hash", "rustls", "socket2 0.5.10", - "thiserror", + "thiserror 2.0.12", "tokio", "tracing", "web-time", @@ -1516,13 +2087,13 @@ dependencies = [ "bytes", "getrandom 0.3.3", "lru-slab", - "rand", + "rand 0.9.2", "ring", "rustc-hash", "rustls", "rustls-pki-types", "slab", - "thiserror", + "thiserror 2.0.12", "tinyvec", "tracing", "web-time", @@ -1559,12 +2130,33 @@ checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[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", - "rand_core", + "rand_chacha 0.9.0", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", ] [[package]] @@ -1574,7 +2166,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", ] [[package]] @@ -1592,7 +2193,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f703f4665700daf5512dcca5f43afa6af89f09db47fb56be587f80636bda2d41" dependencies = [ - "rand_core", + "rand_core 0.9.3", ] [[package]] @@ -1605,6 +2206,43 @@ dependencies = [ ] [[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", + "crc16", + "futures-channel", + "futures-sink", + "futures-util", + "itoa", + "log", + "num-bigint", + "percent-encoding", + "pin-project-lite", + "rand 0.9.2", + "ryu", + "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.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1683,7 +2321,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots", + "webpki-roots 1.0.2", ] [[package]] @@ -1701,6 +2339,26 @@ dependencies = [ ] [[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 = "rust-embed" version = "8.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1747,6 +2405,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[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.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1768,12 +2435,34 @@ dependencies = [ "once_cell", "ring", "rustls-pki-types", - "rustls-webpki", + "rustls-webpki 0.103.4", "subtle", "zeroize", ] [[package]] +name = "rustls-native-certs" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] name = "rustls-pki-types" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1785,6 +2474,16 @@ dependencies = [ [[package]] name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustls-webpki" version = "0.103.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" @@ -1816,6 +2515,60 @@ dependencies = [ ] [[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "secrecy" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a" +dependencies = [ + "serde", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" + +[[package]] name = "serde" version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1848,6 +2601,15 @@ dependencies = [ ] [[package]] +name = "serde_nanos" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a93142f0367a4cc53ae0fead1bcda39e85beccfad3dcd717656cacab94b12985" +dependencies = [ + "serde", +] + +[[package]] name = "serde_path_to_error" version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1858,6 +2620,17 @@ dependencies = [ ] [[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "serde_spanned" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1879,6 +2652,17 @@ dependencies = [ ] [[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] name = "sha2" version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1914,6 +2698,28 @@ dependencies = [ ] [[package]] +name = "signatory" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1e303f8205714074f6068773f0e29527e0453937fe837c9717d066635b65f31" +dependencies = [ + "pkcs8", + "rand_core 0.6.4", + "signature", + "zeroize", +] + +[[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" @@ -1936,6 +2742,9 @@ name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +dependencies = [ + "serde", +] [[package]] name = "snap" @@ -1964,29 +2773,227 @@ dependencies = [ ] [[package]] -name = "stable_deref_trait" -version = "1.2.0" +name = "spin" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] [[package]] -name = "stack-up" -version = "0.1.0" -source = "git+https://github.com/rtkay123/stack-up#3bd3765d8354e9cdcda01febafd26f79c94688e7" +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ - "bon", - "opentelemetry", - "opentelemetry-http", - "opentelemetry-otlp", - "opentelemetry-semantic-conventions", - "opentelemetry_sdk", + "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", + "crc", + "crossbeam-queue", + "either", + "event-listener", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashbrown", + "hashlink", + "indexmap", + "log", + "memchr", + "once_cell", + "percent-encoding", "serde", "serde_json", - "thiserror", + "sha2", + "smallvec", + "thiserror 2.0.12", + "tokio", + "tokio-stream", "tracing", - "tracing-loki", - "tracing-opentelemetry", - "tracing-subscriber", + "url", +] + +[[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", + "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 2.0.12", + "tracing", + "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", + "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 2.0.12", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea" +dependencies = [ + "atoi", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "serde_urlencoded", + "sqlx-core", + "thiserror 2.0.12", + "tracing", + "url", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", ] [[package]] @@ -2047,11 +3054,31 @@ dependencies = [ [[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", + "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", ] [[package]] @@ -2141,6 +3168,7 @@ dependencies = [ "io-uring", "libc", "mio", + "parking_lot", "pin-project-lite", "signal-hook-registry", "slab", @@ -2195,6 +3223,27 @@ dependencies = [ ] [[package]] +name = "tokio-websockets" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f591660438b3038dd04d16c938271c79e7e06260ad2ea2885a4861bfb238605d" +dependencies = [ + "base64", + "bytes", + "futures-core", + "futures-sink", + "http", + "httparse", + "rand 0.8.5", + "ring", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tokio-util", + "webpki-roots 0.26.11", +] + +[[package]] name = "toml" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2500,6 +3549,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] +name = "tryhard" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fe58ebd5edd976e0fe0f8a14d2a04b7c81ef153ea9a54eebc42e67c2c23b4e5" +dependencies = [ + "pin-project-lite", + "tokio", +] + +[[package]] name = "typenum" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2512,12 +3571,33 @@ 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.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-properties" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" + +[[package]] name = "unicode-segmentation" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2538,6 +3618,7 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] [[package]] @@ -2661,6 +3742,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[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" @@ -2697,7 +3784,6 @@ dependencies = [ "metrics-exporter-prometheus", "serde", "serde_json", - "stack-up", "time", "tokio", "tower", @@ -2710,6 +3796,7 @@ dependencies = [ "utoipa-scalar", "utoipa-swagger-ui", "warden-core", + "warden-stack", ] [[package]] @@ -2731,7 +3818,46 @@ dependencies = [ name = "warden-pseudonyms" version = "0.1.0" dependencies = [ + "anyhow", + "clap", + "config", + "metrics", + "metrics-exporter-prometheus", + "serde", + "serde_json", + "time", + "tokio", + "tracing", "warden-core", + "warden-stack", +] + +[[package]] +name = "warden-stack" +version = "0.1.0" +dependencies = [ + "async-nats", + "bb8", + "bb8-redis", + "bon", + "opentelemetry", + "opentelemetry-http", + "opentelemetry-otlp", + "opentelemetry-semantic-conventions", + "opentelemetry_sdk", + "redis", + "secrecy", + "serde", + "serde_json", + "sqlx", + "thiserror 2.0.12", + "tokio", + "tonic 0.14.0", + "tracing", + "tracing-loki", + "tracing-opentelemetry", + "tracing-subscriber", + "url", ] [[package]] @@ -2750,6 +3876,12 @@ dependencies = [ ] [[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + +[[package]] name = "wasm-bindgen" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2842,6 +3974,15 @@ dependencies = [ [[package]] name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.2", +] + +[[package]] +name = "webpki-roots" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" @@ -2850,6 +3991,16 @@ dependencies = [ ] [[package]] +name = "whoami" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6994d13118ab492c3c80c1f81928718159254c53c472bf9ce36f8dae4add02a7" +dependencies = [ + "redox_syscall", + "wasite", +] + +[[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2888,6 +4039,15 @@ checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[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" @@ -2915,6 +4075,21 @@ dependencies = [ [[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" @@ -2948,6 +4123,12 @@ dependencies = [ [[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" @@ -2960,6 +4141,12 @@ 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" @@ -2972,6 +4159,12 @@ 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" @@ -2996,6 +4189,12 @@ 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" @@ -3008,6 +4207,12 @@ 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" @@ -3020,6 +4225,12 @@ 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" @@ -3032,6 +4243,12 @@ 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" @@ -10,15 +10,27 @@ description = "A rule-based fraud detection platform" [workspace.dependencies] anyhow = "1.0.98" +async-nats = "0.42.0" axum = "0.8.4" +bon = "3.4.0" clap = "4.5.43" config = { version = "0.15.13", default-features = false } metrics = { version = "0.24.2", default-features = false } metrics-exporter-prometheus = { version = "0.17.2", default-features = false } +opentelemetry = { version = "0.30.0", default-features = false } +opentelemetry-http = "0.30.0" +opentelemetry-otlp = { version = "0.30.0", default-features = false } +opentelemetry-semantic-conventions = { version = "0.30.0", default-features = false } +opentelemetry_sdk = { version = "0.30.0", default-features = false } prost = "0.14.1" +redis = { version = "0.32.5", default-features = false } +secrecy = "0.10.3" serde = "1.0.219" serde_json = "1.0.142" -stack-up = { git = "https://github.com/rtkay123/stack-up" } +sqlx = { version = "0.8.6", default-features = false } +thiserror = "2.0.12" +tracing-opentelemetry = "0.31.0" +url = "2.5.4" time = "0.3.41" tokio = "1.47.1" tonic = "0.14.0" @@ -32,3 +44,4 @@ utoipa-redoc = "6.0.0" utoipa-scalar = "0.3.0" utoipa-swagger-ui = "9.0.2" warden-core = { path = "lib/warden-core" } +warden-stack = { path = "lib/warden-stack" } @@ -1,7 +1,7 @@ # warden ```sh -git clone git://kanjala.com/warden.git +git clone https://github.com/rtkay123/warden.git cd warden git submodule update --init --depth 1 --recommend-shallow ``` diff --git a/crates/pseudonyms/Cargo.toml b/crates/pseudonyms/Cargo.toml index 4ddf179..02ecddc 100644 --- a/crates/pseudonyms/Cargo.toml +++ b/crates/pseudonyms/Cargo.toml @@ -8,4 +8,18 @@ documentation.workspace = true description.workspace = true [dependencies] +anyhow.workspace = true +clap = { workspace = true, features = ["derive"] } +config = { workspace = true, features = ["convert-case", "toml"] } +metrics.workspace = true +metrics-exporter-prometheus.workspace = true +serde = { workspace = true, features = ["derive"] } +serde_json.workspace = true +time.workspace = true +tokio = { workspace = true, features = ["macros", "rt-multi-thread", "signal"] } +tracing.workspace = true warden-core = { workspace = true, features = ["pseudonyms", "serde-time"] } + +[dependencies.warden-stack] +workspace = true +features = ["api", "cache", "postgres", "opentelemetry", "tracing-loki"] diff --git a/crates/warden/Cargo.toml b/crates/warden/Cargo.toml index b10ed45..dde983b 100644 --- a/crates/warden/Cargo.toml +++ b/crates/warden/Cargo.toml @@ -42,6 +42,6 @@ scalar = ["dep:utoipa-scalar", "utoipa-scalar/axum"] [dev-dependencies] tower = { workspace = true, features = ["util"] } -[dependencies.stack-up] +[dependencies.warden-stack] workspace = true features = ["api", "opentelemetry", "tracing-loki"] diff --git a/crates/warden/src/main.rs b/crates/warden/src/main.rs index b649280..68b185c 100644 --- a/crates/warden/src/main.rs +++ b/crates/warden/src/main.rs @@ -7,7 +7,7 @@ mod version; use std::net::{Ipv6Addr, SocketAddr}; use clap::{Parser, command}; -use stack_up::{Configuration, tracing::Tracing}; +use warden_stack::{Configuration, tracing::Tracing}; use tracing::info; use crate::state::AppState; diff --git a/crates/warden/src/server.rs b/crates/warden/src/server.rs index ce01fb8..6712516 100644 --- a/crates/warden/src/server.rs +++ b/crates/warden/src/server.rs @@ -58,8 +58,8 @@ pub async fn health_check() -> impl axum::response::IntoResponse { } #[cfg(test)] -pub(crate) fn test_config() -> stack_up::Configuration { - use stack_up::Configuration; +pub(crate) fn test_config() -> warden_stack::Configuration { + use warden_stack::Configuration; let config_path = "warden.toml"; diff --git a/crates/warden/src/server/routes/processor/pacs008.rs b/crates/warden/src/server/routes/processor/pacs008.rs index 87f1fee..3efc7f1 100644 --- a/crates/warden/src/server/routes/processor/pacs008.rs +++ b/crates/warden/src/server/routes/processor/pacs008.rs @@ -1,10 +1,10 @@ use axum::{extract::State, response::IntoResponse}; -use stack_up::tracing_opentelemetry::OpenTelemetrySpanExt; +use warden_stack::tracing_opentelemetry::OpenTelemetrySpanExt; use tracing::{debug, error, trace, warn}; use warden_core::{ google::r#type::Money, - iso20022::{TransactionType, pacs008::Pacs008Document}, - message::DataCache, + iso20022::{pacs008::Pacs008Document, TransactionType}, + message::DataCache, pseudonyms::transaction_relationship::{CreatePseudonymRequest, TransactionRelationship}, }; use crate::{error::AppError, server::routes::PACS008_001_12, state::AppHandle, version::Version}; @@ -99,6 +99,29 @@ pub(super) async fn post_pacs008( None }; + let transaction_relationship = TransactionRelationship { + from: data_cache.dbtr_acct_id.to_string(), + to: data_cache.cdtr_acct_id.to_string(), + amt: money, + cre_dt_tm: data_cache.cre_dt_tm, + end_to_end_id: end_to_end_id.to_string(), + msg_id: msg_id.to_string(), + pmt_inf_id: pmt_inf_id.into(), + tx_tp: tx_tp.to_owned(), + ..Default::default() + }; + + let request = CreatePseudonymRequest { + transaction_relationship: Some(transaction_relationship), + debtor_id: data_cache.dbtr_id.to_string(), + debtor_account_id: data_cache.dbtr_acct_id.to_string(), + creditor_id: data_cache.cdtr_id.to_string(), + creditor_account_id: data_cache.cdtr_acct_id.to_string(), + }; + + + debug!(%msg_id, %end_to_end_id, "constructed transaction relationship"); + Ok(String::default()) } diff --git a/crates/warden/src/state.rs b/crates/warden/src/state.rs index aabcaab..8e5b182 100644 --- a/crates/warden/src/state.rs +++ b/crates/warden/src/state.rs @@ -1,4 +1,4 @@ -use stack_up::{Configuration, Environment}; +use warden_stack::{Configuration, Environment}; use std::{ops::Deref, sync::Arc}; use crate::{cnfg::LocalConfig, error::AppError}; diff --git a/lib/warden-core/build.rs b/lib/warden-core/build.rs index 57e20e0..6d5efbb 100644 --- a/lib/warden-core/build.rs +++ b/lib/warden-core/build.rs @@ -9,17 +9,11 @@ enum Entity { #[cfg(any(feature = "message", feature = "pseudonyms"))] impl Entity { fn protos(&self) -> Vec<&'static str> { - let mut res: Vec<&'static str> = vec![ - // "proto/googleapis/google/type/date.proto", - // "proto/googleapis/google/type/money.proto", - // "proto/googleapis/google/type/latlng.proto", - ]; + let mut res: Vec<&'static str> = vec![]; #[cfg(feature = "message")] fn iso20022_protos() -> Vec<&'static str> { vec![ - // "proto/iso20022/pacs_008_001_12.proto", - // "proto/iso20022/pacs_002_001_12.proto", "proto/warden_message.proto", ] } @@ -60,7 +54,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> { #[cfg(feature = "pseudonyms")] protos.extend(Entity::Pseudonyms.protos()); -#[cfg(any(feature = "message", feature = "pseudonyms"))] + #[cfg(any(feature = "message", feature = "pseudonyms"))] build_proto(&protos)?; Ok(()) diff --git a/lib/warden-stack/Cargo.toml b/lib/warden-stack/Cargo.toml new file mode 100644 index 0000000..d7c1eb8 --- /dev/null +++ b/lib/warden-stack/Cargo.toml @@ -0,0 +1,75 @@ +[package] +name = "warden-stack" +version = "0.1.0" +edition = "2024" +license = "MIT OR Apache-2.0" + +[dependencies] +async-nats = { workspace = true, optional = true } +bb8 = { version = "0.9.0", optional = true } +bb8-redis = { version = "0.24.0", optional = true } +bon.workspace = true +opentelemetry = { workspace = true, optional = true } +opentelemetry-http = { workspace = true, optional = true } +opentelemetry-otlp = { workspace = true, optional = true } +opentelemetry-semantic-conventions = { workspace = true, optional = true } +opentelemetry_sdk = { workspace = true, optional = true } +redis = { workspace = true, optional = true } +secrecy = { workspace = true, optional = true } +serde = { workspace = true, features = ["derive", "rc"] } +serde_json.workspace = true +sqlx = { workspace = true, optional = true } +thiserror.workspace = true +tokio = { workspace = true, optional = true } +tonic = { workspace = true, optional = true } +tracing = { workspace = true, optional = true } +tracing-loki = { version = "0.2.6", optional = true, default-features = false, features = ["compat-0-2-1", "rustls"] } +tracing-opentelemetry = { workspace = true, optional = true } +tracing-subscriber = { version = "0.3.19", optional = true } +url = { workspace = true, optional = true } + +[features] +default = [] +api = [] +cache = [ + "dep:redis", + "redis/cluster-async", + "redis/connection-manager", + "redis/tokio-comp", + "redis/sentinel", + "tokio/sync", + "dep:bb8", + "dep:bb8-redis", + "url/serde", +] +nats-core = ["dep:async-nats"] +nats-jetstream = ["dep:async-nats"] +opentelemetry = [ + "dep:opentelemetry", + "dep:tracing-opentelemetry", + "tracing", + "opentelemetry_sdk/rt-tokio", + "opentelemetry_sdk/trace", + "opentelemetry/trace", + "opentelemetry-http", + "opentelemetry-otlp/grpc-tonic", + "opentelemetry-otlp/http-proto", + "opentelemetry-semantic-conventions/semconv_experimental", +] +postgres = ["sqlx/postgres", "url/serde", "secrecy/serde"] +tracing = ["dep:tracing", "tracing-subscriber/env-filter"] +opentelemetry-tonic = ["dep:tonic"] +tracing-loki = ["dep:tracing-loki", "tracing"] + +[[example]] +name = "tracing" +path = "examples/tracing.rs" +required-features = ["tracing"] + +[dev-dependencies] +tokio = { version = "*", features = ["macros", "rt"] } +sqlx = { version = "*", features = ["runtime-tokio"] } + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/lib/warden-stack/LICENSE-APACHE b/lib/warden-stack/LICENSE-APACHE new file mode 100644 index 0000000..4c986ff --- /dev/null +++ b/lib/warden-stack/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2025 rtkay + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/lib/warden-stack/LICENSE-MIT b/lib/warden-stack/LICENSE-MIT new file mode 100644 index 0000000..7eed760 --- /dev/null +++ b/lib/warden-stack/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 rtkay + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/warden-stack/README.md b/lib/warden-stack/README.md new file mode 100644 index 0000000..12f9f3a --- /dev/null +++ b/lib/warden-stack/README.md @@ -0,0 +1,22 @@ +warden-stack! + +A crate centralising the configuration and initialisation of various services I find myself using in a lot of projects. + +# TL:DR +Find a feature, activate it, initialise it in your builder, then you're good to go + +## Features + +Each feature unlocks configuration methods on the `ServicesBuilder`, allowing you to selectively wire up what you need. + +| Feature | Description | +|----------------|-----------------------------------------------------------| +| `api` | Enables `port` configuration | +| `cache` | Enables redis/valkey caching support | +| `nats-core` | Enables core NATS messaging via `async-nats` | +| `nats-jetstream`| Enables NATS JetStream support via `async-nats` | +| `opentelemetry`| Enables distributed tracing with OpenTelemetry | +| `postgres` | Enables PostgreSQL support using `sqlx` | +| `tracing` | Enables tracing setup via `tracing` and `tracing-subscriber` | +| `opentelemetry-tonic` | Enables opentelemetry injector and extractor utilities for `tonic` | +| `tracing-loki` | Enables tracing output to Loki. | diff --git a/lib/warden-stack/examples/tracing.rs b/lib/warden-stack/examples/tracing.rs new file mode 100644 index 0000000..7ebe41e --- /dev/null +++ b/lib/warden-stack/examples/tracing.rs @@ -0,0 +1,10 @@ +use warden_stack::{Monitoring, tracing::TracingBuilder}; + +fn main() { + let config = Monitoring { + log_level: "info".to_string(), + }; + let _tracing = TracingBuilder::default().build(&config); + + tracing::info!("hello from tracing"); +} diff --git a/lib/warden-stack/src/cache.rs b/lib/warden-stack/src/cache.rs new file mode 100644 index 0000000..9be3778 --- /dev/null +++ b/lib/warden-stack/src/cache.rs @@ -0,0 +1,292 @@ +// https://github.com/svix/svix-webhooks/tree/4ede01a3209658615bb8d3153965c5c3a2e1b7ff/server/svix-server/src/redis +pub mod cluster; +pub mod sentinel; + +use std::{sync::Arc, time::Duration}; + +use bb8::{Pool, RunError}; +use bb8_redis::RedisConnectionManager; +use redis::{ + AsyncConnectionConfig, ProtocolVersion, RedisConnectionInfo, RedisError, TlsMode, + aio::ConnectionManagerConfig, sentinel::SentinelNodeConnectionInfo, +}; +use sentinel::{RedisSentinelConnectionManager, SentinelConfig}; +use serde::Deserialize; +use tokio::sync::Mutex; + +use crate::{ + ServiceError, ServicesBuilder, + services_builder::{IsUnset, SetCache, State}, +}; + +pub use self::cluster::RedisClusterConnectionManager; + +pub const REDIS_CONN_TIMEOUT: Duration = Duration::from_secs(2); + +impl<S: State> ServicesBuilder<S> { + pub async fn cache( + self, + config: &CacheConfig, + ) -> Result<ServicesBuilder<SetCache<S>>, crate::ServiceError> + where + S::Cache: IsUnset, + { + Ok(self.cache_internal(RedisManager::new(config).await?)) + } +} + +fn default_max_conns() -> u16 { + 100 +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "lowercase")] +pub struct CacheConfig { + #[serde(rename = "dsn")] + redis_dsn: Arc<str>, + #[serde(default)] + pooled: bool, + #[serde(rename = "type")] + kind: RedisVariant, + #[serde(default = "default_max_conns")] + #[serde(rename = "max-connections")] + max_connections: u16, +} + +#[derive(Debug, Deserialize, Clone)] +#[serde(rename_all = "kebab-case")] +pub enum RedisVariant { + Clustered, + NonClustered, + Sentinel(SentinelConfig), +} + +#[derive(Clone)] +pub enum RedisManager { + Clustered(Pool<RedisClusterConnectionManager>), + NonClustered(Pool<RedisConnectionManager>), + Sentinel(Pool<RedisSentinelConnectionManager>), + ClusteredUnpooled(redis::cluster_async::ClusterConnection), + NonClusteredUnpooled(redis::aio::ConnectionManager), + SentinelUnpooled(Arc<Mutex<redis::sentinel::SentinelClient>>), +} + +impl RedisManager { + pub async fn new(config: &CacheConfig) -> Result<Self, ServiceError> { + if config.pooled { + Self::new_pooled( + config.redis_dsn.as_ref(), + &config.kind, + config.max_connections, + ) + .await + } else { + Self::new_unpooled(config.redis_dsn.as_ref(), &config.kind).await + } + } + async fn new_pooled( + dsn: &str, + variant: &RedisVariant, + max_conns: u16, + ) -> Result<Self, ServiceError> { + match variant { + RedisVariant::Clustered => { + let mgr = RedisClusterConnectionManager::new(dsn)?; + let pool = bb8::Pool::builder() + .max_size(max_conns.into()) + .build(mgr) + .await?; + Ok(RedisManager::Clustered(pool)) + } + RedisVariant::NonClustered => { + let mgr = RedisConnectionManager::new(dsn)?; + let pool = bb8::Pool::builder() + .max_size(max_conns.into()) + .build(mgr) + .await?; + Ok(RedisManager::NonClustered(pool)) + } + RedisVariant::Sentinel(cfg) => { + let tls_mode = cfg.redis_tls_mode_secure.then_some(TlsMode::Secure); + let protocol = if cfg.redis_use_resp3 { + ProtocolVersion::RESP3 + } else { + ProtocolVersion::default() + }; + let mgr = RedisSentinelConnectionManager::new( + vec![dsn], + cfg.service_name.clone(), + Some(SentinelNodeConnectionInfo { + tls_mode, + redis_connection_info: Some(RedisConnectionInfo { + db: cfg.redis_db.unwrap_or(0), + username: cfg.redis_username.clone(), + password: cfg.redis_password.clone(), + protocol, + }), + }), + )?; + let pool = bb8::Pool::builder() + .max_size(max_conns.into()) + .build(mgr) + .await?; + Ok(RedisManager::Sentinel(pool)) + } + } + } + + async fn new_unpooled(dsn: &str, variant: &RedisVariant) -> Result<Self, ServiceError> { + match variant { + RedisVariant::Clustered => { + let cli = redis::cluster::ClusterClient::builder(vec![dsn]) + .retries(1) + .connection_timeout(REDIS_CONN_TIMEOUT) + .build()?; + let con = cli.get_async_connection().await?; + Ok(RedisManager::ClusteredUnpooled(con)) + } + RedisVariant::NonClustered => { + let cli = redis::Client::open(dsn)?; + let con = redis::aio::ConnectionManager::new_with_config( + cli, + ConnectionManagerConfig::new() + .set_number_of_retries(1) + .set_connection_timeout(REDIS_CONN_TIMEOUT), + ) + .await?; + Ok(RedisManager::NonClusteredUnpooled(con)) + } + RedisVariant::Sentinel(cfg) => { + let tls_mode = cfg.redis_tls_mode_secure.then_some(TlsMode::Secure); + let protocol = if cfg.redis_use_resp3 { + ProtocolVersion::RESP3 + } else { + ProtocolVersion::default() + }; + let cli = redis::sentinel::SentinelClient::build( + vec![dsn], + cfg.service_name.clone(), + Some(SentinelNodeConnectionInfo { + tls_mode, + redis_connection_info: Some(RedisConnectionInfo { + db: cfg.redis_db.unwrap_or(0), + username: cfg.redis_username.clone(), + password: cfg.redis_password.clone(), + protocol, + }), + }), + redis::sentinel::SentinelServerType::Master, + )?; + + Ok(RedisManager::SentinelUnpooled(Arc::new(Mutex::new(cli)))) + } + } + } + + pub async fn get(&self) -> Result<RedisConnection<'_>, RunError<RedisError>> { + match self { + Self::Clustered(pool) => Ok(RedisConnection::Clustered(pool.get().await?)), + Self::NonClustered(pool) => Ok(RedisConnection::NonClustered(pool.get().await?)), + Self::Sentinel(pool) => Ok(RedisConnection::SentinelPooled(pool.get().await?)), + Self::ClusteredUnpooled(conn) => Ok(RedisConnection::ClusteredUnpooled(conn.clone())), + Self::NonClusteredUnpooled(conn) => { + Ok(RedisConnection::NonClusteredUnpooled(conn.clone())) + } + Self::SentinelUnpooled(conn) => { + let mut conn = conn.lock().await; + let con = conn + .get_async_connection_with_config( + &AsyncConnectionConfig::new().set_response_timeout(REDIS_CONN_TIMEOUT), + ) + .await?; + Ok(RedisConnection::SentinelUnpooled(con)) + } + } + } +} + +pub enum RedisConnection<'a> { + Clustered(bb8::PooledConnection<'a, RedisClusterConnectionManager>), + NonClustered(bb8::PooledConnection<'a, RedisConnectionManager>), + SentinelPooled(bb8::PooledConnection<'a, RedisSentinelConnectionManager>), + ClusteredUnpooled(redis::cluster_async::ClusterConnection), + NonClusteredUnpooled(redis::aio::ConnectionManager), + SentinelUnpooled(redis::aio::MultiplexedConnection), +} + +impl redis::aio::ConnectionLike for RedisConnection<'_> { + fn req_packed_command<'a>( + &'a mut self, + cmd: &'a redis::Cmd, + ) -> redis::RedisFuture<'a, redis::Value> { + match self { + RedisConnection::Clustered(conn) => conn.req_packed_command(cmd), + RedisConnection::NonClustered(conn) => conn.req_packed_command(cmd), + RedisConnection::ClusteredUnpooled(conn) => conn.req_packed_command(cmd), + RedisConnection::NonClusteredUnpooled(conn) => conn.req_packed_command(cmd), + RedisConnection::SentinelPooled(conn) => conn.req_packed_command(cmd), + RedisConnection::SentinelUnpooled(conn) => conn.req_packed_command(cmd), + } + } + + fn req_packed_commands<'a>( + &'a mut self, + cmd: &'a redis::Pipeline, + offset: usize, + count: usize, + ) -> redis::RedisFuture<'a, Vec<redis::Value>> { + match self { + RedisConnection::Clustered(conn) => conn.req_packed_commands(cmd, offset, count), + RedisConnection::NonClustered(conn) => conn.req_packed_commands(cmd, offset, count), + RedisConnection::ClusteredUnpooled(conn) => { + conn.req_packed_commands(cmd, offset, count) + } + RedisConnection::NonClusteredUnpooled(conn) => { + conn.req_packed_commands(cmd, offset, count) + } + RedisConnection::SentinelPooled(conn) => conn.req_packed_commands(cmd, offset, count), + RedisConnection::SentinelUnpooled(conn) => conn.req_packed_commands(cmd, offset, count), + } + } + + fn get_db(&self) -> i64 { + match self { + RedisConnection::Clustered(conn) => conn.get_db(), + RedisConnection::NonClustered(conn) => conn.get_db(), + RedisConnection::ClusteredUnpooled(conn) => conn.get_db(), + RedisConnection::NonClusteredUnpooled(conn) => conn.get_db(), + RedisConnection::SentinelPooled(conn) => conn.get_db(), + RedisConnection::SentinelUnpooled(conn) => conn.get_db(), + } + } +} + +#[cfg(test)] +mod tests { + use redis::AsyncCommands; + + use crate::cache::CacheConfig; + + use super::RedisManager; + + // Ensure basic set/get works -- should test sharding as well: + #[tokio::test] + // run with `cargo test -- --ignored redis` only when redis is up and configured + #[ignore] + async fn test_set_read_random_keys() { + let config = CacheConfig { + redis_dsn: "redis://localhost:6379".into(), + pooled: false, + kind: crate::cache::RedisVariant::NonClustered, + max_connections: 10, + }; + let mgr = RedisManager::new(&config).await.unwrap(); + let mut conn = mgr.get().await.unwrap(); + + for (val, key) in "abcdefghijklmnopqrstuvwxyz".chars().enumerate() { + let key = key.to_string(); + let _: () = conn.set(key.clone(), val).await.unwrap(); + assert_eq!(conn.get::<_, usize>(&key).await.unwrap(), val); + } + } +} diff --git a/lib/warden-stack/src/cache/cluster.rs b/lib/warden-stack/src/cache/cluster.rs new file mode 100644 index 0000000..91e3b24 --- /dev/null +++ b/lib/warden-stack/src/cache/cluster.rs @@ -0,0 +1,52 @@ +use redis::{ + ErrorKind, FromRedisValue, IntoConnectionInfo, RedisError, + cluster::{ClusterClient, ClusterClientBuilder}, + cluster_routing::{MultipleNodeRoutingInfo, ResponsePolicy, RoutingInfo}, +}; + +/// ConnectionManager that implements `bb8::ManageConnection` and supports +/// asynchronous clustered connections via `redis_cluster_async::Connection` +#[derive(Clone)] +pub struct RedisClusterConnectionManager { + client: ClusterClient, +} + +impl RedisClusterConnectionManager { + pub fn new<T: IntoConnectionInfo>( + info: T, + ) -> Result<RedisClusterConnectionManager, RedisError> { + Ok(RedisClusterConnectionManager { + client: ClusterClientBuilder::new(vec![info]).retries(0).build()?, + }) + } +} + +impl bb8::ManageConnection for RedisClusterConnectionManager { + type Connection = redis::cluster_async::ClusterConnection; + type Error = RedisError; + + async fn connect(&self) -> Result<Self::Connection, Self::Error> { + self.client.get_async_connection().await + } + + async fn is_valid(&self, conn: &mut Self::Connection) -> Result<(), Self::Error> { + let pong = conn + .route_command( + &redis::cmd("PING"), + RoutingInfo::MultiNode(( + MultipleNodeRoutingInfo::AllMasters, + Some(ResponsePolicy::OneSucceeded), + )), + ) + .await + .and_then(|v| String::from_redis_value(&v))?; + match pong.as_str() { + "PONG" => Ok(()), + _ => Err((ErrorKind::ResponseError, "ping request").into()), + } + } + + fn has_broken(&self, _: &mut Self::Connection) -> bool { + false + } +} diff --git a/lib/warden-stack/src/cache/sentinel.rs b/lib/warden-stack/src/cache/sentinel.rs new file mode 100644 index 0000000..c9f787a --- /dev/null +++ b/lib/warden-stack/src/cache/sentinel.rs @@ -0,0 +1,65 @@ +use redis::{ + ErrorKind, IntoConnectionInfo, RedisError, + sentinel::{SentinelClient, SentinelNodeConnectionInfo, SentinelServerType}, +}; +use serde::Deserialize; +use tokio::sync::Mutex; + +struct LockedSentinelClient(pub(crate) Mutex<SentinelClient>); + +/// ConnectionManager that implements `bb8::ManageConnection` and supports +/// asynchronous Sentinel connections via `redis::sentinel::SentinelClient` +pub struct RedisSentinelConnectionManager { + client: LockedSentinelClient, +} + +impl RedisSentinelConnectionManager { + pub fn new<T: IntoConnectionInfo>( + info: Vec<T>, + service_name: String, + node_connection_info: Option<SentinelNodeConnectionInfo>, + ) -> Result<RedisSentinelConnectionManager, RedisError> { + Ok(RedisSentinelConnectionManager { + client: LockedSentinelClient(Mutex::new(SentinelClient::build( + info, + service_name, + node_connection_info, + SentinelServerType::Master, + )?)), + }) + } +} + +impl bb8::ManageConnection for RedisSentinelConnectionManager { + type Connection = redis::aio::MultiplexedConnection; + type Error = RedisError; + + async fn connect(&self) -> Result<Self::Connection, Self::Error> { + self.client.0.lock().await.get_async_connection().await + } + + async fn is_valid(&self, conn: &mut Self::Connection) -> Result<(), Self::Error> { + let pong: String = redis::cmd("PING").query_async(conn).await?; + match pong.as_str() { + "PONG" => Ok(()), + _ => Err((ErrorKind::ResponseError, "ping request").into()), + } + } + + fn has_broken(&self, _: &mut Self::Connection) -> bool { + false + } +} + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq)] +pub struct SentinelConfig { + #[serde(rename = "sentinel_service_name")] + pub service_name: String, + #[serde(default)] + pub redis_tls_mode_secure: bool, + pub redis_db: Option<i64>, + pub redis_username: Option<String>, + pub redis_password: Option<String>, + #[serde(default)] + pub redis_use_resp3: bool, +} diff --git a/lib/warden-stack/src/config.rs b/lib/warden-stack/src/config.rs new file mode 100644 index 0000000..9f42f43 --- /dev/null +++ b/lib/warden-stack/src/config.rs @@ -0,0 +1,139 @@ +use serde::Deserialize; + +use std::{fmt::Display, sync::Arc}; + +#[derive(Clone, Debug, Deserialize)] +pub struct AppConfig { + #[serde(skip)] + pub name: Arc<str>, + #[serde(skip)] + pub version: Arc<str>, + #[serde(default)] + pub env: Environment, + #[cfg(feature = "api")] + #[serde(default = "default_port")] + pub port: u16, +} + +#[cfg(feature = "api")] +pub(crate) fn default_port() -> u16 { + 2210 +} + +#[cfg(feature = "tracing")] +pub(crate) fn default_log() -> String { + #[cfg(debug_assertions)] + return "debug".into(); + #[cfg(not(debug_assertions))] + "info".into() +} + +#[derive(Clone, Debug, Deserialize)] +pub struct Configuration { + pub application: AppConfig, + #[cfg(feature = "postgres")] + pub database: crate::postgres::PostgresConfig, + #[cfg(feature = "cache")] + pub cache: crate::cache::CacheConfig, + #[serde(default)] + pub misc: serde_json::Value, + #[cfg(feature = "tracing")] + pub monitoring: Monitoring, + #[cfg(any(feature = "nats-core", feature = "nats-jetstream"))] + pub nats: crate::nats::NatsConfig, +} + +#[derive(Clone, Debug, Deserialize)] +pub struct Monitoring { + #[serde(rename = "log-level")] + #[cfg(feature = "tracing")] + #[serde(default = "default_log")] + pub log_level: String, + #[cfg(feature = "opentelemetry")] + #[serde(rename = "opentelemetry-endpoint")] + #[serde(default = "default_opentelemetry")] + pub opentelemetry_endpoint: Arc<str>, + #[cfg(feature = "tracing-loki")] + #[serde(rename = "loki-endpoint")] + #[serde(default = "default_loki")] + pub loki_endpoint: Arc<str>, +} + +#[cfg(feature = "tracing-loki")] +pub(crate) fn default_loki() -> Arc<str> { + "http://localhost:3100".into() +} + +#[cfg(feature = "opentelemetry")] +pub(crate) fn default_opentelemetry() -> Arc<str> { + "http://localhost:4317".into() +} + +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Default)] +#[cfg_attr(test, derive(serde::Serialize))] +#[serde(rename_all = "lowercase")] +pub enum Environment { + #[default] + Development, + Production, +} + +impl Display for Environment { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + Environment::Development => "development", + Environment::Production => "production", + } + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + // Test that the enum is correctly serialized and deserialized + #[test] + fn test_environment_serialization() { + // Test serialization for Development + let dev = Environment::Development; + let dev_json = serde_json::to_string(&dev).unwrap(); + assert_eq!(dev_json, "\"development\""); + + // Test serialization for Production + let prod = Environment::Production; + let prod_json = serde_json::to_string(&prod).unwrap(); + assert_eq!(prod_json, "\"production\""); + + // Test deserialization for Development + let dev_str = "\"development\""; + let deserialized_dev: Environment = serde_json::from_str(dev_str).unwrap(); + assert_eq!(deserialized_dev, Environment::Development); + + // Test deserialization for Production + let prod_str = "\"production\""; + let deserialized_prod: Environment = serde_json::from_str(prod_str).unwrap(); + assert_eq!(deserialized_prod, Environment::Production); + } + + // Test Display implementation + #[test] + fn test_environment_display() { + let dev = Environment::Development; + assert_eq!(format!("{}", dev), "development"); + + let prod = Environment::Production; + assert_eq!(format!("{}", prod), "production"); + } + + #[test] + #[cfg(feature = "api")] + fn test_port() { + let listen_address = + std::net::SocketAddr::from((std::net::Ipv6Addr::UNSPECIFIED, default_port())); + assert_eq!(listen_address.port(), default_port()); + } +} diff --git a/lib/warden-stack/src/lib.rs b/lib/warden-stack/src/lib.rs new file mode 100644 index 0000000..efd6862 --- /dev/null +++ b/lib/warden-stack/src/lib.rs @@ -0,0 +1,95 @@ +#![cfg_attr(docsrs, feature(doc_cfg))] + +#[cfg(feature = "tracing")] +#[cfg_attr(docsrs, doc(cfg(feature = "tracing")))] +pub mod tracing; + +#[cfg(feature = "cache")] +#[cfg_attr(docsrs, doc(cfg(feature = "cache")))] +pub mod cache; + +#[cfg(feature = "cache")] +pub use redis; + +#[cfg(feature = "postgres")] +pub use sqlx; + +#[cfg(any(feature = "nats-core", feature = "nats-jetstream"))] +pub use async_nats; + +#[cfg(feature = "opentelemetry")] +mod otel { + pub use opentelemetry; + pub use opentelemetry_http; + pub use opentelemetry_otlp; + pub use opentelemetry_sdk; + pub use opentelemetry_semantic_conventions; + pub use tracing_opentelemetry; +} + +#[cfg(feature = "opentelemetry")] +pub use otel::*; + +#[cfg(feature = "postgres")] +#[cfg_attr(docsrs, doc(cfg(feature = "postgres")))] +pub mod postgres; + +#[cfg(any(feature = "nats-core", feature = "nats-jetstream"))] +#[cfg_attr( + docsrs, + doc(cfg(any(feature = "nats-core", feature = "nats-jetstream"))) +)] +pub mod nats; + +mod config; +pub use config::*; + +#[derive(Clone, bon::Builder)] +pub struct Services { + #[cfg(feature = "postgres")] + #[builder(setters(vis = "", name = pg_internal))] + pub postgres: Option<sqlx::PgPool>, + #[cfg(feature = "cache")] + #[builder(setters(vis = "", name = cache_internal))] + pub cache: Option<cache::RedisManager>, + #[cfg(feature = "nats-core")] + #[cfg_attr(docsrs, doc(cfg(feature = "nats-core")))] + #[builder(setters(vis = "", name = nats_internal))] + /// NATS connection handle + pub nats: Option<async_nats::Client>, + #[cfg(feature = "nats-jetstream")] + #[cfg_attr(docsrs, doc(cfg(feature = "nats-jetstream")))] + #[builder(setters(vis = "", name = jetstream_internal))] + /// NATS-Jetstream connection handle + pub jetstream: Option<async_nats::jetstream::Context>, +} + +#[derive(thiserror::Error, Debug)] +pub enum ServiceError { + #[error("service was not initialised")] + NotInitialised, + #[error("unknown data store error")] + Unknown, + #[error("invalid config `{0}`")] + Configuration(String), + #[cfg(feature = "postgres")] + #[error(transparent)] + /// Postgres error + Postgres(#[from] sqlx::Error), + #[cfg(feature = "cache")] + #[error(transparent)] + /// Redis error + Cache(#[from] redis::RedisError), + #[cfg(feature = "opentelemetry")] + #[error(transparent)] + /// When creating the tracing layer + Opentelemetry(#[from] opentelemetry_sdk::trace::TraceError), + #[cfg(any(feature = "nats-core", feature = "nats-jetstream"))] + #[error(transparent)] + /// NATS error + Nats(#[from] async_nats::error::Error<async_nats::ConnectErrorKind>), + #[cfg(feature = "tracing-loki")] + #[error(transparent)] + /// When creating the tracing layer + Loki(#[from] tracing_loki::Error), +} diff --git a/lib/warden-stack/src/nats.rs b/lib/warden-stack/src/nats.rs new file mode 100644 index 0000000..952490c --- /dev/null +++ b/lib/warden-stack/src/nats.rs @@ -0,0 +1,61 @@ +use std::sync::Arc; + +use serde::Deserialize; + +#[derive(Deserialize, Clone, Debug)] +/// Nats configuration +pub struct NatsConfig { + /// Hosts dsn + #[serde(default = "nats")] + pub hosts: Arc<[String]>, +} + +pub(crate) fn nats() -> Arc<[String]> { + let hosts = vec!["nats://localhost:4222".to_string()]; + hosts.into() +} + +impl NatsConfig { + fn hosts(&self) -> Vec<String> { + self.hosts.iter().map(ToString::to_string).collect() + } +} + +use crate::{ + ServiceError, ServicesBuilder, + services_builder::{IsUnset, State}, +}; + +#[cfg(feature = "nats-jetstream")] +impl<S: State> ServicesBuilder<S> { + /// create a Jetstream Context using the provided [NatsConfig] + pub async fn nats_jetstream( + self, + config: &NatsConfig, + ) -> Result<ServicesBuilder<crate::services_builder::SetJetstream<S>>, ServiceError> + where + S::Jetstream: IsUnset, + { + let hosts = config.hosts(); + let client = async_nats::connect(hosts).await?; + + Ok(self.jetstream_internal(async_nats::jetstream::new(client))) + } +} + +#[cfg(feature = "nats-core")] +impl<S: State> ServicesBuilder<S> { + /// create a NATS connection using the provided [NatsConfig] + pub async fn nats( + self, + config: &NatsConfig, + ) -> Result<ServicesBuilder<crate::services_builder::SetNats<S>>, ServiceError> + where + S::Nats: IsUnset, + { + let hosts = config.hosts(); + let client = async_nats::connect(hosts).await?; + + Ok(self.nats_internal(client)) + } +} diff --git a/lib/warden-stack/src/postgres.rs b/lib/warden-stack/src/postgres.rs new file mode 100644 index 0000000..3264368 --- /dev/null +++ b/lib/warden-stack/src/postgres.rs @@ -0,0 +1,137 @@ +use std::sync::Arc; + +use secrecy::{ExposeSecret, SecretString}; +use serde::Deserialize; +use url::Url; + +use crate::{ + ServicesBuilder, + services_builder::{IsUnset, SetPostgres, State}, +}; + +#[derive(Debug, Deserialize, Clone)] +pub struct PostgresConfig { + #[serde(default = "default_pool_size")] + pool_size: u32, + #[serde(default = "default_port")] + port: u32, + name: Arc<str>, + host: Arc<str>, + #[serde(default = "user")] + user: Arc<str>, + password: SecretString, +} + +fn default_pool_size() -> u32 { + 100 +} + +fn user() -> Arc<str> { + "postgres".into() +} + +fn default_port() -> u32 { + 5432 +} + +impl PostgresConfig { + // Getter for size + pub fn pool_size(&self) -> u32 { + self.pool_size + } + + // Getter for port + pub fn port(&self) -> u32 { + self.port + } + + // Getter for name + pub fn name(&self) -> &str { + self.name.as_ref() + } + + // Getter for host + pub fn host(&self) -> &str { + self.host.as_ref() + } + + // Getter for username + pub fn username(&self) -> &str { + self.user.as_ref() + } + + // Getter for password (you may want to return a reference or handle it differently) + pub fn password(&self) -> &SecretString { + &self.password + } + + pub(crate) fn connection_string(&self) -> Result<Url, crate::ServiceError> { + Url::parse(&format!( + "postgres://{}:{}@{}:{}/{}", + self.user, + self.password.expose_secret(), + self.host, + self.port, + self.name + )) + .map_err(|e| crate::ServiceError::Configuration(e.to_string())) + } +} + +impl<S: State> ServicesBuilder<S> { + pub async fn postgres( + self, + config: &PostgresConfig, + ) -> Result<ServicesBuilder<SetPostgres<S>>, crate::ServiceError> + where + S::Postgres: IsUnset, + { + let pg = sqlx::postgres::PgPoolOptions::new() + // The default connection limit for a Postgres server is 100 connections, with 3 reserved for superusers. + // + // If you're deploying your application with multiple replicas, then the total + // across all replicas should not exceed the Postgres connection limit + // (max_connections postgresql.conf). + .max_connections(config.pool_size) + .connect(config.connection_string()?.as_ref()) + .await?; + Ok(self.pg_internal(pg)) + } +} + +#[cfg(all(test, target_os = "linux"))] +mod test { + use super::*; + use crate::Services; + + #[tokio::test] + async fn docker_stack_db() { + let port = default_port(); + let name = ""; + let host = "localhost"; + let user = user(); + let pool_size = default_pool_size(); + let password = "postgres"; + + let config = PostgresConfig { + pool_size, + port, + name: name.into(), + host: host.into(), + user: user.clone(), + password: secrecy::SecretString::new(password.into()), + }; + + assert_eq!(config.name(), name); + assert_eq!(config.pool_size(), pool_size); + assert_eq!(config.username(), user.as_ref()); + assert_eq!(config.host(), host); + assert_eq!(config.port(), port); + + assert_eq!(config.password().expose_secret(), password); + + let service = Services::builder().postgres(&config).await; + + assert!(service.is_ok()); + } +} diff --git a/lib/warden-stack/src/tracing.rs b/lib/warden-stack/src/tracing.rs new file mode 100644 index 0000000..1a40f4b --- /dev/null +++ b/lib/warden-stack/src/tracing.rs @@ -0,0 +1,66 @@ +#[cfg(feature = "opentelemetry")] +pub mod telemetry; + +#[cfg(feature = "opentelemetry")] +pub use opentelemetry_sdk::trace::SdkTracerProvider; + +#[cfg(feature = "tracing-loki")] +mod loki; + +use tracing_subscriber::{ + EnvFilter, Layer, Registry, layer::SubscriberExt, util::SubscriberInitExt, +}; + +/// Telemetry handle +#[derive(bon::Builder)] +#[builder(finish_fn(vis = "", name = build_internal))] +pub struct Tracing { + #[builder(field = vec![tracing_subscriber::fmt::layer().boxed()])] + layers: Vec<Box<dyn Layer<Registry> + Sync + Send>>, + #[cfg(feature = "tracing-loki")] + #[builder(setters(vis = "", name = loki_internal))] + pub loki_task: tracing_loki::BackgroundTask, + #[cfg(feature = "opentelemetry")] + #[builder(setters(vis = "", name = otel_internal))] + pub otel_provider: opentelemetry_sdk::trace::SdkTracerProvider, +} + +// Define a custom finishing function as a method on the `UserBuilder`. +// The builder's state must implement the `IsComplete` trait. +// See details about it in the tip below this example. +impl<S: tracing_builder::IsComplete> TracingBuilder<S> { + pub fn build(self, config: &crate::Monitoring) -> Tracing { + // Delegate to `build_internal()` to get the instance of user. + let mut tracing = self.build_internal(); + + let layers = std::mem::take(&mut tracing.layers); + tracing_subscriber::registry() + .with(layers) + .with( + EnvFilter::try_from_default_env() + .unwrap_or_else(|_| config.log_level.to_string().into()), + ) + .try_init() + .ok(); + tracing + } +} + +// #[cfg(test)] +// mod tests { +// use super::*; +// +// #[test] +// fn build() { +// let builder = Tracing::builder().build(); +// let level = crate::Monitoring { +// log_level: "info".to_string(), +// #[cfg(feature = "opentelemetry")] +// opentelemetry_endpoint: "http://localhost:4317".into(), +// #[cfg(feature = "tracing-loki")] +// loki_endpoint: "http://localhost:3100".into(), +// }; +// builder.init(&level); +// builder.loki_task +// } +// } diff --git a/lib/warden-stack/src/tracing/loki.rs b/lib/warden-stack/src/tracing/loki.rs new file mode 100644 index 0000000..cbf4e40 --- /dev/null +++ b/lib/warden-stack/src/tracing/loki.rs @@ -0,0 +1,29 @@ +use crate::Monitoring; + +use super::TracingBuilder; +use super::tracing_builder::{IsUnset, SetLokiTask, State}; +use tracing_subscriber::Layer; + +impl<S: State> TracingBuilder<S> { + pub fn loki( + mut self, + config: &crate::AppConfig, + monitoring: &Monitoring, + ) -> Result<TracingBuilder<SetLokiTask<S>>, crate::ServiceError> + where + S::LokiTask: IsUnset, + { + use std::str::FromStr; + let url = FromStr::from_str(&monitoring.loki_endpoint.as_ref()) + .map_err(|_e| crate::ServiceError::Unknown)?; + + let (layer, task) = tracing_loki::builder() + .label("service_name", config.name.as_ref())? + .extra_field("pid", format!("{}", std::process::id()))? + .build_url(url)?; + + self.layers.push(layer.boxed()); + + Ok(self.loki_internal(task)) + } +} diff --git a/lib/warden-stack/src/tracing/telemetry.rs b/lib/warden-stack/src/tracing/telemetry.rs new file mode 100644 index 0000000..b024937 --- /dev/null +++ b/lib/warden-stack/src/tracing/telemetry.rs @@ -0,0 +1,137 @@ +#[cfg(any(feature = "nats-jetstream", feature = "nats-core"))] +pub mod nats { + pub mod extractor { + pub struct HeaderMap<'a>(pub &'a async_nats::HeaderMap); + + impl opentelemetry::propagation::Extractor for HeaderMap<'_> { + fn get(&self, key: &str) -> Option<&str> { + self.0 + .get(async_nats::header::IntoHeaderName::into_header_name(key)) + .map(|value| value.as_str()) + } + + fn keys(&self) -> Vec<&str> { + self.0.iter().map(|(n, _v)| n.as_ref()).collect() + } + } + } + + pub mod injector { + pub struct HeaderMap<'a>(pub &'a mut async_nats::HeaderMap); + + impl opentelemetry::propagation::Injector for HeaderMap<'_> { + fn set(&mut self, key: &str, value: String) { + self.0.insert(key, value); + } + } + } +} + +#[cfg(feature = "opentelemetry-tonic")] +pub mod tonic { + pub mod extractor { + pub struct MetadataMap<'a>(pub &'a tonic::metadata::MetadataMap); + impl opentelemetry::propagation::Extractor for MetadataMap<'_> { + fn get(&self, key: &str) -> Option<&str> { + self.0.get(key).and_then(|metadata| metadata.to_str().ok()) + } + + /// Collect all the keys from the MetadataMap. + fn keys(&self) -> Vec<&str> { + self.0 + .keys() + .map(|key| match key { + tonic::metadata::KeyRef::Ascii(v) => v.as_str(), + tonic::metadata::KeyRef::Binary(v) => v.as_str(), + }) + .collect::<Vec<_>>() + } + } + } + + pub mod injector { + pub struct MetadataMap<'a>(pub &'a mut tonic::metadata::MetadataMap); + + impl opentelemetry::propagation::Injector for MetadataMap<'_> { + /// Set a key and value in the MetadataMap. Does nothing if the key or value are not valid inputs + fn set(&mut self, key: &str, value: String) { + if let Ok(key) = tonic::metadata::MetadataKey::from_bytes(key.as_bytes()) { + if let Ok(val) = tonic::metadata::MetadataValue::try_from(&value) { + self.0.insert(key, val); + } + } + } + } + } +} + +use crate::Monitoring; + +use super::TracingBuilder; +use super::tracing_builder::{IsUnset, SetOtelProvider, State}; +use tracing_subscriber::Layer; + +impl<S: State> TracingBuilder<S> { + pub fn opentelemetry( + mut self, + config: &crate::AppConfig, + monitoring: &Monitoring, + ) -> Result<TracingBuilder<SetOtelProvider<S>>, crate::ServiceError> + where + S::OtelProvider: IsUnset, + { + use opentelemetry::{ + KeyValue, + global::{self}, + trace::TracerProvider, + }; + use opentelemetry_otlp::WithExportConfig; + use opentelemetry_sdk::{ + Resource, + trace::{RandomIdGenerator, Sampler, SdkTracerProvider}, + }; + use opentelemetry_semantic_conventions::{ + SCHEMA_URL, + resource::{DEPLOYMENT_ENVIRONMENT_NAME, SERVICE_NAME, SERVICE_VERSION}, + }; + use tracing_opentelemetry::OpenTelemetryLayer; + + global::set_text_map_propagator( + opentelemetry_sdk::propagation::TraceContextPropagator::new(), + ); + + let resource = Resource::builder() + .with_schema_url( + [ + KeyValue::new(SERVICE_NAME, config.name.to_owned()), + KeyValue::new(SERVICE_VERSION, config.version.to_owned()), + KeyValue::new(DEPLOYMENT_ENVIRONMENT_NAME, config.env.to_string()), + ], + SCHEMA_URL, + ) + .with_service_name(config.name.to_string()) + .build(); + + let exporter = opentelemetry_otlp::SpanExporter::builder() + .with_tonic() + .with_endpoint(monitoring.opentelemetry_endpoint.as_ref()) + .build() + .unwrap(); + + let provider = SdkTracerProvider::builder() + .with_batch_exporter(exporter) + .with_resource(resource) + .with_id_generator(RandomIdGenerator::default()) + .with_sampler(Sampler::ParentBased(Box::new(Sampler::TraceIdRatioBased( + 1.0, + )))) + .build(); + + global::set_tracer_provider(provider.clone()); + + let layer = OpenTelemetryLayer::new(provider.tracer(config.name.as_ref().to_string())); + self.layers.push(layer.boxed()); + + Ok(self.otel_internal(provider)) + } +} |