From 5a688e1558ea0fbe0ba224b32a126a8a3cb8d25d Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Fri, 13 Feb 2026 08:10:15 +0700 Subject: [PATCH] use new crate for password reset, fix domain name handling, wip --- Cargo.lock | 433 ++++++++++++++++-- Cargo.toml | 2 +- crates/service/web/storyteller_web/Cargo.toml | 3 + .../web/storyteller_web/src/api_doc_server.rs | 1 + .../web/storyteller_web/src/docs/api_doc.rs | 4 - .../src/email/get_password_reset_url.rs | 26 ++ .../web/storyteller_web/src/email/mod.rs | 2 + .../src/email/send_password_reset_email.rs | 54 +++ .../enqueue_face_fusion_workflow_handler.rs | 1 + .../enqueue_live_portrait_workflow_handler.rs | 1 + ...e_video_style_transfer_workflow_handler.rs | 1 + .../media_files/helpers/get_media_domain.rs | 2 + .../users/password_reset_request_handler.rs | 2 + .../requests/get_request_domain_branding.rs | 52 +++ .../service/web/storyteller_web/src/main.rs | 1 + 15 files changed, 553 insertions(+), 32 deletions(-) create mode 100644 crates/service/web/storyteller_web/src/email/get_password_reset_url.rs create mode 100644 crates/service/web/storyteller_web/src/email/mod.rs create mode 100644 crates/service/web/storyteller_web/src/email/send_password_reset_email.rs diff --git a/Cargo.lock b/Cargo.lock index 44aa6e71e2..d7caf3e6de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -721,7 +721,7 @@ dependencies = [ "openai_sora_client", "os_info", "primitives", - "rand 0.9.1", + "rand 0.9.2", "reqwest 0.12.23", "serde", "serde_derive", @@ -2984,6 +2984,20 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core 0.9.10", +] + [[package]] name = "data-encoding" version = "2.5.0" @@ -3219,7 +3233,7 @@ dependencies = [ "log", "mimetypes", "mysql_queries", - "rand 0.9.1", + "rand 0.9.2", "sqlx", "storyteller_root", "tokens", @@ -3468,7 +3482,7 @@ checksum = "f2646c2bb4993c86006961293208b69d86a2b254356889ddad826e9df3f684f9" dependencies = [ "bit-set", "cssparser 0.35.0", - "foldhash", + "foldhash 0.1.5", "html5ever 0.35.0", "precomputed-hash", "selectors 0.30.0", @@ -3641,6 +3655,15 @@ dependencies = [ "spki", ] +[[package]] +name = "ecow" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78e4f79b296fbaab6ce2e22d52cb4c7f010fe0ebe7a32e34fa25885fd797bd02" +dependencies = [ + "serde", +] + [[package]] name = "ed25519-compact" version = "2.1.1" @@ -4242,6 +4265,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "foreign-types" version = "0.3.2" @@ -4454,6 +4483,12 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + [[package]] name = "futures-util" version = "0.3.31" @@ -4646,11 +4681,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi 0.13.3+wasi-0.2.2", + "wasm-bindgen", "windows-targets 0.52.6", ] +[[package]] +name = "getrandom" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasip2", + "wasip3", + "wasm-bindgen", +] + [[package]] name = "gif" version = "0.13.1" @@ -4805,6 +4857,29 @@ dependencies = [ "workspace-build-acceleration", ] +[[package]] +name = "governor" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9efcab3c1958580ff1f25a2a41be1668f7603d849bb63af523b208a3cc1223b8" +dependencies = [ + "cfg-if", + "dashmap", + "futures-sink", + "futures-timer", + "futures-util", + "getrandom 0.3.1", + "hashbrown 0.16.1", + "nonzero_ext", + "parking_lot 0.12.2", + "portable-atomic", + "quanta", + "rand 0.9.2", + "smallvec", + "spinning_top", + "web-time", +] + [[package]] name = "grok_client" version = "0.0.1" @@ -4981,6 +5056,20 @@ name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] [[package]] name = "hashing" @@ -5613,6 +5702,12 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "idempotency" version = "0.0.1" @@ -6239,6 +6334,12 @@ dependencies = [ "spin 0.5.2", ] +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "lebe" version = "0.5.2" @@ -6566,7 +6667,7 @@ dependencies = [ "log-mdc", "mock_instant", "parking_lot 0.12.2", - "rand 0.9.1", + "rand 0.9.2", "serde", "serde-value", "serde_json", @@ -6747,13 +6848,13 @@ checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "maybe-async" -version = "0.2.7" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f1b8c13cb1f814b634a96b2c725449fe7ed464a7b8781de8688be5ffbd3f305" +checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.106", ] [[package]] @@ -7122,7 +7223,7 @@ dependencies = [ "log", "num-traits", "once_cell", - "rand 0.9.1", + "rand 0.9.2", "regex", "reusable_types", "serde", @@ -7296,6 +7397,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nonzero_ext" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21" + [[package]] name = "noop_proc_macro" version = "0.3.0" @@ -7840,7 +7947,7 @@ dependencies = [ "idempotency", "log", "once_cell", - "rand 0.9.1", + "rand 0.9.2", "serde", "serde_derive", "serde_json", @@ -7870,7 +7977,7 @@ dependencies = [ "idempotency", "log", "once_cell", - "rand 0.9.1", + "rand 0.9.2", "reqwest 0.12.23", "serde", "serde_derive", @@ -8589,6 +8696,16 @@ dependencies = [ "log", ] +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.106", +] + [[package]] name = "primeorder" version = "0.13.6" @@ -8832,6 +8949,21 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "quanta" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3ab5a9d756f0d97bdc89019bd2e4ea098cf9cde50ee7564dde6b81ccc8f06c7" +dependencies = [ + "crossbeam-utils", + "libc", + "once_cell", + "raw-cpuid", + "wasi 0.11.0+wasi-snapshot-preview1", + "web-sys", + "winapi", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -8946,6 +9078,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79ec282e887b434b68c18fe5c121d38e72a5cf35119b59e54ec5b992ea9c8eb0" +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "r2d2" version = "0.8.10" @@ -9013,9 +9151,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", @@ -9161,6 +9299,15 @@ dependencies = [ "rgb", ] +[[package]] +name = "raw-cpuid" +version = "11.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" +dependencies = [ + "bitflags 2.5.0", +] + [[package]] name = "raw-window-handle" version = "0.6.2" @@ -9246,7 +9393,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "r2d2", - "rand 0.9.1", + "rand 0.9.2", "rustls 0.23.13", "rustls-native-certs 0.8.0", "ryu", @@ -9488,6 +9635,61 @@ dependencies = [ "webpki-roots 1.0.2", ] +[[package]] +name = "reqwest" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "h2 0.4.5", + "http 1.3.1", + "http-body 1.0.1", + "http-body-util", + "hyper 1.6.0", + "hyper-rustls 0.27.3", + "hyper-tls", + "hyper-util", + "js-sys", + "log", + "native-tls", + "percent-encoding", + "pin-project-lite", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.2", + "tokio", + "tokio-native-tls", + "tower 0.5.2", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "resend-rs" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47209b1b50ed58522cd0084c581df21967616b98308c636d96d81ef24da63566" +dependencies = [ + "ecow", + "getrandom 0.4.1", + "governor", + "maybe-async", + "rand 0.9.2", + "reqwest 0.13.2", + "serde", + "serde_json", + "thiserror 2.0.12", +] + [[package]] name = "reusable_types" version = "0.0.1" @@ -9633,7 +9835,7 @@ dependencies = [ "jwt_light", "log", "once_cell", - "rand 0.9.1", + "rand 0.9.2", "reqwest 0.12.23", "serde", "serde_derive", @@ -10233,10 +10435,11 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ + "serde_core", "serde_derive", ] @@ -10262,10 +10465,19 @@ dependencies = [ ] [[package]] -name = "serde_derive" -version = "1.0.219" +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -10298,15 +10510,16 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "indexmap 2.9.0", "itoa 1.0.11", "memchr", - "ryu", "serde", + "serde_core", + "zmij", ] [[package]] @@ -10841,6 +11054,15 @@ dependencies = [ "lock_api", ] +[[package]] +name = "spinning_top" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300" +dependencies = [ + "lock_api", +] + [[package]] name = "spki" version = "0.7.3" @@ -11198,13 +11420,14 @@ dependencies = [ "primitives", "prompt_engineering", "r2d2", - "rand 0.9.1", + "rand 0.9.2", "redis 1.0.2", "redis_caching", "redis_common", "redis_schema", "regex", "reqwest 0.12.23", + "resend-rs", "reusable_types", "rust-s3", "serde", @@ -12655,9 +12878,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ "async-compression", "bitflags 2.5.0", @@ -12873,7 +13096,7 @@ dependencies = [ "http 1.3.1", "httparse", "log", - "rand 0.9.1", + "rand 0.9.2", "sha1 0.10.6", "thiserror 2.0.12", "utf-8", @@ -13126,7 +13349,7 @@ dependencies = [ "md-5 0.10.6", "once_cell", "primitives", - "rand 0.9.1", + "rand 0.9.2", "workspace-build-acceleration", ] @@ -13425,6 +13648,24 @@ dependencies = [ "wit-bindgen-rt", ] +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + [[package]] name = "wasite" version = "0.1.0" @@ -13510,6 +13751,28 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap 2.9.0", + "wasm-encoder", + "wasmparser", +] + [[package]] name = "wasm-streams" version = "0.4.0" @@ -13523,6 +13786,18 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.5.0", + "hashbrown 0.15.2", + "indexmap 2.9.0", + "semver 1.0.23", +] + [[package]] name = "wayland-backend" version = "0.3.10" @@ -13593,6 +13868,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "web_atoms" version = "0.1.3" @@ -14357,6 +14642,26 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck 0.5.0", + "wit-parser", +] + [[package]] name = "wit-bindgen-rt" version = "0.33.0" @@ -14366,6 +14671,74 @@ dependencies = [ "bitflags 2.5.0", ] +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck 0.5.0", + "indexmap 2.9.0", + "prettyplease", + "syn 2.0.106", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.106", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.5.0", + "indexmap 2.9.0", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.9.0", + "log", + "semver 1.0.23", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + [[package]] name = "workspace-build-acceleration" version = "0.1.0" @@ -14918,6 +15291,12 @@ dependencies = [ "zstd 0.13.3", ] +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + [[package]] name = "zopfli" version = "0.8.1" diff --git a/Cargo.toml b/Cargo.toml index da3364f75f..933315f8b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -276,7 +276,7 @@ r2d2 = "0.8.10" rand = "0.9.1" redis = { version = "1.0.2", features = ["r2d2", "tokio-rustls-comp", "tokio-comp", "cluster-async", "aio"] } regex = "1.12.2" -reqwest = { version = "0.12.23", default-features = false, features = [ "charset", "http2", "system-proxy", "json", "cookies", "rustls-tls" ] } +reqwest = { version = "0.12.23", default-features = false, features = [ "charset", "http2", "system-proxy", "json", "cookies", "rustls-tls", "gzip" ] } reqwest-websocket = { version = "0.5.0" } serde = { version = "1.0.219", features = ["derive"] } serde_derive = "1.0.219" diff --git a/crates/service/web/storyteller_web/Cargo.toml b/crates/service/web/storyteller_web/Cargo.toml index 1a607ec792..24af018604 100644 --- a/crates/service/web/storyteller_web/Cargo.toml +++ b/crates/service/web/storyteller_web/Cargo.toml @@ -113,6 +113,9 @@ language-tags = "0.3.2" # Parsing single tags richly # TODO/NB: This is just for Twitch, but we might need it later. reqwest.workspace = true +# Email +resend-rs = "0.21.0" + # Runtime tokio.workspace = true diff --git a/crates/service/web/storyteller_web/src/api_doc_server.rs b/crates/service/web/storyteller_web/src/api_doc_server.rs index de5c546b18..88d10bf2cd 100644 --- a/crates/service/web/storyteller_web/src/api_doc_server.rs +++ b/crates/service/web/storyteller_web/src/api_doc_server.rs @@ -37,6 +37,7 @@ pub mod threads; pub mod util; pub mod docs; +mod email; #[actix_web::main] async fn main() -> Result<(), impl Error> { diff --git a/crates/service/web/storyteller_web/src/docs/api_doc.rs b/crates/service/web/storyteller_web/src/docs/api_doc.rs index abb9fb9311..5e198bff8e 100644 --- a/crates/service/web/storyteller_web/src/docs/api_doc.rs +++ b/crates/service/web/storyteller_web/src/docs/api_doc.rs @@ -552,7 +552,6 @@ use tokens::tokens::zs_voice_datasets::*; EnqueueVoiceConversionInferenceError, EnqueueVoiceConversionInferenceRequest, EnqueueVoiceConversionInferenceSuccessResponse, - FakeYouPlan, FeaturedMediaFile, FeaturedModelWeightForList, FundamentalFrequencyMethod, @@ -728,10 +727,8 @@ use tokens::tokens::zs_voice_datasets::*; SearchModelWeightsSortField, SearchModelWeightsSuccessResponse, SessionInfoError, - SessionInfoSuccessResponse, SessionTokenInfoError, SessionTokenInfoSuccessResponse, - SessionUserInfo, SetMediaFileCoverImageError, SetMediaFileCoverImageRequest, SetModelWeightCoverImageError, @@ -750,7 +747,6 @@ use tokens::tokens::zs_voice_datasets::*; StatusAlertInfo, StatusAlertResponse, StatusDetailsResponse, - StorytellerStreamPlan, SubscriptionProductKey, TerminateInferenceJobError, TerminateInferenceJobPathInfo, diff --git a/crates/service/web/storyteller_web/src/email/get_password_reset_url.rs b/crates/service/web/storyteller_web/src/email/get_password_reset_url.rs new file mode 100644 index 0000000000..b43e0d420c --- /dev/null +++ b/crates/service/web/storyteller_web/src/email/get_password_reset_url.rs @@ -0,0 +1,26 @@ +use std::fmt::format; +use server_environment::ServerEnvironment; +use crate::http_server::requests::get_request_domain_branding::DomainBranding; + + +#[derive(Clone)] +pub enum PasswordResetLocation { + FakeYouProduction, + FakeYouDevelopment, + ArtCraftProduction, + ArtCraftDevelopment, + CustomUrl(String) +} + +pub fn get_password_reset_url(reset_token: &str, location: PasswordResetLocation) -> String { + let url = match location { + PasswordResetLocation::ArtCraftProduction => "https://getartcraft.com/password-reset/verify", + PasswordResetLocation::FakeYouProduction => "https://fakeyou.com/password-reset/verify", + PasswordResetLocation::ArtCraftDevelopment | + PasswordResetLocation::FakeYouDevelopment => { + "http://localhost:7000/password-reset/verify" + }, + PasswordResetLocation::CustomUrl(ref url) => url, + }; + format!("{url}?token={reset_token}") +} \ No newline at end of file diff --git a/crates/service/web/storyteller_web/src/email/mod.rs b/crates/service/web/storyteller_web/src/email/mod.rs new file mode 100644 index 0000000000..66c46b36e7 --- /dev/null +++ b/crates/service/web/storyteller_web/src/email/mod.rs @@ -0,0 +1,2 @@ +mod send_password_reset_email; +mod get_password_reset_url; \ No newline at end of file diff --git a/crates/service/web/storyteller_web/src/email/send_password_reset_email.rs b/crates/service/web/storyteller_web/src/email/send_password_reset_email.rs new file mode 100644 index 0000000000..b694a1e192 --- /dev/null +++ b/crates/service/web/storyteller_web/src/email/send_password_reset_email.rs @@ -0,0 +1,54 @@ +use crate::email::get_password_reset_url::{get_password_reset_url, PasswordResetLocation}; +use crate::http_server::requests::get_request_domain_branding::DomainBranding; +use errors::AnyhowResult; +use resend_rs::types::CreateEmailBaseOptions; +use resend_rs::Resend; +use server_environment::ServerEnvironment; + +pub struct SendPasswordResetEmailArgs<'a> { + email_address_destination: &'a str, + verification_token: &'a str, + resend_client: &'a Resend, + server_environment: ServerEnvironment, + domain_branding: DomainBranding, +} + +pub async fn send_password_reset_email( + args: SendPasswordResetEmailArgs<'_> +) -> AnyhowResult<()> { + let resend = Resend::new("re_xxxxxxxxx"); + + let from = "ArtCraft "; + let to = [args.email_address_destination]; + + let subject = "ArtCraft Password Reset"; + + let url = match (args.domain_branding, args.server_environment) { + (DomainBranding::FakeYou, ServerEnvironment::Development) => PasswordResetLocation::FakeYouDevelopment, + (DomainBranding::FakeYou, ServerEnvironment::Production) => PasswordResetLocation::FakeYouProduction, + (_, _) => PasswordResetLocation::ArtCraftProduction, + }; + + let link = get_password_reset_url(args.verification_token, url); + let code = args.verification_token; + + let html_message = format!(r#" + Click here to reset your password! +
+
+ If you can't click the link, here's the secret reset code: {code} +
+
+ Thank You, +
+
+ Storyteller.ai (FakeYou) Team + "#); + + let email = CreateEmailBaseOptions::new(from, to, subject) + .with_html(&html_message); + + let _email = args.resend_client.emails.send(email).await?; + + Ok(()) +} \ No newline at end of file diff --git a/crates/service/web/storyteller_web/src/http_server/deprecated_endpoints/workflows/enqueue/enqueue_face_fusion_workflow_handler.rs b/crates/service/web/storyteller_web/src/http_server/deprecated_endpoints/workflows/enqueue/enqueue_face_fusion_workflow_handler.rs index ae54507676..9f50f9f31e 100644 --- a/crates/service/web/storyteller_web/src/http_server/deprecated_endpoints/workflows/enqueue/enqueue_face_fusion_workflow_handler.rs +++ b/crates/service/web/storyteller_web/src/http_server/deprecated_endpoints/workflows/enqueue/enqueue_face_fusion_workflow_handler.rs @@ -224,6 +224,7 @@ pub async fn enqueue_face_fusion_workflow_handler( let mut watermark_type = match branding { Some(DomainBranding::FakeYou) => Some(WatermarkType::FakeYou), Some(DomainBranding::Storyteller) => Some(WatermarkType::Storyteller), + Some(_) => Some(WatermarkType::Storyteller), None => Some(WatermarkType::Storyteller), }; diff --git a/crates/service/web/storyteller_web/src/http_server/deprecated_endpoints/workflows/enqueue/enqueue_live_portrait_workflow_handler.rs b/crates/service/web/storyteller_web/src/http_server/deprecated_endpoints/workflows/enqueue/enqueue_live_portrait_workflow_handler.rs index 9039ef1736..b20399c5b7 100644 --- a/crates/service/web/storyteller_web/src/http_server/deprecated_endpoints/workflows/enqueue/enqueue_live_portrait_workflow_handler.rs +++ b/crates/service/web/storyteller_web/src/http_server/deprecated_endpoints/workflows/enqueue/enqueue_live_portrait_workflow_handler.rs @@ -232,6 +232,7 @@ pub async fn enqueue_live_portrait_workflow_handler( let mut watermark_type = match branding { Some(DomainBranding::FakeYou) => Some(WatermarkType::FakeYou), Some(DomainBranding::Storyteller) => Some(WatermarkType::Storyteller), + Some(_) => Some(WatermarkType::Storyteller), None => Some(WatermarkType::Storyteller), }; diff --git a/crates/service/web/storyteller_web/src/http_server/deprecated_endpoints/workflows/enqueue/enqueue_video_style_transfer_workflow_handler.rs b/crates/service/web/storyteller_web/src/http_server/deprecated_endpoints/workflows/enqueue/enqueue_video_style_transfer_workflow_handler.rs index 6cb61c579f..d7cc182ff1 100644 --- a/crates/service/web/storyteller_web/src/http_server/deprecated_endpoints/workflows/enqueue/enqueue_video_style_transfer_workflow_handler.rs +++ b/crates/service/web/storyteller_web/src/http_server/deprecated_endpoints/workflows/enqueue/enqueue_video_style_transfer_workflow_handler.rs @@ -253,6 +253,7 @@ pub async fn enqueue_video_style_transfer_workflow_handler( let mut watermark_type = match branding { Some(DomainBranding::FakeYou) => Some(WatermarkType::FakeYou), Some(DomainBranding::Storyteller) => Some(WatermarkType::Storyteller), + Some(_) => Some(WatermarkType::Storyteller), None => Some(WatermarkType::Storyteller), }; diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/media_files/helpers/get_media_domain.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/media_files/helpers/get_media_domain.rs index 197c09a22a..ddc1abfbfa 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/media_files/helpers/get_media_domain.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/media_files/helpers/get_media_domain.rs @@ -8,6 +8,8 @@ pub fn get_media_domain(http_request: &HttpRequest) -> MediaDomain { .map(|domain| match domain { DomainBranding::FakeYou => MediaDomain::FakeYou, DomainBranding::Storyteller => MediaDomain::Storyteller, + DomainBranding::ArtCraftDotAi => MediaDomain::Storyteller, + DomainBranding::GetArtCraft => MediaDomain::Storyteller, }) .unwrap_or(MediaDomain::FakeYou) } diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/users/password_reset_request_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/users/password_reset_request_handler.rs index bde4483cb2..5c050001e4 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/users/password_reset_request_handler.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/users/password_reset_request_handler.rs @@ -153,6 +153,8 @@ pub async fn password_reset_request_handler( PasswordResetRequestedRequestError::Internal })?; + + success_response() } diff --git a/crates/service/web/storyteller_web/src/http_server/requests/get_request_domain_branding.rs b/crates/service/web/storyteller_web/src/http_server/requests/get_request_domain_branding.rs index fae3b5b165..08148d3e9e 100644 --- a/crates/service/web/storyteller_web/src/http_server/requests/get_request_domain_branding.rs +++ b/crates/service/web/storyteller_web/src/http_server/requests/get_request_domain_branding.rs @@ -5,6 +5,8 @@ use log::{debug, warn}; #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum DomainBranding { + ArtCraftDotAi, + GetArtCraft, FakeYou, Storyteller, } @@ -59,6 +61,8 @@ fn match_possible_hostname(maybe_hostname: Option<&str>) -> Option return None, }; match hostname { + host if host.contains("artcraft.ai") => Some(DomainBranding::ArtCraftDotAi), + host if host.contains("getartcraft") => Some(DomainBranding::GetArtCraft), host if host.contains("fakeyou") => Some(DomainBranding::FakeYou), host if host.contains("storyteller") => Some(DomainBranding::Storyteller), _ => None, @@ -73,6 +77,39 @@ mod tests { mod origin_header { use super::*; + + #[test] + fn getartcraft_dot_com() { + let request = TestRequest::get() + .insert_header(("origin", "https://getartcraft.com")) + .to_http_request(); + assert_eq!(get_request_domain_branding(&request), Some(DomainBranding::GetArtCraft)); + } + + #[test] + fn api_dot_getartcraft_dot_com() { + let request = TestRequest::get() + .insert_header(("origin", "https://api.getartcraft.com")) + .to_http_request(); + assert_eq!(get_request_domain_branding(&request), Some(DomainBranding::GetArtCraft)); + } + + + #[test] + fn artcraft_dot_ai() { + let request = TestRequest::get() + .insert_header(("origin", "https://artcraft.ai")) + .to_http_request(); + assert_eq!(get_request_domain_branding(&request), Some(DomainBranding::ArtCraftDotAi)); + } + + #[test] + fn api_dot_artcraft_dot_ai() { + let request = TestRequest::get() + .insert_header(("origin", "https://api.artcraft.ai")) + .to_http_request(); + assert_eq!(get_request_domain_branding(&request), Some(DomainBranding::ArtCraftDotAi)); + } #[test] fn fakeyou_dot_com() { @@ -110,6 +147,21 @@ mod tests { mod host_header { use super::*; + fn getartcraft_dot_com() { + let request = TestRequest::get() + .insert_header(("host", "getartcraft.com")) + .to_http_request(); + assert_eq!(get_request_domain_branding(&request), Some(DomainBranding::GetArtCraft)); + } + + #[test] + fn api_dot_getartcraft_dot_com() { + let request = TestRequest::get() + .insert_header(("host", "api.getartcraft.com")) + .to_http_request(); + assert_eq!(get_request_domain_branding(&request), Some(DomainBranding::GetArtCraft)); + } + #[test] fn fakeyou_dot_com() { let request = TestRequest::get() diff --git a/crates/service/web/storyteller_web/src/main.rs b/crates/service/web/storyteller_web/src/main.rs index 10ca1d3d7a..6b02feaea1 100644 --- a/crates/service/web/storyteller_web/src/main.rs +++ b/crates/service/web/storyteller_web/src/main.rs @@ -91,6 +91,7 @@ use crate::util::troll_user_bans::troll_user_ban_list::TrollUserBanList; pub mod billing; pub mod configs; +pub mod email; pub mod error; pub mod http_server; pub mod state;