Add error handling
This commit is contained in:
parent
de2d00cce6
commit
4e3d856128
|
@ -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
|
||||
|
|
178
src/api.rs
178
src/api.rs
|
@ -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),
|
||||
});
|
||||
}
|
||||
|
|
173
src/database.rs
173
src/database.rs
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue