resend client config

This commit is contained in:
Brandon Thomas committed 2026-02-13 08:26:32 +07:00
1 parent 5a688e1558
commit 2691f8b669
5 files changed
+56 -16

No files matched your search

@@ -1,2 +1,2 @@
mod send_password_reset_email;
mod get_password_reset_url;
pub mod send_password_reset_email;
pub mod get_password_reset_url;
@@ -6,27 +6,37 @@ 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 email_address_destination: &'a str,
pub verification_token: &'a str,
pub resend_api_key: &'a str,
pub server_environment: ServerEnvironment,
pub domain_branding: DomainBranding,
}
pub async fn send_password_reset_email(
args: SendPasswordResetEmailArgs<'_>
) -> AnyhowResult<()> {
let resend = Resend::new("re_xxxxxxxxx");
let resend = Resend::new(&args.resend_api_key);
let from = "ArtCraft <noreply@getartcraft.com>";
let to = [args.email_address_destination];
let subject = "ArtCraft Password Reset";
let from_address = match args.domain_branding {
DomainBranding::ArtCraftDotAi |
DomainBranding::GetArtCraft => "ArtCraft <noreply@getartcraft.com>",
DomainBranding::FakeYou => "FakeYou <noreply@fakeyou.com>",
DomainBranding::Storyteller => "FakeYou <noreply@fakeyou.com>",
};
let url = match (args.domain_branding, args.server_environment) {
// Legacy FakeYou
(DomainBranding::FakeYou, ServerEnvironment::Development) => PasswordResetLocation::FakeYouDevelopment,
(DomainBranding::FakeYou, ServerEnvironment::Production) => PasswordResetLocation::FakeYouProduction,
(_, _) => PasswordResetLocation::ArtCraftProduction,
// Everything else "development" is ArtCraft Development
(_, ServerEnvironment::Development) => PasswordResetLocation::ArtCraftDevelopment,
// Everything else "production" is ArtCraft Production
(_, ServerEnvironment::Production) => PasswordResetLocation::ArtCraftProduction,
};
let link = get_password_reset_url(args.verification_token, url);
@@ -45,10 +55,10 @@ pub async fn send_password_reset_email(
Storyteller.ai (FakeYou) Team
"#);
let email = CreateEmailBaseOptions::new(from, to, subject)
let email = CreateEmailBaseOptions::new(from_address, to, subject)
.with_html(&html_message);
let _email = args.resend_client.emails.send(email).await?;
let _email = resend.emails.send(email).await?;
Ok(())
}
@@ -2,7 +2,7 @@ use std::fmt::Display;
use actix_web::http::StatusCode;
use actix_web::{web, HttpRequest, HttpResponse, ResponseError};
use log::{error, warn};
use log::{error, info, warn};
use serde::Deserialize;
use sqlx::MySqlPool;
use strum_macros::Display;
@@ -20,6 +20,9 @@ use mysql_queries::queries::users::user::get::lookup_user_for_login_by_email::lo
use mysql_queries::queries::users::user::get::lookup_user_for_login_by_username::lookup_user_for_login_by_username;
use mysql_queries::queries::users::user_password_resets::create_password_reset_request::create_password_reset;
use server_environment::ServerEnvironment;
use crate::email::send_password_reset_email::{send_password_reset_email, SendPasswordResetEmailArgs};
use crate::http_server::requests::get_request_domain_branding::{get_request_domain_branding, DomainBranding};
use crate::state::server_state::ServerState;
#[derive(Deserialize)]
pub struct PasswordResetRequestedRequest {
@@ -81,7 +84,8 @@ pub async fn password_reset_request_handler(
http_request: HttpRequest,
request: web::Json<PasswordResetRequestedRequest>,
mysql_pool: web::Data<MySqlPool>,
_server_environment: web::Data<ServerEnvironment>,
server_environment: web::Data<ServerEnvironment>,
server_state: web::Data<ServerState>,
_sender: web::Data<SmtpEmailSender>,
) -> Result<HttpResponse, PasswordResetRequestedErrorResponse> {
@@ -134,7 +138,7 @@ pub async fn password_reset_request_handler(
email_category: EmailCategory::PasswordReset,
maybe_email_args: Some(EmailSenderJobArgs {
args: Some(PolymorphicEmailSenderJobArgs::Pr(EmailJobPasswordResetArgs {
password_reset_secret_key: Some(secret_key),
password_reset_secret_key: Some(secret_key.clone()),
})),
}),
ietf_language_tag,
@@ -153,7 +157,20 @@ pub async fn password_reset_request_handler(
PasswordResetRequestedRequestError::Internal
})?;
let domain_branding = get_request_domain_branding(&http_request)
.unwrap_or(DomainBranding::GetArtCraft);
let server_environment = **server_environment;
info!("Sending password reset email...");
send_password_reset_email(SendPasswordResetEmailArgs {
email_address_destination: &user.email_address,
verification_token: &secret_key,
resend_api_key: &server_state.resend.api_key,
server_environment,
domain_branding,
}).await?;
success_response()
}
@@ -80,7 +80,7 @@ use crate::http_server::web_utils::handle_multipart_error::handle_multipart_erro
use crate::http_server::web_utils::scoped_temp_dir_creator::ScopedTempDirCreator;
use crate::state::certs::google_sign_in_cert::GoogleSignInCert;
use crate::state::memory_cache::model_token_to_info_cache::ModelTokenToInfoCache;
use crate::state::server_state::{DurableInMemoryCaches, EnvConfig, EphemeralInMemoryCaches, FalData, InMemoryCaches, OpenAiData, ServerInfo, ServerState, StaticFeatureFlags, StripeSettings, TrollBans};
use crate::state::server_state::{DurableInMemoryCaches, EnvConfig, EphemeralInMemoryCaches, FalData, InMemoryCaches, OpenAiData, ResendData, ServerInfo, ServerState, StaticFeatureFlags, StripeSettings, TrollBans};
use crate::threads::db_health_checker_thread::db_health_check_status::HealthCheckStatus;
use crate::threads::db_health_checker_thread::db_health_checker_thread::db_health_checker_thread;
use crate::threads::poll_ip_banlist_thread::poll_ip_bans;
@@ -402,6 +402,8 @@ async fn main() -> AnyhowResult<()> {
let openai_api_key= easyenv::get_env_string_required("OPENAI_API_KEY")?;
let resend_api_key = easyenv::get_env_string_required("RESEND_API_KEY")?;
let startup_time = Utc::now();
let server_state = ServerState {
@@ -460,6 +462,9 @@ async fn main() -> AnyhowResult<()> {
openai: OpenAiData {
api_key: openai_api_key,
},
resend: ResendData {
api_key: resend_api_key,
},
caches: InMemoryCaches {
durable: DurableInMemoryCaches {
model_token_info: model_token_info_cache,
@@ -95,6 +95,8 @@ pub struct ServerState {
pub openai: OpenAiData,
pub resend: ResendData,
/// Where to store audio uploads for w2l
pub audio_uploads_bucket_root: String,
@@ -314,3 +316,9 @@ pub struct FalData {
pub struct OpenAiData {
pub api_key: String,
}
/// Resend integration
#[derive(Clone)]
pub struct ResendData {
pub api_key: String,
}