use base64::Engine;
use rand::{distributions::Alphanumeric, Rng};
use super::*;
#[derive(Deserialize, Debug)]
pub struct Request {
pub user: RequestUser,
pub verify: Option<RequestVerify>,
}
#[derive(Deserialize, Debug, Insertable)]
#[diesel(table_name = schema::user)]
pub struct RequestUser {
pub email: String,
pub password: String,
pub first_name: Option<String>,
pub last_name: Option<String>,
pub birthday: Option<time::Date>,
}
#[derive(Deserialize, Debug)]
pub struct RequestVerify {
pub sender_name: String,
pub callback: String,
}
#[derive(Serialize, Default)]
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 mut req = req.into_inner();
if !valid(&req) {
return Err(ErrorCode::BadRequest);
}
let hashed_password = hashing::generate(&req.user.password);
req.user.password = hashed_password;
let email = req.user.email.clone();
let verified_value = !req.verify.is_some();
let verified_exists = req.verify.is_some();
let mut token_value = String::new();
if req.verify.is_some() {
token_value = rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(32)
.map(char::from)
.collect()
}
let token_value_moved = token_value.clone();
let response: Result<i32, diesel::result::Error> = web::block(move || {
use diesel::dsl::*;
use schema::user::dsl::*;
let mut conn = db.get_conn();
conn.transaction(|conn| {
let mut user_id = insert_into(user)
.values((
email.eq(req.user.email),
password.eq(req.user.password),
first_name.eq(req.user.first_name),
last_name.eq(req.user.last_name),
birthday.eq(req.user.birthday),
verified.eq(verified_value),
))
.returning(id)
.get_result(conn);
if user_id.is_err() {
return user_id;
}
if verified_exists {
user_id = insert_into(schema::verify::table)
.values((
schema::verify::user.eq(user_id.unwrap()),
schema::verify::token.eq(token_value_moved),
))
.returning(schema::verify::user)
.get_result(conn);
}
user_id
})
})
.await
.unwrap();
let response = match response {
Err(_) => return Err(ErrorCode::Conflict),
Ok(id) => Response { id },
};
if let Some(verify) = req.verify {
let body = format!(
"Please click the following link to verify your {} account: {}?token={}",
verify.sender_name, verify.callback, token_value
);
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: verify.sender_name,
recipient: email,
subject: "Email Verification".to_owned(),
base64_content: body,
})
.send()
.await
.unwrap();
}
Ok(web::Json(response))
}
fn valid(req: &Request) -> bool {
if !user_client::validate::email(&req.user.email) {
return false;
}
if !user_client::validate::password(&req.user.password) {
return false;
}
if req.user.first_name.is_some() {
if !user_client::validate::first_name(req.user.first_name.as_ref().unwrap()) {
return false;
}
}
if req.user.last_name.is_some() {
if !user_client::validate::last_name(req.user.last_name.as_ref().unwrap()) {
return false;
}
}
if req.user.birthday.is_some() {
if !user_client::validate::birthday(&req.user.birthday.unwrap()) {
return false;
}
}
true
}