Add error handling

This commit is contained in:
~erin 2021-11-23 11:54:02 -05:00
parent de2d00cce6
commit 4e3d856128
No known key found for this signature in database
GPG Key ID: DA70E064A8C70F44
3 changed files with 265 additions and 90 deletions

View File

@ -14,8 +14,8 @@ A simple server for dealing with products & orders for the Catgirl Pharmacy.
- [x] Add frontend to update orders
- [x] Store admin login in localStorage
- [x] Finalize CAPTCHA functionality w/ cookies [https://rocket.rs/v0.5-rc/guide/requests/#private-cookies]
- [ ] Add proper error handling
- [ ] Store admin password more securely
- [x] Add proper error handling
- [x] Store admin password more securely
- [x] Cleanup code
- [x] Add comments
- [x] Add logging

View File

@ -15,12 +15,11 @@ use argon2::{password_hash::SaltString, Argon2};
pub fn new_product(
db: &State<sled::Db>,
argon2: &State<Argon2>,
salt: &State<SaltString>,
request: Form<ProductRequest>,
) -> Json<Status> {
/* Need to rework the password mechanism so it's not stored in plaintext serverside */
if check_password(&request.password, &salt, &argon2) {
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
@ -29,7 +28,18 @@ pub fn new_product(
price_usd: request.price_usd,
stock: request.stock,
};
register_product(db, &new_product.short_name, &new_product); // Register the new product in the database
match register_product(db, &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(),
})
}
}
return Json(Status {
// Return a JSON status
status: "success".to_string(),
@ -66,7 +76,14 @@ pub fn new_order(cookies: &CookieJar<'_>, order: Form<OrderRequest<'_>>) -> Stri
uuid: Uuid::new_v5(&Uuid::NAMESPACE_X500, to_hash.as_bytes()),
status: OrderStatus::Submitted,
};
make_order(&db_order);
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()
@ -78,19 +95,33 @@ pub fn new_order(cookies: &CookieJar<'_>, order: Form<OrderRequest<'_>>) -> Stri
// Modify an order
#[post("/api/order/update", data = "<update>")]
pub fn update_order(
argon2: &State<Argon2>,
salt: &State<SaltString>,
update: Form<OrderUpdate<'_>>,
) -> Json<Status> {
if check_password(&update.password, &salt, &argon2) {
let mut new_order = read_order(&update.order_uuid).unwrap().unwrap();
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;
make_order(&new_order);
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: "meowy".to_string(),
reason: "updated order".to_string(),
});
} else {
return Json(Status {
@ -102,20 +133,56 @@ pub fn update_order(
// Get the details of a specific order from it's Uuid
#[get("/api/order/<uuid>")]
pub fn order_info(uuid: &str) -> Json<Order> {
let order = read_order(uuid).unwrap().unwrap(); // Read the order from the database
return Json(order); // Serialize it into json & return
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>,
salt: &State<SaltString>,
) -> Result<Json<Vec<Order>>, Json<Status>> {
if check_password(&auth.password, &salt, &argon2) {
let all_orders = read_all_orders(); // Read all orders from the database
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
@ -134,14 +201,17 @@ pub fn all_orders(
// Delete an order
#[post("/api/delete/<order>", data = "<auth>")]
pub fn delete_order(
auth: Form<Authenticate>,
order: &str,
argon2: &State<Argon2>,
salt: &State<SaltString>,
) -> Json<Status> {
if check_password(&auth.password, &salt, &argon2) {
remove_order(&order).unwrap();
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(),
@ -159,16 +229,35 @@ pub fn delete_order(
pub fn update_product(
db: &State<sled::Db>,
argon2: &State<Argon2>,
salt: &State<SaltString>,
update: Form<Update<'_>>,
) -> Json<Status> {
if check_password(&update.password, &salt, &argon2) {
if check_password(&update.password, &argon2) {
match update.field {
// Check what field is being updated
"price_usd" => {
let mut new_product = read_product(db, &update.product).unwrap().unwrap(); // Read the product from the database
let mut new_product = match read_product(db, &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; // Change the price_usd value to the provided value
register_product(db, &update.product, &new_product); // Update the product in the database
match register_product(db, &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(),
})
}
}
return Json(Status {
// Return a json success status
status: "success".to_string(),
@ -177,9 +266,27 @@ pub fn update_product(
}
"stock" => {
// Same as before, but with stock
let mut new_product = read_product(db, &update.product).unwrap().unwrap();
let mut new_product = match read_product(db, &update.product) {
Ok(product) => product,
Err(error) => {
return Json(Status {
status: "fail".to_string(),
reason: error.to_string(),
});
}
};
new_product.stock = update.value as u32;
register_product(db, &update.product, &new_product);
match register_product(db, &update.product, &new_product) {
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: "stock changed".to_string(),
@ -204,5 +311,8 @@ pub fn update_product(
// Get all data about a product
#[get("/api/product/<product>")]
pub fn get_product_info(db: &State<sled::Db>, product: &str) -> Json<Product> {
return Json(read_product(db, &product).unwrap().unwrap());
return Json(match read_product(db, &product) {
Ok(product) => product,
Err(error) => panic!("{:?}", error),
});
}

View File

@ -1,79 +1,135 @@
// File for database functions
use crate::structures::*;
// no idea tbh
type MyErrorType = Box<dyn std::error::Error>;
// Open the products database
pub fn open() -> sled::Db {
sled::open("products_db").unwrap()
}
// Register a new product
pub fn register_product(db: &sled::Db, short_name: &str, product: &Product) {
info!("Registering new product {:?}", &product);
let bytes = bincode::serialize(&product).unwrap(); //Serialize the data
db.insert(short_name, bytes).unwrap(); // Insert the product using the short name as a key
db.flush().unwrap();
}
// Read info about a product
pub fn read_product(
db: &sled::Db,
short_name: &str,
) -> std::result::Result<Option<Product>, MyErrorType> {
info!("Reading product from shortcode {}", short_name);
let entry = db.get(short_name)?; // Get a product entry out of the database using the short name
if let Some(product_entry) = entry {
// If it can read it, deserialize the entry then return it
info!("Succesfully read product from products_db");
let read_product: Product = bincode::deserialize(&product_entry)?;
db.flush().unwrap();
Ok(Some(read_product))
} else {
warn!("Failed to find product {} in products_db", short_name);
db.flush().unwrap(); // Otherwise error
Ok(None)
match sled::open("products_db") {
Ok(database) => database,
Err(error) => panic!("error opening database: {:?}", error),
}
}
// Register a new product
pub fn register_product(
db: &sled::Db,
short_name: &str,
product: &Product,
) -> Result<String, sled::Error> {
info!("Registering new product {:?}", &product);
let bytes = bincode::serialize(&product).unwrap(); //Serialize the data
match db.insert(short_name, bytes) {
// Insert the product using the short name as a key
Ok(_) => info!("inserted product"),
Err(error) => return Err(error),
}
match db.flush() {
Ok(_) => info!("flushed database"),
Err(error) => return Err(error),
}
return Ok("succesfully registered product".to_string());
}
// Read info about a product
pub fn read_product(db: &sled::Db, short_name: &str) -> Result<Product, sled::Error> {
info!("Reading product from shortcode {}", short_name);
let entry = match db.get(short_name) {
Ok(entry) => entry,
Err(error) => return Err(error),
}; // Get a product entry out of the database using the short name
if let Some(product_entry) = entry {
let read_product = match bincode::deserialize(&product_entry) {
Ok(product) => product,
Err(error) => panic!("could not deserialize product: {:?}", error),
};
return Ok(read_product);
} else {
warn!("product not found");
return Err(sled::Error::Unsupported(
sled::Error::ReportableBug("product not found".to_string()).to_string(),
));
};
}
// Create a new order
pub fn make_order(order: &Order) {
pub fn make_order(order: &Order) -> Result<String, sled::Error> {
info!(
"Creating new order with Uuid {}",
&order.uuid.to_hyphenated().to_string()
);
let db = sled::open("order_db").unwrap(); // Open the orders database
let bytes = bincode::serialize(&order).unwrap(); // Serialize the new order
db.insert(order.uuid.to_hyphenated().to_string(), bytes)
.unwrap(); // Insert using the UUID as key
db.flush().unwrap();
let db = match sled::open("order_db") {
// Open the orders database
Ok(database) => database,
Err(error) => return Err(error),
};
let bytes = match bincode::serialize(&order) {
// Serialize the new order
Ok(bytes) => bytes,
Err(error) => panic!("could not serialize order: {:?}", error),
};
match db.insert(order.uuid.to_hyphenated().to_string(), bytes) {
// Insert using the UUID as key
Ok(_) => info!("succesfully inserted product"),
Err(error) => return Err(error),
}
match db.flush() {
Ok(_) => info!("flushed database"),
Err(error) => return Err(error),
}
return Ok("succesfully made new order".to_string());
}
// Read an order from UUID
pub fn read_order(uuid: &str) -> std::result::Result<Option<Order>, MyErrorType> {
pub fn read_order(uuid: &str) -> Result<Order, sled::Error> {
info!("Reading order with Uuid {}", uuid);
let db = sled::open("order_db").unwrap(); // Open the orders database
let entry = db.get(uuid)?; // Read an entry using UUID as a key
let db = match sled::open("order_db") {
// Open the orders database
Ok(database) => database,
Err(error) => return Err(error),
};
let entry = match db.get(uuid) {
// Read an entry using UUID as a key
Ok(order) => order,
Err(error) => return Err(error),
};
if let Some(order_entry) = entry {
let read_order: Order = bincode::deserialize(&order_entry)?; // If succesful, deserialize & return
let read_order: Order = match bincode::deserialize(&order_entry) {
// If succesful, deserialize & return
Ok(order) => order,
Err(error) => panic!("could not deserialize order: {:?}", error),
};
info!("Succesfully deserialized order");
db.flush().unwrap();
Ok(Some(read_order))
return Ok(read_order);
} else {
// Fail
warn!("Failed on reading order from orders_db with Uuid {}", uuid);
db.flush().unwrap();
Ok(None)
}
return Err(sled::Error::ReportableBug("order not found".to_string()));
};
}
// Read all orders pushed to the database
pub fn read_all_orders() -> Vec<Order> {
pub fn read_all_orders() -> Result<Vec<Order>, sled::Error> {
info!("Reading all orders in orders_db");
let db = sled::open("order_db").unwrap(); // Open the orders database
let db = match sled::open("order_db") {
Ok(database) => database,
Err(error) => return Err(error),
}; // Open the orders database
let first_key = match db.first() {
Ok(key) => key.unwrap().0,
Err(error) => return Err(error),
}; // Get the first key from the database
let first_key: &[u8] = &db.first().unwrap().unwrap().0; // Get the first key from the database
let iter = db.range(first_key..); // Create an iterator of all keys from the first one
let mut all_orders: Vec<Order> = Vec::new(); // Create a new vector to store the orders
@ -81,22 +137,31 @@ pub fn read_all_orders() -> Vec<Order> {
// Iterate over the orders
if let order_entry = order.unwrap().1 {
// If we can unwrap it:
let read_order: Order = bincode::deserialize(&order_entry).unwrap(); // Deserialize the entry
db.flush().unwrap();
let read_order: Order = match bincode::deserialize(&order_entry) {
Ok(order) => order,
Err(error) => panic!("could not deserialize order: {:?}", error),
}; // Deserialize the entry
all_orders.push(read_order); // Add the entry to the vector
} else {
// Fail
info!("Could not find any more orders");
warn!("Could not find any more orders");
db.flush().unwrap();
}
}
return all_orders; // Return the orders
return Ok(all_orders); // Return the orders
}
// Remove an order from the database
pub fn remove_order(uuid: &str) -> std::result::Result<Option<sled::IVec>, sled::Error> {
pub fn remove_order(uuid: &str) -> Result<String, sled::Error> {
info!("Removing order with Uuid {}", uuid);
let db = sled::open("order_db").unwrap();
db.remove(uuid)
let db = match sled::open("order_db") {
Ok(database) => database,
Err(error) => return Err(error),
};
match db.remove(uuid) {
Ok(_) => Ok("removed order".to_string()),
Err(error) => return Err(error),
}
}