diff --git a/Cargo.lock b/Cargo.lock index a284d894..5b2043dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,7 +8,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "bytes", "futures-core", "futures-sink", @@ -29,7 +29,7 @@ dependencies = [ "actix-service", "actix-utils", "actix-web", - "bitflags 2.6.0", + "bitflags 2.8.0", "bytes", "derive_more 0.99.18", "futures-core", @@ -54,7 +54,7 @@ dependencies = [ "actix-utils", "ahash", "base64 0.22.1", - "bitflags 2.6.0", + "bitflags 2.8.0", "brotli", "bytes", "bytestring", @@ -104,7 +104,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -238,7 +238,7 @@ dependencies = [ "actix-router", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -402,11 +402,12 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "3.0.6" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", + "once_cell", "windows-sys 0.59.0", ] @@ -470,7 +471,7 @@ dependencies = [ "async-task", "concurrent-queue", "fastrand 2.3.0", - "futures-lite 2.5.0", + "futures-lite 2.6.0", "slab", ] @@ -485,7 +486,7 @@ dependencies = [ "async-io 2.4.0", "async-lock 3.4.0", "blocking", - "futures-lite 2.5.0", + "futures-lite 2.6.0", "once_cell", ] @@ -503,7 +504,7 @@ dependencies = [ "log", "parking", "polling 2.8.0", - "rustix 0.37.27", + "rustix 0.37.28", "slab", "socket2 0.4.10", "waker-fn", @@ -519,10 +520,10 @@ dependencies = [ "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.5.0", + "futures-lite 2.6.0", "parking", "polling 3.7.4", - "rustix 0.38.42", + "rustix 0.38.44", "slab", "tracing", "windows-sys 0.59.0", @@ -543,7 +544,7 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" dependencies = [ - "event-listener 5.3.1", + "event-listener 5.4.0", "event-listener-strategy", "pin-project-lite", ] @@ -563,7 +564,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-io", - "futures-lite 2.5.0", + "futures-lite 2.6.0", "gloo-timers", "kv-log-macro", "log", @@ -589,7 +590,7 @@ checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -687,9 +688,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" dependencies = [ "serde", ] @@ -721,7 +722,7 @@ dependencies = [ "async-channel 2.3.1", "async-task", "futures-io", - "futures-lite 2.5.0", + "futures-lite 2.6.0", "piper", ] @@ -749,7 +750,7 @@ name = "brass-macros" version = "0.1.0" dependencies = [ "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -776,8 +777,9 @@ dependencies = [ "garde", "insta", "lettre", + "maud", "pico-args", - "quick-xml 0.37.2", + "quick-xml", "rand", "regex", "rinja", @@ -785,7 +787,7 @@ dependencies = [ "serde_json", "sqlx", "static-files", - "thiserror 2.0.9", + "thiserror 2.0.11", "zxcvbn", ] @@ -802,9 +804,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "4.0.1" +version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" +checksum = "74fa05ad7d803d413eb8380983b092cbbaf9a85f151b871360e7b00cd7060b37" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -864,9 +866,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.7" +version = "1.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7" +checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" dependencies = [ "jobserver", "libc", @@ -926,9 +928,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.23" +version = "4.5.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" +checksum = "769b0145982b4b48713e01ec42d61614425f27b7058bda7180a3a41f30104796" dependencies = [ "clap_builder", "clap_derive", @@ -936,9 +938,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.23" +version = "4.5.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" +checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7" dependencies = [ "anstream", "anstyle", @@ -948,14 +950,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.18" +version = "4.5.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1141,7 +1143,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1152,7 +1154,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1193,7 +1195,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1203,7 +1205,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1216,7 +1218,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1236,7 +1238,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", "unicode-xid", ] @@ -1266,7 +1268,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1356,9 +1358,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" -version = "5.3.1" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" dependencies = [ "concurrent-queue", "parking", @@ -1371,7 +1373,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" dependencies = [ - "event-listener 5.3.1", + "event-listener 5.4.0", "pin-project-lite", ] @@ -1530,9 +1532,9 @@ dependencies = [ [[package]] name = "futures-lite" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" dependencies = [ "fastrand 2.3.0", "futures-core", @@ -1549,7 +1551,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1583,9 +1585,9 @@ dependencies = [ [[package]] name = "garde" -version = "0.21.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dbf10452e3dbf51033a5035a05762b2653c43bf84d46e96f15bc93beedd426d" +checksum = "f4bd1d7843e437a4caf1d6a9112ba1ee9635b09d909af22aa4e6ec01fe971e22" dependencies = [ "card-validate", "compact_str", @@ -1602,14 +1604,14 @@ dependencies = [ [[package]] name = "garde_derive" -version = "0.21.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccfdbc9c39fad7991686e229c55cf71565eafe73dcb2cf38ddf1d4aa3ca7e176" +checksum = "a0636cbdc03994db48fc89a0ce7765bd68d08bd8a7a68cb9a36bcde96790f413" dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1954,7 +1956,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1992,9 +1994,9 @@ checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2" [[package]] name = "indexmap" -version = "2.7.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "equivalent", "hashbrown 0.15.2", @@ -2018,6 +2020,8 @@ dependencies = [ "console", "linked-hash-map", "once_cell", + "regex", + "serde", "similar", ] @@ -2047,15 +2051,6 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.13.0" @@ -2082,9 +2077,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", @@ -2175,9 +2170,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "litemap" @@ -2214,9 +2209,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" dependencies = [ "value-bag", ] @@ -2230,6 +2225,28 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "maud" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df518b75016b4289cdddffa1b01f2122f4a49802c93191f3133f6dc2472ebcaa" +dependencies = [ + "itoa", + "maud_macros", +] + +[[package]] +name = "maud_macros" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa453238ec218da0af6b11fc5978d3b5c3a45ed97b722391a2a11f3306274e18" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.96", +] + [[package]] name = "md-5" version = "0.10.6" @@ -2270,9 +2287,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" +checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" dependencies = [ "adler2", ] @@ -2402,7 +2419,7 @@ version = "0.10.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cfg-if", "foreign-types", "libc", @@ -2419,7 +2436,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -2518,17 +2535,16 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "phonenumber" -version = "0.3.6+8.13.36" +version = "0.3.7+8.13.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11756237b57b8cc5e97dc8b1e70ea436324d30e7075de63b14fd15073a8f692a" +checksum = "2247167dc3741816fdd4d3690e97f56a892a264b44f4c702078b72d1f8b6bd40" dependencies = [ "bincode", "either", "fnv", - "itertools 0.12.1", - "lazy_static", "nom", - "quick-xml 0.31.0", + "once_cell", + "quick-xml", "regex", "regex-cache", "serde", @@ -2619,7 +2635,7 @@ dependencies = [ "concurrent-queue", "hermit-abi 0.4.0", "pin-project-lite", - "rustix 0.38.42", + "rustix 0.38.44", "tracing", "windows-sys 0.59.0", ] @@ -2652,10 +2668,33 @@ dependencies = [ ] [[package]] -name = "proc-macro2" -version = "1.0.92" +name = "proc-macro-error" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -2669,15 +2708,6 @@ dependencies = [ "cc", ] -[[package]] -name = "quick-xml" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" -dependencies = [ - "memchr", -] - [[package]] name = "quick-xml" version = "0.37.2" @@ -2739,7 +2769,7 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", ] [[package]] @@ -2837,7 +2867,7 @@ dependencies = [ "rinja_parser", "rustc-hash", "serde", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -2894,9 +2924,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.27" +version = "0.37.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" +checksum = "519165d378b97752ca44bbe15047d5d3409e875f39327546b42ac81d7e18c1b6" dependencies = [ "bitflags 1.3.2", "errno", @@ -2908,22 +2938,22 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.42" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "errno", "libc", - "linux-raw-sys 0.4.14", + "linux-raw-sys 0.4.15", "windows-sys 0.59.0", ] [[package]] name = "rustls" -version = "0.23.20" +version = "0.23.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" +checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8" dependencies = [ "once_cell", "ring", @@ -2992,7 +3022,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "core-foundation", "core-foundation-sys", "libc", @@ -3001,9 +3031,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.13.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", @@ -3011,9 +3041,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" +checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" [[package]] name = "serde" @@ -3032,14 +3062,14 @@ checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] name = "serde_json" -version = "1.0.135" +version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" +checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b" dependencies = [ "itoa", "memchr", @@ -3108,9 +3138,9 @@ dependencies = [ [[package]] name = "similar" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" [[package]] name = "slab" @@ -3195,7 +3225,7 @@ dependencies = [ "crc", "crossbeam-queue", "either", - "event-listener 5.3.1", + "event-listener 5.4.0", "futures-core", "futures-intrusive", "futures-io", @@ -3213,7 +3243,7 @@ dependencies = [ "serde_json", "sha2", "smallvec", - "thiserror 2.0.9", + "thiserror 2.0.11", "tracing", "url", "webpki-roots", @@ -3229,7 +3259,7 @@ dependencies = [ "quote", "sqlx-core", "sqlx-macros-core", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -3253,7 +3283,7 @@ dependencies = [ "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", - "syn 2.0.95", + "syn 2.0.96", "tempfile", "url", ] @@ -3266,7 +3296,7 @@ checksum = "4560278f0e00ce64938540546f59f590d60beee33fffbd3b9cd47851e5fff233" dependencies = [ "atoi", "base64 0.22.1", - "bitflags 2.6.0", + "bitflags 2.8.0", "byteorder", "bytes", "chrono", @@ -3296,7 +3326,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.9", + "thiserror 2.0.11", "tracing", "whoami", ] @@ -3309,7 +3339,7 @@ checksum = "c5b98a57f363ed6764d5b3a12bfedf62f07aa16e1856a7ddc2a0bb190a959613" dependencies = [ "atoi", "base64 0.22.1", - "bitflags 2.6.0", + "bitflags 2.8.0", "byteorder", "chrono", "crc", @@ -3334,7 +3364,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.9", + "thiserror 2.0.11", "tracing", "whoami", ] @@ -3435,7 +3465,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -3457,9 +3487,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.95" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", @@ -3474,7 +3504,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -3487,7 +3517,7 @@ dependencies = [ "fastrand 2.3.0", "getrandom", "once_cell", - "rustix 0.38.42", + "rustix 0.38.44", "windows-sys 0.59.0", ] @@ -3502,11 +3532,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.9" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" dependencies = [ - "thiserror-impl 2.0.9", + "thiserror-impl 2.0.11", ] [[package]] @@ -3517,18 +3547,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] name = "thiserror-impl" -version = "2.0.9" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -3589,9 +3619,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.42.0" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" dependencies = [ "backtrace", "bytes", @@ -3637,7 +3667,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -3789,34 +3819,35 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.49" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", @@ -3827,9 +3858,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3837,28 +3868,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "web-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -4104,7 +4138,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", "synstructure", ] @@ -4126,7 +4160,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -4146,7 +4180,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", "synstructure", ] @@ -4175,7 +4209,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -4215,7 +4249,7 @@ dependencies = [ "chrono", "derive_builder", "fancy-regex", - "itertools 0.13.0", + "itertools", "lazy_static", "regex", "time", diff --git a/web/Cargo.toml b/web/Cargo.toml index fc468ed1..a0d151bf 100644 --- a/web/Cargo.toml +++ b/web/Cargo.toml @@ -22,7 +22,7 @@ serde_json = "1.0.114" pico-args = "0.5.0" rand = { version = "0.8.5", features = ["getrandom"] } async-trait = "0.1.79" -lettre = "0.11.7" +lettre = "0.11.11" quick-xml = { version = "0.37", features = ["serde", "serialize"] } actix-web-static-files = "4.0" static-files = "0.2.1" @@ -34,6 +34,7 @@ brass-config = { path = "../config" } actix-http = "3.9.0" rinja = "0.3.5" garde = { version = "0.21.0", features = ["full"] } +maud = "0.26.0" [build-dependencies] built = "0.7.4" @@ -41,5 +42,5 @@ static-files = "0.2.1" change-detection = "1.2.0" [dev-dependencies] -insta = "1.41.1" +insta = { version = "1.41.1", features = ["yaml", "filters"] } fake = { version = "3.0.1", features = ["chrono"]} diff --git a/web/snapshots/brass_web__endpoints__events__get_edit__inner_produces_template.snap b/web/snapshots/brass_web__endpoints__events__get_edit__inner_produces_template.snap index ddcc93f1..8c72e4a2 100644 --- a/web/snapshots/brass_web__endpoints__events__get_edit__inner_produces_template.snap +++ b/web/snapshots/brass_web__endpoints__events__get_edit__inner_produces_template.snap @@ -29,13 +29,15 @@ snapshot_kind: text
-
+ diff --git a/web/src/mail/forgot_password.rs b/web/src/mail/forgot_password.rs new file mode 100644 index 00000000..e69de29b diff --git a/web/src/mail/mod.rs b/web/src/mail/mod.rs new file mode 100644 index 00000000..2f4b5e41 --- /dev/null +++ b/web/src/mail/mod.rs @@ -0,0 +1,77 @@ +use brass_config::{Config, SmtpTlsType}; +use lettre::{ + transport::{ + smtp::{authentication::Credentials, extension::ClientId}, + stub::StubTransport, + }, + SmtpTransport, Transport, +}; + +use crate::utils::ApplicationError; + +mod forgot_password; +mod registration; + +pub struct Mailer { + transport: Transports, + hostname: String, +} + +enum Transports { + SmtpTransport(SmtpTransport), + StubTransport(StubTransport), +} + +impl Transport for Transports { + type Ok = (); + type Error = ApplicationError; + + fn send_raw( + &self, + envelope: &lettre::address::Envelope, + email: &[u8], + ) -> Result { + match self { + Transports::SmtpTransport(smtp_transport) => smtp_transport + .send_raw(envelope, email) + .map(|_| ()) + .map_err(|err| ApplicationError::EmailTransport(err)), + Transports::StubTransport(stub_transport) => stub_transport + .send_raw(envelope, email) + .map(|_| ()) + .map_err(|err| ApplicationError::EmailStubTransport(err)), + } + } +} + +impl Mailer { + pub fn new(config: &Config) -> anyhow::Result { + let mut builder = match config.smtp_tlstype { + SmtpTlsType::StartTLS => { + SmtpTransport::starttls_relay(&config.smtp_server)?.port(config.smtp_port) + } + SmtpTlsType::TLS => SmtpTransport::relay(&config.smtp_server)?.port(config.smtp_port), + SmtpTlsType::NoTLS => { + SmtpTransport::builder_dangerous(&config.smtp_server).port(config.smtp_port) + } + }; + + if let (Some(login), Some(password)) = + (config.smtp_login.as_ref(), config.smtp_password.as_ref()) + { + builder = + builder.credentials(Credentials::new(login.to_string(), password.to_string())); + } + + let transport = builder + .hello_name(ClientId::Domain(config.hostname.clone())) + .build(); + + let mailer = Mailer { + transport: Transports::SmtpTransport(transport), + hostname: config.hostname.to_string(), + }; + + Ok(mailer) + } +} diff --git a/web/src/mail/registration.rs b/web/src/mail/registration.rs new file mode 100644 index 00000000..dafa49af --- /dev/null +++ b/web/src/mail/registration.rs @@ -0,0 +1,96 @@ +use lettre::{ + message::{header::ContentType, Mailbox, MultiPart, SinglePart}, + Message, Transport, +}; +use rinja::Template; + +use crate::{models::User, utils::ApplicationError}; + +use super::Mailer; + +impl Mailer { + pub fn send_registration_mail(&self, user: &User, token: &str) -> Result<(), ApplicationError> { + let message = build(&self.hostname, &user.name, &user.email, token)?; + self.transport.send(&message)?; + + Ok(()) + } +} + +#[derive(Template)] +#[template(path = "emails/register.txt")] +struct RegisterMailTemplatePlain<'a> { + name: &'a str, + hostname: &'a str, + register_url: &'a str, +} + +#[derive(Template)] +#[template(path = "emails/register.html")] +struct RegisterMailTemplateHtml<'a> { + name: &'a str, + hostname: &'a str, + register_url: &'a str, +} + +fn build( + hostname: &str, + name: &str, + email: &str, + token: &str, +) -> Result { + let register_url = format!("https://{hostname}/register?token={token}"); + + let plain = RegisterMailTemplatePlain { + name, + hostname, + register_url: ®ister_url, + } + .to_string(); + + let html = RegisterMailTemplateHtml { + name, + hostname, + register_url: ®ister_url, + } + .to_string(); + + let message = Message::builder() + .from("noreply ".parse()?) + .reply_to("noreply ".parse()?) + .to(Mailbox::new(Some(name.to_string()), email.parse()?)) + .subject("Brass: Registrierung deines Accounts") + .multipart( + MultiPart::alternative() + .singlepart( + SinglePart::builder() + .header(ContentType::TEXT_PLAIN) + .body(plain), + ) + .singlepart( + SinglePart::builder() + .header(ContentType::TEXT_HTML) + .body(html), + ), + )?; + + Ok(message) +} + +#[test] +fn build_mail_snapshot() { + let message = build( + "brasiwa-leipzig.de", + "Max Mustermann", + "max@example.com", + "123456789", + ) + .unwrap(); + + let sender = lettre::transport::stub::StubTransport::new_ok(); + sender.send(&message).unwrap(); + let messages = sender.messages(); + let (_, sent_message) = messages.first().unwrap(); + + crate::utils::test_helper::assert_mail_snapshot!(sent_message); +} diff --git a/web/src/mail/snapshots/brass_web__mail__registration__build_mail_snapshot.snap b/web/src/mail/snapshots/brass_web__mail__registration__build_mail_snapshot.snap new file mode 100644 index 00000000..04eb47c1 --- /dev/null +++ b/web/src/mail/snapshots/brass_web__mail__registration__build_mail_snapshot.snap @@ -0,0 +1,47 @@ +--- +source: web/src/mail/registration.rs +expression: sent_message +snapshot_kind: text +--- +From: noreply +Reply-To: noreply +To: "Max Mustermann" +Subject: Brass: Registrierung deines Accounts +MIME-Version: 1.0 +Date: Date +Content-Type: multipart/alternative; + boundary="boundary" + +--boundary +Content-Type: text/plain; charset=utf-8 +Content-Transfer-Encoding: quoted-printable + +Hallo Max Mustermann, + +dein Account f=C3=BCr https:://brasiwa-leipzig.de wurde erstellt. Du musst = +nur noch ein Passwort festlegen. Kopiere daf=C3=BCr folgenden Link in deine= +n Browser: + +https://brasiwa-leipzig.de/register?token=3D123456789 + +Bitte beachte, dass der Link nur 24 Stunden g=C3=BCltig ist. + +Viele Gr=C3=BC=C3=9Fe +--boundary +Content-Type: text/html; charset=utf-8 +Content-Transfer-Encoding: quoted-printable + +

Hallo Max Mustermann,

+ +

dein Account f=C3=BCr https://brasiwa-leipzig.de wurde erstellt. Du musst nur noch ei= +n Passwort festlegen. Klicke daf=C3=BCr hier oder kopiere folg= +enden Link in deinen Browser:

+ +

https://brasiwa-leipzig.de/register?token=3D123456789

+ +

Bitte beachte, dass der Link nur 24 Stunden g=C3=BCltig ist.

+ +

Viele Gr=C3=BC=C3=9Fe

+--boundary-- diff --git a/web/src/main.rs b/web/src/main.rs index 268cd981..8fd758df 100644 --- a/web/src/main.rs +++ b/web/src/main.rs @@ -20,6 +20,7 @@ mod endpoints; mod middleware; mod models; mod utils; +mod mail; mod filters; mod postgres_session_store; diff --git a/web/src/utils/application_error.rs b/web/src/utils/application_error.rs index 4c857d19..9a5c66c0 100644 --- a/web/src/utils/application_error.rs +++ b/web/src/utils/application_error.rs @@ -17,6 +17,8 @@ pub enum ApplicationError { Email(#[from] lettre::error::Error), #[error("email transport not working")] EmailTransport(#[from] lettre::transport::smtp::Error), + #[error("email transport stub not working")] + EmailStubTransport(#[from] lettre::transport::stub::Error), #[error("hashfunction failed")] Hash(#[from] argon2::password_hash::Error), #[error("templating failed")] @@ -35,6 +37,7 @@ impl actix_web::error::ResponseError for ApplicationError { ApplicationError::EmailTransport(_) => StatusCode::INTERNAL_SERVER_ERROR, ApplicationError::Hash(_) => StatusCode::INTERNAL_SERVER_ERROR, ApplicationError::Template(_) => StatusCode::INTERNAL_SERVER_ERROR, + ApplicationError::EmailStubTransport(_) => StatusCode::INTERNAL_SERVER_ERROR, } } diff --git a/web/src/utils/test_helper/mod.rs b/web/src/utils/test_helper/mod.rs index e133e706..7d703e2a 100644 --- a/web/src/utils/test_helper/mod.rs +++ b/web/src/utils/test_helper/mod.rs @@ -4,7 +4,7 @@ pub use test_context::{setup, teardown, DbTestContext}; pub use test_requests::RequestConfig; pub use test_requests::{read_body, test_delete, test_get, test_post}; -pub use actix_http::StatusCode as StatusCode; +pub use actix_http::StatusCode; macro_rules! assert_snapshot { ($x:expr) => { @@ -14,4 +14,16 @@ macro_rules! assert_snapshot { }; } +macro_rules! assert_mail_snapshot { + ($x:expr) => { + insta::with_settings!({filters => vec![ + (r"[[:alnum:]]{40}", "boundary"), + ("(?m)Date: .*$", "Date: Date") + ]}, { + insta::assert_snapshot!($x); + }); + }; +} + pub(crate) use assert_snapshot; +pub(crate) use assert_mail_snapshot; diff --git a/web/templates/emails/register.html b/web/templates/emails/register.html new file mode 100644 index 00000000..6d54ebbd --- /dev/null +++ b/web/templates/emails/register.html @@ -0,0 +1,9 @@ +

Hallo {{ name }},

+ +

dein Account für https://{{ hostname }} wurde erstellt. Du musst nur noch ein Passwort festlegen. Klicke dafür hier oder kopiere folgenden Link in deinen Browser:

+ +

{{ register_url }}

+ +

Bitte beachte, dass der Link nur 24 Stunden gültig ist.

+ +

Viele Grüße

diff --git a/web/templates/emails/register.txt b/web/templates/emails/register.txt new file mode 100644 index 00000000..f0ebb2e7 --- /dev/null +++ b/web/templates/emails/register.txt @@ -0,0 +1,9 @@ +Hallo {{ name }}, + +dein Account für https:://{{ hostname }} wurde erstellt. Du musst nur noch ein Passwort festlegen. Kopiere dafür folgenden Link in deinen Browser: + +{{ register_url }} + +Bitte beachte, dass der Link nur 24 Stunden gültig ist. + +Viele Grüße