user-service 0.4.1

A user management microservice.
Documentation
use super::*;
use base64::Engine;
use rand::{distributions::Alphanumeric, Rng};

#[derive(Deserialize)]
pub struct Request {
    pub email: String,
    pub sender_name: String,
    // The callback URL to which a query string will be appended containing the verification token
    // Example Input: https://example.com/reset
    // Example Output: https://example.com/reset?token=123456
    pub callback: String,
}

#[derive(Serialize, Queryable, Selectable)]
#[diesel(table_name = schema::user)]
pub struct Response {
    pub id: i32,
}

#[derive(Serialize, Default)]
struct SendMail {
    sender_name: String,
    recipient: String,
    subject: String,
    base64_content: String,
}

#[post("")]
pub async fn route(
    db: web::Data<Database>,
    req: web::Json<Request>,
) -> Result<impl Responder, impl ResponseError> {
    let req = req.into_inner();

    let token: String = rand::thread_rng()
        .sample_iter(&Alphanumeric)
        .take(32)
        .map(char::from)
        .collect();

    let token_moved = token.clone();
    let email_moved = req.email.clone();

    let returned: Result<i32, diesel::result::Error> = web::block(move || {
        use diesel::dsl::*;
        let mut conn = db.get_conn();

        conn.transaction(|conn| {
            let user_id: Result<i32, diesel::result::Error> = schema::user::table
                .select(schema::user::id)
                .filter(schema::user::email.eq(email_moved))
                .first(conn);

            if user_id.is_err() {
                return user_id;
            }

            let user_id = user_id.unwrap();

            delete(schema::reset::table.filter(schema::reset::user.eq(user_id)))
                .execute(conn)
                .unwrap();

            let user_id = insert_into(schema::reset::table)
                .values((
                    schema::reset::user.eq(user_id),
                    schema::reset::token.eq(token_moved),
                ))
                .returning(schema::reset::user)
                .get_result(conn);

            user_id
        })
    })
    .await
    .unwrap();

    let id = match returned {
        Err(_) => return Err(ErrorCode::NotFound),
        Ok(id) => id,
    };

    let body = format!(
        "Please click the following link to reset your {} account password: {}?token={}",
        req.sender_name, req.callback, token
    );

    let body = base64::engine::general_purpose::STANDARD.encode(body);

    reqwest::Client::new()
        .post(format!(
            "{}/v1/send",
            std::env::var("MAIL_SERVICE_HOST")
                .expect("MAIL_SERVICE_HOST environment variable is not set")
        ))
        .json(&SendMail {
            sender_name: req.sender_name,
            recipient: req.email,
            subject: "Password Reset".to_owned(),
            base64_content: body,
        })
        .send()
        .await
        .unwrap();

    Ok(web::Json(Response { id }))
}