aboutsummaryrefslogtreecommitdiff
path: root/backend/src/handlers
diff options
context:
space:
mode:
Diffstat (limited to 'backend/src/handlers')
-rw-r--r--backend/src/handlers/cart_items.rs107
-rw-r--r--backend/src/handlers/mod.rs5
-rw-r--r--backend/src/handlers/product.rs138
-rw-r--r--backend/src/handlers/rating.rs91
-rw-r--r--backend/src/handlers/smoke.rs15
-rw-r--r--backend/src/handlers/users.rs148
6 files changed, 504 insertions, 0 deletions
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 @@
1use crate::models::{AddCartItem, CartItem, Customer, Product};
2use crate::schema::product::dsl as prod;
3use crate::schema::{cart_items::dsl::*, customer::dsl::*};
4use crate::TPool;
5
6use actix_identity::Identity;
7use actix_web::{web, HttpResponse, Responder};
8use diesel::prelude::*;
9use log::{error, info};
10use serde::Deserialize;
11
12pub async fn add_to_cart(
13 cookie: Identity,
14 item_id: String,
15 pool: web::Data<TPool>,
16) -> impl Responder {
17 let item_details = item_id.parse::<i32>().unwrap_or(-1);
18 info!("Add to cart hit: {:?}", item_details);
19 info!("[cart] Current user: {:?}", cookie.identity());
20 let conn = pool.get().unwrap();
21 if let Some(uname) = cookie.identity() {
22 let selected_user = customer
23 .filter(username.eq(&uname))
24 .limit(1)
25 .first::<Customer>(&conn)
26 .expect("Couldn't connect to DB");
27 let new_cart_item = AddCartItem {
28 cart_id: selected_user.id,
29 product_id: item_details,
30 };
31 info!(
32 "cart id: {:?}, product id {:?}",
33 selected_user.id, item_details
34 );
35 diesel::insert_into(cart_items)
36 .values((cart_id.eq(selected_user.id), product_id.eq(item_details)))
37 .execute(&conn)
38 .expect("Coundn't connect to DB");
39 HttpResponse::Ok().body("Inserted successfully!")
40 } else {
41 error!("Unauthorized add to cart action!");
42 return HttpResponse::Unauthorized()
43 .body("Need to be logged in to add to cart!");
44 }
45}
46
47pub async fn remove_from_cart(
48 cookie: Identity,
49 item_id: String,
50 pool: web::Data<TPool>,
51) -> impl Responder {
52 info!("Remove from cart hit: {:?}", item_id);
53 let item_details = item_id.parse::<i32>().unwrap_or(-1);
54 let conn = pool.get().unwrap();
55 if let Some(uname) = cookie.identity() {
56 let selected_user = customer
57 .filter(username.eq(&uname))
58 .limit(1)
59 .first::<Customer>(&conn)
60 .expect("Couldn't connect to DB");
61
62 diesel::delete(
63 cart_items
64 .filter(cart_id.eq(selected_user.id))
65 .filter(product_id.eq(item_details)),
66 )
67 .execute(&conn)
68 .expect("Coundn't connect to DB");
69 HttpResponse::Ok().body("Removed successfully!")
70 } else {
71 error!("Unauthorized add to cart action!");
72 return HttpResponse::Unauthorized()
73 .body("Need to be logged in to add to cart!");
74 }
75}
76
77pub async fn get_user_cart_items(
78 cookie: Identity,
79 pool: web::Data<TPool>,
80) -> impl Responder {
81 let conn = pool.get().unwrap();
82 if let Some(uname) = cookie.identity() {
83 let selected_user = customer
84 .filter(username.eq(&uname))
85 .limit(1)
86 .first::<Customer>(&conn)
87 .expect("Couldn't connect to DB");
88 let user_cart_items = cart_items
89 .filter(cart_id.eq(selected_user.id))
90 .load::<CartItem>(&conn)
91 .expect("Couldn't connect to DB");
92 let cart_products = user_cart_items
93 .into_iter()
94 .map(|item| {
95 prod::product
96 .filter(prod::id.eq(item.product_id))
97 .limit(1)
98 .first::<Product>(&conn)
99 .expect("Couldn't connect to db")
100 })
101 .collect::<Vec<_>>();
102 return HttpResponse::Ok().json(&cart_products);
103 } else {
104 return HttpResponse::Unauthorized()
105 .body("Need to be logged in to add to cart!");
106 }
107}
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 @@
1pub mod cart_items;
2pub mod product;
3pub mod rating;
4pub mod smoke;
5pub 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 @@
1use crate::models::{Customer, NewProduct, Product, Rating, UpdateProduct};
2use crate::schema::customer::dsl as cust;
3use crate::schema::product::dsl::*;
4use crate::schema::rating::dsl as rating;
5use crate::TPool;
6
7use actix_web::{web, HttpResponse, Responder};
8use chrono::naive::NaiveDate;
9use diesel::prelude::*;
10use log::{error, info};
11use serde::{Deserialize, Serialize};
12
13pub async fn new_product(
14 pool: web::Data<TPool>,
15 item: web::Json<NewProduct>,
16) -> impl Responder {
17 info!("New product hit: {:?}", item.name);
18 let conn = pool.get().unwrap();
19 diesel::insert_into(product)
20 .values(item.into_inner())
21 .execute(&conn)
22 .expect("Coundn't connect to DB");
23 HttpResponse::Ok().body("Inserted successfully!")
24}
25
26pub async fn product_details(
27 pool: web::Data<TPool>,
28 product_id: web::Path<i32>,
29) -> impl Responder {
30 let conn = pool.get().unwrap();
31 let product_id = product_id.into_inner();
32 info!("Fetching product details for {}", product_id);
33 let selected_product = product
34 .filter(id.eq(&product_id))
35 .limit(1)
36 .first::<Product>(&conn);
37 match selected_product {
38 Ok(m) => {
39 info!("Found product: {}", product_id);
40 HttpResponse::Ok().json(m)
41 }
42 Err(_) => {
43 error!("Product not found: {}", product_id);
44 HttpResponse::NotFound().finish()
45 }
46 }
47}
48
49pub async fn update_product(
50 pool: web::Data<TPool>,
51 product_id: web::Path<i32>,
52 product_details: web::Json<UpdateProduct>,
53) -> impl Responder {
54 let conn = pool.get().unwrap();
55 let product_id = product_id.into_inner();
56 let product_details = product_details.into_inner();
57 info!("Updating product: {:?}", product_id);
58 match diesel::update(product.filter(id.eq(product_id)))
59 .set((
60 name.eq(product_details.name),
61 kind.eq(product_details.kind),
62 price.eq(product_details.price),
63 description.eq(product_details.description),
64 ))
65 .execute(&conn)
66 {
67 Ok(_) => {
68 return HttpResponse::Ok().body("Changed product successfully")
69 }
70 _ => {
71 return HttpResponse::InternalServerError()
72 .body("Unable to update record")
73 }
74 }
75}
76
77pub async fn get_all_products(pool: web::Data<TPool>) -> impl Responder {
78 let conn = pool.get().unwrap();
79 info!("Generating and returning catalog ...");
80 match product.load::<Product>(&conn) {
81 Ok(products) => return HttpResponse::Ok().json(&products),
82 Err(_) => {
83 return HttpResponse::InternalServerError()
84 .body("Unable to fetch product catalog")
85 }
86 }
87}
88
89#[derive(Serialize, Deserialize, Debug)]
90struct ProductRating {
91 pub comment_text: Option<String>,
92 pub comment_date: NaiveDate,
93 pub product_name: String,
94 pub customer_name: String,
95 pub stars: Option<i32>,
96}
97
98pub async fn get_product_reviews(
99 pool: web::Data<TPool>,
100 product_id: web::Path<i32>,
101) -> impl Responder {
102 let conn = pool.get().unwrap();
103 info!("Fetching product reviews for {}", product_id);
104 let pid = product_id.into_inner();
105 let rating_entries = rating::rating
106 .filter(rating::product_id.eq(pid))
107 .load::<Rating>(&conn)
108 .expect("Couldn't connect to DB");
109 let json_ratings = rating_entries
110 .into_iter()
111 .map(move |p| {
112 let selected_product = product
113 .filter(id.eq(&p.product_id.unwrap()))
114 .limit(1)
115 .first::<Product>(&conn)
116 .unwrap()
117 .name
118 .clone();
119
120 let selected_customer = cust::customer
121 .filter(cust::id.eq(&p.customer_id.unwrap()))
122 .limit(1)
123 .first::<Customer>(&conn)
124 .unwrap()
125 .username
126 .clone();
127
128 ProductRating {
129 comment_text: p.comment_text,
130 comment_date: p.comment_date.unwrap(),
131 product_name: selected_product,
132 customer_name: selected_customer,
133 stars: p.stars,
134 }
135 })
136 .collect::<Vec<_>>();
137 return HttpResponse::Ok().json(&json_ratings);
138}
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 @@
1use crate::models::{AddRating, Customer, Rating};
2use crate::schema::rating::dsl as rating;
3use crate::schema::{customer::dsl::*, product::dsl::*};
4use crate::TPool;
5
6use actix_identity::Identity;
7use actix_web::{web, HttpResponse, Responder};
8use diesel::prelude::*;
9use log::{error, info};
10use serde::Deserialize;
11
12#[derive(Deserialize, Debug)]
13pub struct AddRatingJson {
14 pub comment_text: Option<String>,
15 pub stars: Option<i32>,
16 pub product_id: i32,
17}
18
19pub async fn add_rating(
20 cookie: Identity,
21 rating_details: web::Json<AddRatingJson>,
22 pool: web::Data<TPool>,
23) -> impl Responder {
24 info!("Add rating hit: {:?}", rating_details.product_id);
25 info!("{:?}", cookie.identity());
26 let conn = pool.get().unwrap();
27 if let Some(uname) = cookie.identity() {
28 let selected_user = customer
29 .filter(username.eq(&uname))
30 .limit(1)
31 .first::<Customer>(&conn)
32 .expect("Couldn't connect to DB");
33 let rating_details = rating_details.into_inner();
34 let new_rating = AddRating {
35 comment_text: rating_details.comment_text,
36 stars: rating_details.stars,
37 product_id: rating_details.product_id,
38 customer_id: selected_user.id,
39 };
40 diesel::insert_into(rating::rating)
41 .values(new_rating)
42 .execute(&conn)
43 .expect("Coundn't connect to DB");
44 HttpResponse::Ok().body("Inserted rating successfully!")
45 } else {
46 error!("Unauthorized add rating action!");
47 return HttpResponse::Unauthorized()
48 .body("Need to be logged in to add rating!");
49 }
50}
51
52#[derive(Deserialize, Debug)]
53pub struct RemoveRating {
54 rating_id: i32,
55}
56
57pub async fn remove_rating(
58 cookie: Identity,
59 rating_details: web::Json<RemoveRating>,
60 pool: web::Data<TPool>,
61) -> impl Responder {
62 info!("Remove rating hit: {:?}", rating_details.rating_id);
63 let conn = pool.get().unwrap();
64 if let Some(uname) = cookie.identity() {
65 let selected_user = customer
66 .filter(username.eq(&uname))
67 .limit(1)
68 .first::<Customer>(&conn)
69 .expect("Couldn't connect to DB");
70
71 diesel::delete(
72 rating::rating
73 .filter(rating::customer_id.eq(selected_user.id))
74 .filter(rating::id.eq(rating_details.rating_id)),
75 )
76 .execute(&conn)
77 .expect("Coundn't connect to DB");
78 HttpResponse::Ok().body("Removed successfully!")
79 } else {
80 error!("Unauthorized add to cart action!");
81 return HttpResponse::Unauthorized()
82 .body("Need to be logged in to add to cart!");
83 }
84}
85
86// pub async fn get_product_reviews(
87// product: web::Json<GetProductReviews>,
88// pool: web::Data<TPool>,
89// ) -> impl Responder {
90// unimplemented!()
91// }
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 @@
1use actix_web::{get, post, HttpResponse, Responder};
2
3#[get("/")]
4async fn hello() -> impl Responder {
5 HttpResponse::Ok().body("Hello world!")
6}
7
8#[post("/echo")]
9async fn echo(req_body: String) -> impl Responder {
10 HttpResponse::Ok().body(req_body)
11}
12
13pub async fn manual_hello() -> impl Responder {
14 HttpResponse::Ok().body("Hey there!")
15}
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 @@
1use crate::models::{Customer, NewCustomer};
2use crate::schema::customer::dsl::*;
3use crate::TPool;
4
5use actix_identity::Identity;
6use actix_web::{web, HttpResponse, Responder};
7use bcrypt::{hash, verify, DEFAULT_COST};
8use diesel::prelude::*;
9use log::{error, info};
10use serde::Deserialize;
11
12pub async fn new_user(
13 pool: web::Data<TPool>,
14 item: web::Json<NewCustomer>,
15) -> impl Responder {
16 info!("Creating ... {:?}", item.username);
17 let conn = pool.get().unwrap();
18 let hashed_item = NewCustomer {
19 password: hash(&item.password, DEFAULT_COST).unwrap(),
20 ..(item.into_inner())
21 };
22 diesel::insert_into(customer)
23 .values(hashed_item)
24 .execute(&conn)
25 .expect("Coundn't connect to DB");
26 HttpResponse::Ok().body("Inserted successfully!")
27}
28
29pub async fn name_exists(
30 pool: web::Data<TPool>,
31 item: String,
32) -> impl Responder {
33 let conn = pool.get().unwrap();
34 info!("target: {:?}", item);
35 if (customer
36 .filter(username.eq(&item))
37 .limit(1)
38 .load::<Customer>(&conn)
39 .expect("Coundn't connect to DB"))
40 .len()
41 > 0
42 {
43 HttpResponse::Ok().body("true")
44 } else {
45 HttpResponse::Ok().body("false")
46 }
47}
48
49#[derive(Deserialize)]
50pub struct Login {
51 username: String,
52 password: String,
53}
54
55pub async fn login(
56 pool: web::Data<TPool>,
57 cookie: Identity,
58 login_details: web::Json<Login>,
59) -> impl Responder {
60 info!("Login hit");
61 if let Some(uname) = cookie.identity() {
62 info!("Found existing cookie: {:?}", cookie.identity());
63 return HttpResponse::Ok().finish();
64 }
65 let conn = pool.get().unwrap();
66 let entered_pass = &login_details.password;
67 let selected_user = customer
68 .filter(username.eq(&login_details.username))
69 .limit(1)
70 .first::<Customer>(&conn)
71 .expect("Couldn't connect to DB");
72 let hashed_pass = selected_user.password;
73 if verify(entered_pass, &hashed_pass).unwrap() {
74 cookie.remember(login_details.username.clone());
75 info!(
76 "Successful login: {} {}",
77 selected_user.username, selected_user.email_id
78 );
79 HttpResponse::Ok().finish()
80 } else {
81 HttpResponse::Unauthorized().finish()
82 }
83}
84
85pub async fn logout(cookie: Identity) -> impl Responder {
86 cookie.forget();
87 HttpResponse::Found().header("location", "/").finish()
88}
89
90pub async fn user_details(
91 uname: web::Path<String>,
92 pool: web::Data<TPool>,
93) -> impl Responder {
94 let conn = pool.get().unwrap();
95 let uname = uname.into_inner();
96 info!("Fetching info for: \"{}\"", uname);
97 let selected_user = customer
98 .filter(username.eq(&uname))
99 .limit(1)
100 .first::<Customer>(&conn);
101 match selected_user {
102 Ok(m) => {
103 info!("Found user: {}", uname);
104 HttpResponse::Ok().json(m)
105 }
106 Err(_) => {
107 error!("User not found: {}", uname);
108 HttpResponse::NotFound().finish()
109 }
110 }
111}
112
113#[derive(Deserialize, Debug)]
114pub struct ChangePassword {
115 old_password: String,
116 new_password: String,
117}
118
119pub async fn change_password(
120 cookie: Identity,
121 password_details: web::Json<ChangePassword>,
122 pool: web::Data<TPool>,
123) -> impl Responder {
124 info!("Change password request: {:?}", password_details);
125 let conn = pool.get().unwrap();
126 if let Some(uname) = cookie.identity() {
127 let entered_pass = &password_details.old_password;
128 let new_password = &password_details.new_password;
129 let selected_user = customer
130 .filter(username.eq(&uname))
131 .limit(1)
132 .first::<Customer>(&conn)
133 .expect("Couldn't connect to DB");
134 let hashed_pass = selected_user.password;
135 if verify(entered_pass, &hashed_pass).unwrap() {
136 let hashed_new_password =
137 hash(&new_password, DEFAULT_COST).unwrap();
138 diesel::update(customer.filter(id.eq(selected_user.id)))
139 .set(password.eq(hashed_new_password))
140 .execute(&conn)
141 .unwrap();
142 return HttpResponse::Ok().body("Changed password successfully");
143 } else {
144 return HttpResponse::Ok().body("Invalid password");
145 }
146 }
147 return HttpResponse::Unauthorized().body("Login first");
148}