zero4rs 2.0.0

zero4rs is a powerful, pragmatic, and extremely fast web framework for Rust
Documentation
use crate::prelude2::*;

use crate::core::http_clients::EmailClient;
use crate::core::remail::{do_send_email, SendEmailBuilder};
use crate::services::newsletter_service::SubscribeFormData;
use crate::services::newsletter_service::SubscriberEmail;

use crate::services::newsletter_service::insert_subscriber;
use crate::services::newsletter_service::store_token;

#[derive(serde::Deserialize)]
pub struct SendEmailParams {
    pub subject: String,
    pub receiver: String,
    pub email_body: String,
}

pub async fn send_confirmation_email(
    _receiver: SubscriberEmail,
    _email_client: &EmailClient,
    _confirmation_link: &str,
) -> Result<()> {
    let html_body = format!(
        "Welcome to our newsletter!<br />\
        Click <a href=\"{}\">here</a> to confirm your subscription.",
        _confirmation_link
    );

    _email_client
        .send_email(&_receiver, "Welcome!", &html_body)
        .await?;

    Ok(())
}

// curl --request POST --data 'name=le%20guin&email=ursula_le_guin%40gmail.com' 127.0.0.1:8000/subscriptions --verbose
pub async fn subscribe(
    _form: web::Form<SubscribeFormData>,
    app_state: web::Data<AppContext>,
) -> Result<HttpResponse> {
    let subscription_token = crate::commons::random_token();

    let confirmation_link = format!(
        "https://my-api.com/subscriptions/confirm?subscription_token={}",
        subscription_token
    );

    // validate
    let new_subscriber = _form
        .0
        .try_into()
        .map_err(|e| Error::invalid_request(format!("参数有误: error={:}", e)))?;

    let mut transaction = app_state
        .mysql()
        .begin()
        .await
        .context("Failed to acquire a Postgres connection from the pool")
        .map_err(Error::UnexpectedError)?;

    // insert into email
    let subscriber_id = insert_subscriber(&mut transaction, &new_subscriber)
        .await
        .context("Failed to insert new subscriber in the database.")
        .map_err(Error::UnexpectedError)?;

    // save token
    store_token(&mut transaction, subscriber_id, &subscription_token)
        .await
        .context("Failed to store the confirmation token for a new subscriber.")
        .map_err(Error::UnexpectedError)?;

    // if commit or rollback have not been called before the Transaction object goes out of scope (i.e. Drop is invoked),
    // a rollback command is queued to be executed as soon as an opportunity arises.
    transaction
        .commit()
        .await
        .context("Failed to commit SQL transaction to store a new subscriber.")
        .map_err(Error::UnexpectedError)?;

    // send confirm email
    send_confirmation_email(
        new_subscriber.email,
        &app_state.email_client,
        &confirmation_link,
    )
    .await
    .context("Failed to send a confirmation email.")
    .map_err(Error::UnexpectedError)?;

    Ok(HttpResponse::Ok().body(confirmation_link.to_string()))
}

pub async fn send_email(
    _form: web::Form<SendEmailParams>,
    _app_state: web::Data<crate::server::AppContext>,
) -> impl Responder {
    let receiver = match SubscriberEmail::parse(_form.0.receiver) {
        Ok(email) => email,
        Err(e) => {
            return format!("Validation failed: {:?}", e);
        }
    };

    match _app_state
        .email_client
        .send_email(
            &receiver,
            _form.0.subject.as_ref(),
            _form.0.email_body.as_ref(),
        )
        .await
    {
        Ok(()) => {
            log::info!("New email send successful");
            "Ok".to_string()
        }
        Err(e) => {
            format!("Failed to SendEmail: {:?}", e)
        }
    }
}

pub async fn send_emails() -> impl Responder {
    match do_send_email(&SendEmailBuilder {
        from: "asdasdf@126.com",
        passwd: "asdfasdf",
        smtp: "smtp.gmail.com",
        receiver: "asdfsadf@qq.com",
        _type: "text",
        title: "激活账号",
        content: "激活账号sad",
        file_path: None,
    })
    .await
    {
        Ok(_) => {}
        Err(e) => {
            log::error!("Failed to send email: {:?}", e);
        }
    }

    "Ok".to_string()
}