From 9d2b6ee10ec5359cc91769d430485c8c869ba1a8 Mon Sep 17 00:00:00 2001 From: Akshay Date: Thu, 24 Dec 2020 10:51:40 +0530 Subject: monorepo --- backend/src/handlers/cart_items.rs | 107 +++++++++++++++++++++++++++ backend/src/handlers/mod.rs | 5 ++ backend/src/handlers/product.rs | 138 ++++++++++++++++++++++++++++++++++ backend/src/handlers/rating.rs | 91 +++++++++++++++++++++++ backend/src/handlers/smoke.rs | 15 ++++ backend/src/handlers/users.rs | 148 +++++++++++++++++++++++++++++++++++++ 6 files changed, 504 insertions(+) create mode 100644 backend/src/handlers/cart_items.rs create mode 100644 backend/src/handlers/mod.rs create mode 100644 backend/src/handlers/product.rs create mode 100644 backend/src/handlers/rating.rs create mode 100644 backend/src/handlers/smoke.rs create mode 100644 backend/src/handlers/users.rs (limited to 'backend/src/handlers') diff --git a/backend/src/handlers/cart_items.rs b/backend/src/handlers/cart_items.rs new file mode 100644 index 0000000..25baaeb --- /dev/null +++ b/backend/src/handlers/cart_items.rs @@ -0,0 +1,107 @@ +use crate::models::{AddCartItem, CartItem, Customer, Product}; +use crate::schema::product::dsl as prod; +use crate::schema::{cart_items::dsl::*, customer::dsl::*}; +use crate::TPool; + +use actix_identity::Identity; +use actix_web::{web, HttpResponse, Responder}; +use diesel::prelude::*; +use log::{error, info}; +use serde::Deserialize; + +pub async fn add_to_cart( + cookie: Identity, + item_id: String, + pool: web::Data, +) -> impl Responder { + let item_details = item_id.parse::().unwrap_or(-1); + info!("Add to cart hit: {:?}", item_details); + info!("[cart] Current user: {:?}", cookie.identity()); + let conn = pool.get().unwrap(); + if let Some(uname) = cookie.identity() { + let selected_user = customer + .filter(username.eq(&uname)) + .limit(1) + .first::(&conn) + .expect("Couldn't connect to DB"); + let new_cart_item = AddCartItem { + cart_id: selected_user.id, + product_id: item_details, + }; + info!( + "cart id: {:?}, product id {:?}", + selected_user.id, item_details + ); + diesel::insert_into(cart_items) + .values((cart_id.eq(selected_user.id), product_id.eq(item_details))) + .execute(&conn) + .expect("Coundn't connect to DB"); + HttpResponse::Ok().body("Inserted successfully!") + } else { + error!("Unauthorized add to cart action!"); + return HttpResponse::Unauthorized() + .body("Need to be logged in to add to cart!"); + } +} + +pub async fn remove_from_cart( + cookie: Identity, + item_id: String, + pool: web::Data, +) -> impl Responder { + info!("Remove from cart hit: {:?}", item_id); + let item_details = item_id.parse::().unwrap_or(-1); + let conn = pool.get().unwrap(); + if let Some(uname) = cookie.identity() { + let selected_user = customer + .filter(username.eq(&uname)) + .limit(1) + .first::(&conn) + .expect("Couldn't connect to DB"); + + diesel::delete( + cart_items + .filter(cart_id.eq(selected_user.id)) + .filter(product_id.eq(item_details)), + ) + .execute(&conn) + .expect("Coundn't connect to DB"); + HttpResponse::Ok().body("Removed successfully!") + } else { + error!("Unauthorized add to cart action!"); + return HttpResponse::Unauthorized() + .body("Need to be logged in to add to cart!"); + } +} + +pub async fn get_user_cart_items( + cookie: Identity, + pool: web::Data, +) -> impl Responder { + let conn = pool.get().unwrap(); + if let Some(uname) = cookie.identity() { + let selected_user = customer + .filter(username.eq(&uname)) + .limit(1) + .first::(&conn) + .expect("Couldn't connect to DB"); + let user_cart_items = cart_items + .filter(cart_id.eq(selected_user.id)) + .load::(&conn) + .expect("Couldn't connect to DB"); + let cart_products = user_cart_items + .into_iter() + .map(|item| { + prod::product + .filter(prod::id.eq(item.product_id)) + .limit(1) + .first::(&conn) + .expect("Couldn't connect to db") + }) + .collect::>(); + return HttpResponse::Ok().json(&cart_products); + } else { + return HttpResponse::Unauthorized() + .body("Need to be logged in to add to cart!"); + } +} diff --git a/backend/src/handlers/mod.rs b/backend/src/handlers/mod.rs new file mode 100644 index 0000000..9416857 --- /dev/null +++ b/backend/src/handlers/mod.rs @@ -0,0 +1,5 @@ +pub mod cart_items; +pub mod product; +pub mod rating; +pub mod smoke; +pub mod users; diff --git a/backend/src/handlers/product.rs b/backend/src/handlers/product.rs new file mode 100644 index 0000000..41a47a0 --- /dev/null +++ b/backend/src/handlers/product.rs @@ -0,0 +1,138 @@ +use crate::models::{Customer, NewProduct, Product, Rating, UpdateProduct}; +use crate::schema::customer::dsl as cust; +use crate::schema::product::dsl::*; +use crate::schema::rating::dsl as rating; +use crate::TPool; + +use actix_web::{web, HttpResponse, Responder}; +use chrono::naive::NaiveDate; +use diesel::prelude::*; +use log::{error, info}; +use serde::{Deserialize, Serialize}; + +pub async fn new_product( + pool: web::Data, + item: web::Json, +) -> impl Responder { + info!("New product hit: {:?}", item.name); + let conn = pool.get().unwrap(); + diesel::insert_into(product) + .values(item.into_inner()) + .execute(&conn) + .expect("Coundn't connect to DB"); + HttpResponse::Ok().body("Inserted successfully!") +} + +pub async fn product_details( + pool: web::Data, + product_id: web::Path, +) -> impl Responder { + let conn = pool.get().unwrap(); + let product_id = product_id.into_inner(); + info!("Fetching product details for {}", product_id); + let selected_product = product + .filter(id.eq(&product_id)) + .limit(1) + .first::(&conn); + match selected_product { + Ok(m) => { + info!("Found product: {}", product_id); + HttpResponse::Ok().json(m) + } + Err(_) => { + error!("Product not found: {}", product_id); + HttpResponse::NotFound().finish() + } + } +} + +pub async fn update_product( + pool: web::Data, + product_id: web::Path, + product_details: web::Json, +) -> impl Responder { + let conn = pool.get().unwrap(); + let product_id = product_id.into_inner(); + let product_details = product_details.into_inner(); + info!("Updating product: {:?}", product_id); + match diesel::update(product.filter(id.eq(product_id))) + .set(( + name.eq(product_details.name), + kind.eq(product_details.kind), + price.eq(product_details.price), + description.eq(product_details.description), + )) + .execute(&conn) + { + Ok(_) => { + return HttpResponse::Ok().body("Changed product successfully") + } + _ => { + return HttpResponse::InternalServerError() + .body("Unable to update record") + } + } +} + +pub async fn get_all_products(pool: web::Data) -> impl Responder { + let conn = pool.get().unwrap(); + info!("Generating and returning catalog ..."); + match product.load::(&conn) { + Ok(products) => return HttpResponse::Ok().json(&products), + Err(_) => { + return HttpResponse::InternalServerError() + .body("Unable to fetch product catalog") + } + } +} + +#[derive(Serialize, Deserialize, Debug)] +struct ProductRating { + pub comment_text: Option, + pub comment_date: NaiveDate, + pub product_name: String, + pub customer_name: String, + pub stars: Option, +} + +pub async fn get_product_reviews( + pool: web::Data, + product_id: web::Path, +) -> impl Responder { + let conn = pool.get().unwrap(); + info!("Fetching product reviews for {}", product_id); + let pid = product_id.into_inner(); + let rating_entries = rating::rating + .filter(rating::product_id.eq(pid)) + .load::(&conn) + .expect("Couldn't connect to DB"); + let json_ratings = rating_entries + .into_iter() + .map(move |p| { + let selected_product = product + .filter(id.eq(&p.product_id.unwrap())) + .limit(1) + .first::(&conn) + .unwrap() + .name + .clone(); + + let selected_customer = cust::customer + .filter(cust::id.eq(&p.customer_id.unwrap())) + .limit(1) + .first::(&conn) + .unwrap() + .username + .clone(); + + ProductRating { + comment_text: p.comment_text, + comment_date: p.comment_date.unwrap(), + product_name: selected_product, + customer_name: selected_customer, + stars: p.stars, + } + }) + .collect::>(); + return HttpResponse::Ok().json(&json_ratings); +} diff --git a/backend/src/handlers/rating.rs b/backend/src/handlers/rating.rs new file mode 100644 index 0000000..dfbeb3e --- /dev/null +++ b/backend/src/handlers/rating.rs @@ -0,0 +1,91 @@ +use crate::models::{AddRating, Customer, Rating}; +use crate::schema::rating::dsl as rating; +use crate::schema::{customer::dsl::*, product::dsl::*}; +use crate::TPool; + +use actix_identity::Identity; +use actix_web::{web, HttpResponse, Responder}; +use diesel::prelude::*; +use log::{error, info}; +use serde::Deserialize; + +#[derive(Deserialize, Debug)] +pub struct AddRatingJson { + pub comment_text: Option, + pub stars: Option, + pub product_id: i32, +} + +pub async fn add_rating( + cookie: Identity, + rating_details: web::Json, + pool: web::Data, +) -> impl Responder { + info!("Add rating hit: {:?}", rating_details.product_id); + info!("{:?}", cookie.identity()); + let conn = pool.get().unwrap(); + if let Some(uname) = cookie.identity() { + let selected_user = customer + .filter(username.eq(&uname)) + .limit(1) + .first::(&conn) + .expect("Couldn't connect to DB"); + let rating_details = rating_details.into_inner(); + let new_rating = AddRating { + comment_text: rating_details.comment_text, + stars: rating_details.stars, + product_id: rating_details.product_id, + customer_id: selected_user.id, + }; + diesel::insert_into(rating::rating) + .values(new_rating) + .execute(&conn) + .expect("Coundn't connect to DB"); + HttpResponse::Ok().body("Inserted rating successfully!") + } else { + error!("Unauthorized add rating action!"); + return HttpResponse::Unauthorized() + .body("Need to be logged in to add rating!"); + } +} + +#[derive(Deserialize, Debug)] +pub struct RemoveRating { + rating_id: i32, +} + +pub async fn remove_rating( + cookie: Identity, + rating_details: web::Json, + pool: web::Data, +) -> impl Responder { + info!("Remove rating hit: {:?}", rating_details.rating_id); + let conn = pool.get().unwrap(); + if let Some(uname) = cookie.identity() { + let selected_user = customer + .filter(username.eq(&uname)) + .limit(1) + .first::(&conn) + .expect("Couldn't connect to DB"); + + diesel::delete( + rating::rating + .filter(rating::customer_id.eq(selected_user.id)) + .filter(rating::id.eq(rating_details.rating_id)), + ) + .execute(&conn) + .expect("Coundn't connect to DB"); + HttpResponse::Ok().body("Removed successfully!") + } else { + error!("Unauthorized add to cart action!"); + return HttpResponse::Unauthorized() + .body("Need to be logged in to add to cart!"); + } +} + +// pub async fn get_product_reviews( +// product: web::Json, +// pool: web::Data, +// ) -> impl Responder { +// unimplemented!() +// } diff --git a/backend/src/handlers/smoke.rs b/backend/src/handlers/smoke.rs new file mode 100644 index 0000000..d0a1038 --- /dev/null +++ b/backend/src/handlers/smoke.rs @@ -0,0 +1,15 @@ +use actix_web::{get, post, HttpResponse, Responder}; + +#[get("/")] +async fn hello() -> impl Responder { + HttpResponse::Ok().body("Hello world!") +} + +#[post("/echo")] +async fn echo(req_body: String) -> impl Responder { + HttpResponse::Ok().body(req_body) +} + +pub async fn manual_hello() -> impl Responder { + HttpResponse::Ok().body("Hey there!") +} diff --git a/backend/src/handlers/users.rs b/backend/src/handlers/users.rs new file mode 100644 index 0000000..24fb591 --- /dev/null +++ b/backend/src/handlers/users.rs @@ -0,0 +1,148 @@ +use crate::models::{Customer, NewCustomer}; +use crate::schema::customer::dsl::*; +use crate::TPool; + +use actix_identity::Identity; +use actix_web::{web, HttpResponse, Responder}; +use bcrypt::{hash, verify, DEFAULT_COST}; +use diesel::prelude::*; +use log::{error, info}; +use serde::Deserialize; + +pub async fn new_user( + pool: web::Data, + item: web::Json, +) -> impl Responder { + info!("Creating ... {:?}", item.username); + let conn = pool.get().unwrap(); + let hashed_item = NewCustomer { + password: hash(&item.password, DEFAULT_COST).unwrap(), + ..(item.into_inner()) + }; + diesel::insert_into(customer) + .values(hashed_item) + .execute(&conn) + .expect("Coundn't connect to DB"); + HttpResponse::Ok().body("Inserted successfully!") +} + +pub async fn name_exists( + pool: web::Data, + item: String, +) -> impl Responder { + let conn = pool.get().unwrap(); + info!("target: {:?}", item); + if (customer + .filter(username.eq(&item)) + .limit(1) + .load::(&conn) + .expect("Coundn't connect to DB")) + .len() + > 0 + { + HttpResponse::Ok().body("true") + } else { + HttpResponse::Ok().body("false") + } +} + +#[derive(Deserialize)] +pub struct Login { + username: String, + password: String, +} + +pub async fn login( + pool: web::Data, + cookie: Identity, + login_details: web::Json, +) -> impl Responder { + info!("Login hit"); + if let Some(uname) = cookie.identity() { + info!("Found existing cookie: {:?}", cookie.identity()); + return HttpResponse::Ok().finish(); + } + let conn = pool.get().unwrap(); + let entered_pass = &login_details.password; + let selected_user = customer + .filter(username.eq(&login_details.username)) + .limit(1) + .first::(&conn) + .expect("Couldn't connect to DB"); + let hashed_pass = selected_user.password; + if verify(entered_pass, &hashed_pass).unwrap() { + cookie.remember(login_details.username.clone()); + info!( + "Successful login: {} {}", + selected_user.username, selected_user.email_id + ); + HttpResponse::Ok().finish() + } else { + HttpResponse::Unauthorized().finish() + } +} + +pub async fn logout(cookie: Identity) -> impl Responder { + cookie.forget(); + HttpResponse::Found().header("location", "/").finish() +} + +pub async fn user_details( + uname: web::Path, + pool: web::Data, +) -> impl Responder { + let conn = pool.get().unwrap(); + let uname = uname.into_inner(); + info!("Fetching info for: \"{}\"", uname); + let selected_user = customer + .filter(username.eq(&uname)) + .limit(1) + .first::(&conn); + match selected_user { + Ok(m) => { + info!("Found user: {}", uname); + HttpResponse::Ok().json(m) + } + Err(_) => { + error!("User not found: {}", uname); + HttpResponse::NotFound().finish() + } + } +} + +#[derive(Deserialize, Debug)] +pub struct ChangePassword { + old_password: String, + new_password: String, +} + +pub async fn change_password( + cookie: Identity, + password_details: web::Json, + pool: web::Data, +) -> impl Responder { + info!("Change password request: {:?}", password_details); + let conn = pool.get().unwrap(); + if let Some(uname) = cookie.identity() { + let entered_pass = &password_details.old_password; + let new_password = &password_details.new_password; + let selected_user = customer + .filter(username.eq(&uname)) + .limit(1) + .first::(&conn) + .expect("Couldn't connect to DB"); + let hashed_pass = selected_user.password; + if verify(entered_pass, &hashed_pass).unwrap() { + let hashed_new_password = + hash(&new_password, DEFAULT_COST).unwrap(); + diesel::update(customer.filter(id.eq(selected_user.id))) + .set(password.eq(hashed_new_password)) + .execute(&conn) + .unwrap(); + return HttpResponse::Ok().body("Changed password successfully"); + } else { + return HttpResponse::Ok().body("Invalid password"); + } + } + return HttpResponse::Unauthorized().body("Login first"); +} -- cgit v1.2.3