pharmacy/src/main.rs

177 lines
6.2 KiB
Rust

#[macro_use]
extern crate rocket;
use rocket::{form::Form, State, Request, Response, http::Header};
use rocket::fairing::{Fairing, Info, Kind};
use uuid::Uuid;
use rand::Rng;
use argon2::{
password_hash::{
rand_core::OsRng,
PasswordHash, PasswordHasher, PasswordVerifier, SaltString
},
Argon2
};
use std::env;
mod database;
use crate::database::*;
mod structures;
use crate::structures::*;
pub struct CORS;
#[rocket::async_trait]
impl Fairing for CORS {
fn info(&self) -> Info {
Info {
name: "Attaching CORS headers to responses",
kind: Kind::Response
}
}
async fn on_response<'r>(&self, _request: &'r Request<'_>, response: &mut Response<'r>) {
response.set_header(Header::new("Access-Control-Allow-Origin", "*"));
response.set_header(Header::new("Access-Control-Allow-Methods", "POST, GET, PATCH, OPTIONS"));
response.set_header(Header::new("Access-Control-Allow-Headers", "*"));
response.set_header(Header::new("Access-Control-Allow-Credentials", "true"));
}
}
#[post("/api/new", data = "<request>")]
fn new_product(db: &State<sled::Db>, argon2: &State<Argon2>, salt: &State<SaltString>, request: Form<ProductRequest>) -> String {
let password = request.password.as_bytes();
let hashed_password = argon2.hash_password(password, &salt.as_ref()).unwrap().to_string();
let mut admin_password = env::var("ADMIN_PASSWD").unwrap();
admin_password = argon2.hash_password(admin_password.as_bytes(), &salt.as_ref()).unwrap().to_string();
println!("salt is: {}\n hashed password is: {}\nadmin password is: {}", &salt.as_ref(), hashed_password, admin_password);
if hashed_password == admin_password {
let new_product: Product = Product {
short_name: request.short_name.clone(),
full_name: request.full_name.clone(),
price_usd: request.price_usd,
stock: request.stock,
};
register_product(db, &new_product.short_name, &new_product);
let new = read_product(db, &request.short_name).unwrap().unwrap();
return format!(
"registered new produt {} ${}. There are {} left in stock.",
new.full_name, new.price_usd, new.stock
);
};
return format!("wrong password!");
}
#[post("/api/order", data = "<order>")]
fn new_order(order: Form<OrderRequest<'_>>) -> String {
let mut rng = rand::thread_rng();
let to_hash = format!("{}{}{}", order.user_message.to_string(), order.encryption_key.to_string(), rng.gen::<f64>().to_string());
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()),
};
make_order(&db_order);
format!("Thank you for ordering from Catgirl Pharmacy!\nYour order Uuid is {}", db_order.uuid.to_hyphenated().to_string())
}
#[get("/api/order/<uuid>")]
fn order_info(uuid: &str) -> String {
let order = read_order(uuid).unwrap().unwrap();
format!("Uuid: {}\nName: {}\nEmail: {}\nMessage: {}\nEncryption Key: {}\nPayment Type: {:?}",
order.uuid,
order.name,
order.email,
order.message,
order.encryption_key,
order.payment_type
)
}
#[post("/api/orders", data="<auth>")]
fn all_orders(auth: Form<Authenticate>, argon2: &State<Argon2>, salt: &State<SaltString>) -> String {
let entered_password = auth.password.as_bytes();
let hashed_password = argon2.hash_password(entered_password, &salt.as_ref()).unwrap().to_string();
let mut admin_password = env::var("ADMIN_PASSWD").unwrap();
admin_password = argon2.hash_password(admin_password.as_bytes(), &salt.as_ref()).unwrap().to_string();
if admin_password == hashed_password {
let all_orders = read_all_orders();
let mut return_string = String::new();
for x in &all_orders {
return_string = format!("{}\n{:?}",return_string, x);
}
return return_string;
} else {
return "wrong password".to_string();
}
}
#[post("/api/update", data = "<update>")]
fn update_product(db: &State<sled::Db>, argon2: &State<Argon2>, salt: &State<SaltString>, update: Form<Update<'_>>) -> String {
let password = update.password.as_bytes();
let hashed_password = argon2.hash_password(password, &salt.as_ref()).unwrap().to_string();
let mut admin_password = env::var("ADMIN_PASSWD").unwrap();
admin_password = argon2.hash_password(admin_password.as_bytes(), &salt.as_ref()).unwrap().to_string();
if admin_password == hashed_password {
match update.field {
"price_usd" => {
let mut new_product = read_product(db, &update.product).unwrap().unwrap();
new_product.price_usd = update.value;
register_product(db, &update.product, &new_product);
return "price changed".to_string();
}
"stock" => {
let mut new_product = read_product(db, &update.product).unwrap().unwrap();
new_product.stock = update.value as u32;
register_product(db, &update.product, &new_product);
return "stock changed".to_string();
}
_ => return "field not found".to_string(),
}
};
return "wrong password".to_string();
}
#[get("/api/stock/<product>")]
fn get_stock(db: &State<sled::Db>, product: &str) -> String {
read_product(db, &product)
.unwrap()
.unwrap()
.stock
.to_string()
}
#[get("/api/price/<product>")]
fn get_price(db: &State<sled::Db>, product: &str) -> String {
read_product(db, &product)
.unwrap()
.unwrap()
.price_usd
.to_string()
}
#[launch]
fn rocket() -> _ {
rocket::build()
.manage(database::open())
.manage(Argon2::default())
.manage(SaltString::generate(&mut OsRng))
.mount("/",
routes![get_stock, get_price, new_order, order_info, all_orders, new_product, update_product])
.attach(CORS)
}