Skip to content

Commit

Permalink
Merge pull request #27 from 8shaws/otp_bug
Browse files Browse the repository at this point in the history
fix: email verification part fixed
  • Loading branch information
shawakash authored Jul 30, 2024
2 parents d39998a + fc95dbf commit 27548cc
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 67 deletions.
42 changes: 40 additions & 2 deletions crates/api/src/db/user_db_fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,48 @@ pub fn get_user_by_contact(
Ok(user_result)
}

pub fn verify_user(con: &mut PgConnection, id: String) -> Result<usize, DbError> {
pub fn verify_user(con: &mut PgConnection, user_id: String) -> Result<usize, DbError> {
use crate::schema::users::dsl::*;
diesel::update(users.filter(id.eq(id)))
let user_id = Uuid::parse_str(&user_id).expect("Error parsing user id");

diesel::update(users.filter(id.eq(user_id)))
.set(email_verified.eq(true))
.execute(con)
.map_err(|e| DbError::from(e))
}

pub fn get_user_mail_by_id(
con: &mut PgConnection,
user_id: String,
) -> Result<Option<String>, DbError> {
use crate::schema::users::dsl::*;

let user_id = Uuid::parse_str(&user_id).expect("Error parsing user id");

let mail = users
.filter(id.eq(user_id))
.select(email)
.get_result::<String>(con)
.optional()
.map_err(|e| DbError::from(e))?;

Ok(mail)
}

pub fn is_user_verified(con: &mut PgConnection, user_id: String) -> Result<bool, DbError> {
use crate::schema::users::dsl::*;

let user_id = Uuid::parse_str(&user_id).expect("Error parsing user id");

let verified = users
.filter(id.eq(user_id))
.select(email_verified)
.get_result::<Option<bool>>(con)
.optional()
.map_err(|e| DbError::from(e))?;

match verified.unwrap() {
Some(v) => Ok(v),
None => Ok(false),
}
}
1 change: 1 addition & 0 deletions crates/api/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use std::time::SystemTime;

mod auth;
mod db;
mod middlewares;
mod models;
mod redis;
mod route;
Expand Down
114 changes: 114 additions & 0 deletions crates/api/src/middlewares/check_verify.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// use crate::db::user_db_fn::is_user_verified;
// use crate::{auth::middleware::IdKey, models::AppState};
// use actix_service::Service;
// use actix_web::web::Data;
// use actix_web::{
// body::EitherBody,
// dev::{ServiceRequest, ServiceResponse, Transform},
// Error, HttpMessage, HttpResponse,
// };
// use futures_util::future::{ok, LocalBoxFuture, Ready};
// use serde_json::json;
// use std::rc::Rc;
// use std::task::{Context, Poll};

// pub struct VerifyUser {
// app_state: Data<AppState>,
// }

// impl VerifyUser {
// pub fn new(app_state: Data<AppState>) -> Self {
// Self { app_state }
// }
// }

// impl<S, B: 'static> Transform<S, ServiceRequest> for VerifyUser
// where
// S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
// S::Future: 'static,
// B: 'static,
// {
// type Response = ServiceResponse<EitherBody<B>>;
// type Error = Error;
// type InitError = ();
// type Transform = VerifyUserMiddleware<S>;
// type Future = Ready<Result<Self::Transform, Self::InitError>>;

// fn new_transform(&self, service: S) -> Self::Future {
// ok(VerifyUserMiddleware {
// service: Rc::new(service),
// app_state: self.app_state.clone(),
// })
// }
// }

// pub struct VerifyUserMiddleware<S> {
// service: Rc<S>,
// app_state: Data<AppState>,
// }

// impl<S, B: 'static> Service<ServiceRequest> for VerifyUserMiddleware<S>
// where
// S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
// S::Future: 'static,
// B: 'static,
// {
// type Response = ServiceResponse<EitherBody<B>>;
// type Error = Error;
// type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;

// fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
// self.service.poll_ready(ctx)
// }
// fn call(&self, req: ServiceRequest) -> Self::Future {
// let srv = Rc::clone(&self.service);
// let id_key = req.extensions().get::<IdKey>().cloned();

// let db_pool = self.app_state.db_pool.clone();

// let fut = async move {
// let mut conn = db_pool.get().unwrap();

// match id_key {
// Some(id) => {
// let verified = is_user_verified(&mut conn, id.0);
// match verified {
// Ok(verified) => {
// if verified {
// let res = srv.call(req).await?;
// let mut res = res.map_into_left_body();
// Ok(res)
// } else {
// let response = HttpResponse::Forbidden().json(json!({
// "message": "User Not Verified for this operation!",
// "status": "Error"
// }));
// let service_response =
// req.into_response(response.map_into_left_body());
// Ok(service_response)
// }
// }
// Err(_) => {
// let response = HttpResponse::InternalServerError().json(json!({
// "message": "User Verification Failed!",
// "status": "Error"
// }));
// let service_response = req.into_response(response.map_into_left_body());
// Ok(service_response)
// }
// }
// }
// None => {
// let response = HttpResponse::Forbidden().json(json!({
// "msg": "No authentication token found",
// "status": "Error"
// }));
// let service_response = req.into_response(response.map_into_left_body());
// Ok(service_response)
// }
// }
// };

// Box::pin(fut)
// }
// }
1 change: 1 addition & 0 deletions crates/api/src/middlewares/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod check_verify;
120 changes: 63 additions & 57 deletions crates/api/src/route/user.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
use crate::auth;
use crate::auth::middleware::{ExtractClientId, IdKey, JwtKey};
use crate::auth::middleware::{ExtractClientId, IdKey};
use crate::db::{self};
use crate::models::*;
use crate::types::{EmailVerifyData, VerifyEmailBody};
use crate::types::VerifyEmailBody;
use actix_web::{error, post, web, HttpMessage, HttpRequest, HttpResponse, Responder, Result};
use r2d2_redis::redis;
use serde_json::json;
use std::sync::Arc;

#[post("/login")]
async fn login(state: web::Data<AppState>, form: web::Json<LoginUser>) -> Result<impl Responder> {
Expand Down Expand Up @@ -38,6 +37,7 @@ async fn login(state: web::Data<AppState>, form: web::Json<LoginUser>) -> Result
HttpResponse::Ok().json(json!({
"status": "ok",
"jwt": token,
"user": user,
"message": "Login successful"
}))
} else {
Expand Down Expand Up @@ -76,17 +76,12 @@ async fn register(
};

let mail = created_user.email.clone();
let id = created_user.id.clone().to_string();
let result = web::block(move || {
let mut conn = redis_pool.get().map_err(|e| e.to_string())?;

let data = EmailVerifyData { id: id, mail: mail };

let json_data = serde_json::to_string(&data).unwrap();

let _: () = redis::cmd("LPUSH")
.arg("user_email_verify")
.arg(json_data)
.arg(mail)
.query(&mut *conn)
.map_err(|e| e.to_string())?;
Ok::<(), String>(())
Expand Down Expand Up @@ -118,62 +113,73 @@ async fn verify_email(
state: web::Data<AppState>,
req: HttpRequest,
form: web::Json<VerifyEmailBody>,
) -> Result<impl Responder> {
) -> Result<HttpResponse> {
let id = req.extensions().get::<IdKey>().cloned();

match id {
Some(id) => {
let redis_pool = state.redis_pool.clone();
let id_clone = id.clone();

let otp_result = web::block(move || {
let mut conn = redis_pool.get().map_err(|e| e.to_string())?;

let otp = redis::cmd("GET")
.arg(format!("otp:{}", id.0))
.query::<Option<String>>(&mut *conn)
.map_err(|e| e.to_string());
otp
})
.await
.map_err(|e| actix_web::error::ErrorInternalServerError(e.to_string()))?;

match otp_result {
Ok(Some(otp)) => {
if otp == form.otp {
let id = id_clone.to_owned();
let _ = web::block(move || {
let mut conn = state.db_pool.get()?;
db::user_db_fn::verify_user(&mut conn, id.0)
})
.await?
.map_err(error::ErrorInternalServerError)?;

Ok(HttpResponse::Ok().json(json!({
"status": "ok",
"message": "Email Verified!"
})))
} else {
Ok(HttpResponse::Forbidden().json(json!({
"status": "error",
"message": "Invalid Otp!"
})))
if let Some(id) = id {
let redis_pool = state.redis_pool.clone();
let db_pool = state.db_pool.clone();
let id_clone = id.clone().0;
let form_otp = form.otp.clone();

let otp_result = web::block(move || {
let mut redis_conn = redis_pool.get().map_err(|e| e.to_string())?;
let mut db_conn = db_pool.get().map_err(|e| e.to_string())?;

let mail = db::user_db_fn::get_user_mail_by_id(&mut db_conn, id.0);

match mail {
Ok(Some(mail)) => {
let otp: Option<String> = redis::cmd("GET")
.arg(format!("otp:{}", mail))
.query(&mut *redis_conn)
.map_err(|e| e.to_string())?;

match otp {
Some(otp) => Ok(Some(otp)),
None => Ok(None),
}
}
Ok(None) => Ok(HttpResponse::NotFound().json(json!({
"status": "error",
"message": "Otp Not found, Resend Email?"
}))),
Err(_) => Ok(HttpResponse::Forbidden().json(json!({
"status": "error",
"message": "Invalid Otp!"
}))),
Ok(None) => return Ok(None),
Err(e) => Err(e.to_string()),
}
})
.await
.map_err(|e| actix_web::error::ErrorInternalServerError(e.to_string()))?;
let db_pool = state.db_pool.clone();

match otp_result {
Ok(Some(otp)) if otp == form_otp => {
let _ = web::block(move || {
let mut db_conn = db_pool.get()?;
db::user_db_fn::verify_user(&mut db_conn, id_clone)
})
.await
.map_err(|e| actix_web::error::ErrorInternalServerError(e.to_string()))?;

Ok(HttpResponse::Ok().json(json!({
"status": "ok",
"message": "Email Verified!"
})))
}
Ok(Some(_)) => Ok(HttpResponse::BadRequest().json(json!({
"status": "error",
"message": "Invalid OTP"
}))),
Ok(None) => Ok(HttpResponse::NotFound().json(json!({
"status": "error",
"message": "OTP not found, resend email?"
}))),
Err(_) => Ok(HttpResponse::InternalServerError().json(json!({
"status": "error",
"message": "Internal Server Error"
}))),
}
None => Ok(HttpResponse::BadRequest().json(json!({
} else {
Ok(HttpResponse::BadRequest().json(json!({
"status": "error",
"message": "Invalid token"
}))),
})))
}
}

Expand Down
17 changes: 9 additions & 8 deletions crates/notif_worker/src/process/processes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,21 @@ pub async fn send_email_process(conn: &mut redis::Connection) {
let result = redis::cmd("RPOP").arg(queue).query::<Option<String>>(conn);

match result {
Ok(Some(res)) => {
let data: Value = serde_json::from_str(&res).unwrap();
Ok(Some(mail)) => {
println!(
"{}: Worker {}: processing item from {}: {}",
current_time(),
thread_id,
queue,
data["mail"]
mail
);

let otp = generate_otp();
let otp_clone = otp.clone();

let catch_otp_result = redis::cmd("SET")
.arg(format!("otp:{}", data["id"]))
.arg(&otp)
.arg(format!("otp:{}", mail))
.arg(otp)
.query::<Option<String>>(conn);

match catch_otp_result {
Expand All @@ -38,16 +38,17 @@ pub async fn send_email_process(conn: &mut redis::Connection) {
"{}: Worker {}: OTP for {} saved successfully!",
current_time(),
thread_id,
data["mail"]
mail
);
send_mail(&data["mail"].as_str().unwrap(), &otp, &thread_id).await;

send_mail(&mail, &otp_clone, &thread_id).await;
}
Err(err) => {
println!(
"{}: Worker {}: Failed to save OTP for {}: {}",
current_time(),
thread_id,
data["mail"],
mail,
err
);
}
Expand Down

0 comments on commit 27548cc

Please sign in to comment.