353 lines
12 KiB
Rust
353 lines
12 KiB
Rust
use rocket::serde::json::Json;
|
|
use rocket::{form::Form, http::CookieJar, State};
|
|
|
|
use rand::Rng;
|
|
use uuid::Uuid;
|
|
|
|
use crate::authenticate::*;
|
|
use crate::database::*;
|
|
use crate::structures::*;
|
|
|
|
use crate::html::{render_product, render_products};
|
|
use argon2::{password_hash::SaltString, Argon2};
|
|
|
|
// Endpoint to add a new product
|
|
#[post("/api/new", data = "<request>")]
|
|
pub fn new_product(argon2: &State<Argon2>, request: Form<ProductRequest>) -> Json<Status> {
|
|
/* Need to rework the password mechanism so it's not stored in plaintext serverside */
|
|
|
|
if check_password(&request.password, &argon2) {
|
|
// Check if the password is correct
|
|
let new_product: Product = Product {
|
|
// Create a new Product object from the supplied info
|
|
short_name: request.short_name.clone(),
|
|
full_name: request.full_name.clone(),
|
|
description: request.description.clone(),
|
|
price_usd: request.price_usd,
|
|
stock: request.stock,
|
|
class: request.class,
|
|
};
|
|
|
|
match register_product(&new_product.short_name, &new_product) {
|
|
// Register the new product in the database
|
|
Ok(s) => info!("{}", s),
|
|
Err(error) => {
|
|
return Json(Status {
|
|
status: "fail".to_string(),
|
|
reason: error.to_string(),
|
|
})
|
|
}
|
|
}
|
|
|
|
render_products();
|
|
render_product(new_product);
|
|
|
|
return Json(Status {
|
|
// Return a JSON status
|
|
status: "success".to_string(),
|
|
reason: "registered new product".to_string(),
|
|
});
|
|
};
|
|
|
|
return Json(Status {
|
|
// Return a JSON status fail
|
|
status: "fail".to_string(),
|
|
reason: "wrong password".to_string(),
|
|
});
|
|
}
|
|
|
|
// Endpoint to place a new order
|
|
#[post("/api/order", data = "<order>")]
|
|
pub fn new_order(cookies: &CookieJar<'_>, order: Form<OrderRequest<'_>>) -> String {
|
|
let mut rng = rand::thread_rng(); // Get a new random number
|
|
let to_hash = format!(
|
|
"{}{}{}",
|
|
order.user_message.to_string(),
|
|
order.encryption_key.to_string(),
|
|
rng.gen::<f64>().to_string()
|
|
); // Create a has from various components of the order + a random number
|
|
|
|
let user_token = cookies.get_private("token").unwrap();
|
|
if user_token.value() == order.captcha {
|
|
let db_order: Order = Order {
|
|
name: order.name.to_string(),
|
|
message: order.user_message.to_string(),
|
|
encryption_key: order.encryption_key.to_string(),
|
|
email: order.email.to_string(),
|
|
payment_type: order.payment_type.clone(),
|
|
uuid: Uuid::new_v5(&Uuid::NAMESPACE_X500, to_hash.as_bytes()),
|
|
status: OrderStatus::Submitted,
|
|
};
|
|
|
|
match make_order(&db_order) {
|
|
Ok(s) => info!("{}", s),
|
|
Err(error) => {
|
|
return format!("Failed to submit order: {:?}", error);
|
|
}
|
|
}
|
|
|
|
return format!(
|
|
"Thank you for ordering from Catgirl Pharmacy!\nYour order Uuid is {}",
|
|
db_order.uuid.to_hyphenated().to_string()
|
|
);
|
|
} else {
|
|
return "Incorrect CAPTCHA answer.\nPlease try again!".to_string();
|
|
}
|
|
}
|
|
|
|
// Modify an order
|
|
#[post("/api/order/update", data = "<update>")]
|
|
pub fn update_order(argon2: &State<Argon2>, update: Form<OrderUpdate<'_>>) -> Json<Status> {
|
|
if check_password(&update.password, &argon2) {
|
|
let mut new_order = match read_order(&update.order_uuid) {
|
|
Ok(order) => order,
|
|
Err(error) => {
|
|
return Json(Status {
|
|
status: "fail".to_string(),
|
|
reason: error.to_string(),
|
|
})
|
|
}
|
|
};
|
|
new_order.status = update.status;
|
|
new_order.payment_type = update.payment_type;
|
|
|
|
match make_order(&new_order) {
|
|
Ok(s) => info!("{}", s),
|
|
Err(error) => {
|
|
return Json(Status {
|
|
status: "fail".to_string(),
|
|
reason: error.to_string(),
|
|
})
|
|
}
|
|
}
|
|
|
|
return Json(Status {
|
|
status: "success".to_string(),
|
|
reason: "updated order".to_string(),
|
|
});
|
|
} else {
|
|
return Json(Status {
|
|
status: "fail".to_string(),
|
|
reason: "wrong password".to_string(),
|
|
});
|
|
}
|
|
}
|
|
|
|
// Get the details of a specific order from it's Uuid
|
|
#[get("/api/order/<uuid>")]
|
|
pub fn order_info(uuid: &str) -> Result<Json<Order>, Json<Status>> {
|
|
let order = match read_order(uuid) {
|
|
Ok(order) => order,
|
|
Err(error) => {
|
|
return Err(Json(Status {
|
|
status: "fail".to_string(),
|
|
reason: error.to_string(),
|
|
}));
|
|
}
|
|
}; // Read the order from the database
|
|
return Ok(Json(order)); // Serialize it into json & return
|
|
}
|
|
|
|
// Set the admin password
|
|
#[post("/api/password", data = "<auth>")]
|
|
pub fn set_password(
|
|
auth: Form<Authenticate>,
|
|
argon2: &State<Argon2>,
|
|
salt: &State<SaltString>,
|
|
) -> Json<Status> {
|
|
if admin_password_exists() {
|
|
return Json(Status {
|
|
status: "fail".to_string(),
|
|
reason: "password already exists".to_string(),
|
|
});
|
|
} else {
|
|
set_admin_password(&auth.password, &salt, &argon2);
|
|
return Json(Status {
|
|
status: "success".to_string(),
|
|
reason: "set admin password".to_string(),
|
|
});
|
|
}
|
|
}
|
|
// Get all orders made
|
|
#[post("/api/orders", data = "<auth>")]
|
|
pub fn all_orders(
|
|
auth: Form<Authenticate>,
|
|
argon2: &State<Argon2>,
|
|
) -> Result<Json<Vec<Order>>, Json<Status>> {
|
|
if check_password(&auth.password, &argon2) {
|
|
let all_orders = match read_all_orders() {
|
|
Ok(orders) => orders,
|
|
Err(error) => {
|
|
return Err(Json(Status {
|
|
status: "fail".to_string(),
|
|
reason: error.to_string(),
|
|
}))
|
|
}
|
|
}; // Read all orders from the database
|
|
|
|
let mut orders_list: Vec<Order> = Vec::new(); // Create a new vector of Order's
|
|
for x in all_orders {
|
|
// Loop through all read orders
|
|
orders_list.push(x); // Push each entry to the vector
|
|
}
|
|
|
|
return Ok(Json(orders_list)); // Return the orders serialized as json
|
|
} else {
|
|
return Err(Json(Status {
|
|
// Fail on wrong password
|
|
status: "fail".to_string(),
|
|
reason: "wrong password".to_string(),
|
|
}));
|
|
}
|
|
}
|
|
|
|
// Delete an order
|
|
#[post("/api/delete/<order>", data = "<auth>")]
|
|
pub fn delete_order(auth: Form<Authenticate>, order: &str, argon2: &State<Argon2>) -> Json<Status> {
|
|
if check_password(&auth.password, &argon2) {
|
|
match remove_order(&order) {
|
|
Ok(s) => info!("{}", s),
|
|
Err(error) => {
|
|
return Json(Status {
|
|
status: "fail".to_string(),
|
|
reason: error.to_string(),
|
|
})
|
|
}
|
|
};
|
|
return Json(Status {
|
|
status: "success".to_string(),
|
|
reason: "deleted order".to_string(),
|
|
});
|
|
} else {
|
|
return Json(Status {
|
|
status: "fail".to_string(),
|
|
reason: "wrong password".to_string(),
|
|
});
|
|
}
|
|
}
|
|
|
|
// Update fields of a product
|
|
#[post("/api/update", data = "<update>")]
|
|
pub fn update_product(argon2: &State<Argon2>, update: Form<Update<'_>>) -> Json<Status> {
|
|
if check_password(&update.password, &argon2) {
|
|
match update.field {
|
|
// Check what field is being updated
|
|
UpdateField::Price => {
|
|
let mut new_product = match read_product(&update.product) {
|
|
// Read the product from the database
|
|
Ok(product) => product,
|
|
Err(error) => {
|
|
return Json(Status {
|
|
status: "fail".to_string(),
|
|
reason: error.to_string(),
|
|
});
|
|
}
|
|
};
|
|
|
|
new_product.price_usd = update.value.unwrap(); // Change the price_usd value to the provided value
|
|
match register_product(&update.product, &new_product) {
|
|
// Update the product in the database
|
|
Ok(s) => info!("{}", s),
|
|
Err(error) => {
|
|
return Json(Status {
|
|
status: "fail".to_string(),
|
|
reason: error.to_string(),
|
|
})
|
|
}
|
|
}
|
|
|
|
render_product(new_product);
|
|
|
|
return Json(Status {
|
|
// Return a json success status
|
|
status: "success".to_string(),
|
|
reason: "price changed".to_string(),
|
|
});
|
|
}
|
|
UpdateField::Stock => {
|
|
// Same as before, but with stock
|
|
let mut new_product = match read_product(&update.product) {
|
|
Ok(product) => product,
|
|
Err(error) => {
|
|
return Json(Status {
|
|
status: "fail".to_string(),
|
|
reason: error.to_string(),
|
|
});
|
|
}
|
|
};
|
|
|
|
new_product.stock = update.value.unwrap() as u32;
|
|
match register_product(&update.product, &new_product) {
|
|
Ok(s) => info!("{}", s),
|
|
Err(error) => {
|
|
return Json(Status {
|
|
status: "fail".to_string(),
|
|
reason: error.to_string(),
|
|
})
|
|
}
|
|
}
|
|
|
|
render_product(new_product);
|
|
|
|
return Json(Status {
|
|
status: "success".to_string(),
|
|
reason: "stock changed".to_string(),
|
|
});
|
|
}
|
|
UpdateField::Description => {
|
|
// Update the description
|
|
let mut new_product = match read_product(&update.product) {
|
|
Ok(product) => product,
|
|
Err(error) => {
|
|
return Json(Status {
|
|
status: "fail".to_string(),
|
|
reason: error.to_string(),
|
|
});
|
|
}
|
|
};
|
|
|
|
new_product.description = update.text.unwrap().to_string();
|
|
println!("Description: {}", &new_product.description);
|
|
match register_product(&update.product, &new_product) {
|
|
Ok(s) => info!("{}", s),
|
|
Err(error) => {
|
|
return Json(Status {
|
|
status: "fail".to_string(),
|
|
reason: error.to_string(),
|
|
})
|
|
}
|
|
}
|
|
|
|
render_product(new_product);
|
|
|
|
return Json(Status {
|
|
status: "success".to_string(),
|
|
reason: "description changed".to_string(),
|
|
});
|
|
}
|
|
|
|
_ => {
|
|
// Fail because provided field wasn't found, return json
|
|
return Json(Status {
|
|
status: "fail".to_string(),
|
|
reason: "field not found".to_string(),
|
|
});
|
|
}
|
|
}
|
|
};
|
|
return Json(Status {
|
|
// Return JSON fail due to wrong password
|
|
status: "fail".to_string(),
|
|
reason: "wrong password".to_string(),
|
|
});
|
|
}
|
|
|
|
// Get all data about a product
|
|
#[get("/api/product/<product>")]
|
|
pub fn get_product_info(product: &str) -> Json<Product> {
|
|
return Json(match read_product(&product) {
|
|
Ok(product) => product,
|
|
Err(error) => panic!("{:?}", error),
|
|
});
|
|
}
|