From 57d558e254909dfa71c344f8f1c28f0d1e7cfccb Mon Sep 17 00:00:00 2001 From: SadlyNotSappho Date: Tue, 19 Mar 2024 12:10:47 -0700 Subject: [PATCH] start working on removing stupid shit i made and fixing errors n' all that --- src/lib.rs | 10 -- src/main.rs | 477 ++++++++++++++++++++++++++------------------------ src/tables.rs | 59 ++----- 3 files changed, 262 insertions(+), 284 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 38a7df4..6add084 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1 @@ pub mod tables; - -pub struct Status { - pub status: StatusTypes, - pub data: T -} - -pub enum StatusTypes { - Success, - Faliure -} diff --git a/src/main.rs b/src/main.rs index 8669028..0fd52c9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,17 +1,17 @@ -use fossil::StatusTypes; use image::io::Reader; use rocket::fairing::AdHoc; use rocket::fs::{FileServer, NamedFile}; use rocket::http::Status; use rocket::response::content::{self, RawHtml}; +use rocket::response::status; use rocket::serde::Serialize; use rocket::{Build, Request, Rocket}; use rocket_db_pools::sqlx::pool::PoolConnection; use rocket_db_pools::sqlx::Postgres; use rocket_db_pools::Connection; -use uuid::Uuid; use std::fs; use std::path::Path; +use uuid::Uuid; #[macro_use] extern crate rocket; @@ -20,11 +20,11 @@ use rocket_db_pools::{ Database, }; -use rocket::serde::{json::Json, Deserialize}; -use rocket::http::CookieJar; use rocket::fs::TempFile; +use rocket::http::CookieJar; +use rocket::serde::{json::Json, Deserialize}; -use fossil::tables::{Db, Post, User, Image}; +use fossil::tables::{Db, Image, Post, User}; #[get("/login")] fn login_page() -> RawHtml { @@ -53,38 +53,36 @@ async fn createuser( mut db: Connection, info: Json, cookies: &CookieJar<'_>, -) -> String { +) -> status::Custom<&'static str> { let token = cookies.get_private("token"); match token.is_some() { - true => "You're already logged in. Log out before trying to create a new account.".to_string(), - false => { - match User::get_by_username(&mut db, &info.username).await { - Some(_) => "Username already taken. Please try again.".to_string(), - None => { - let created = User::create(&mut db, &info.username, &info.password).await; - match created.status { - StatusTypes::Success => { - match User::get_by_username(&mut db, &info.username).await { - Some(user) => match user.set_new_token(&mut db).await { - Ok(t) => { - cookies.add_private(("token", t)); - "Your account has been created and you've been automatically logged in.".to_string() - }, - Err(why) => { - eprintln!("{why:?}"); - "Couldn't log you in. Your account has been created, though.".to_string() - }, - }, - None => "Something went really wrong. I don't know what.".to_string() - } - }, - StatusTypes::Faliure => { - format!("Couldn't create user: {}", created.data) - } - } - } - } + true => { + status::Custom(Status::Forbidden, + "You're already logged in. Log out before trying to create a new account.") } + false => match User::get_by_username(&mut db, &info.username).await { + Some(_) => "Username already taken. Please try again.".to_string(), + None => match User::create(&mut db, &info.username, &info.password).await { + Ok(_) => match User::get_by_username(&mut db, &info.username).await { + Some(user) => match user.set_new_token(&mut db).await { + Ok(t) => { + cookies.add_private(("token", t)); + "Your account has been created and you've been automatically logged in." + .to_string() + } + Err(why) => { + eprintln!("{why:?}"); + "Couldn't log you in. Your account has been created, though." + .to_string() + } + }, + None => "Something went really wrong. I don't know what.".to_string(), + }, + Err(why) => { + format!("Couldn't create user: {}", why) + } + }, + }, } } @@ -92,21 +90,14 @@ async fn createuser( async fn account(mut db: Connection, cookies: &CookieJar<'_>) -> String { let token = cookies.get_private("token"); match token { - Some(t) => { - match User::get_by_token(&mut db, t).await { - Some(user) => format!( - "Username: {}\nAdmin: {}\nMake Posts: {}\nComment: {}", - user.username, - user.admin, - user.make_posts, - user.comment - ), - None => "User doesn't exist.".to_string() - } + Some(t) => match User::get_by_token(&mut db, t).await { + Some(user) => format!( + "Username: {}\nAdmin: {}\nMake Posts: {}\nComment: {}", + user.username, user.admin, user.make_posts, user.comment + ), + None => "User doesn't exist.".to_string(), }, - None => { - "Not logged in".to_string() - } + None => "Not logged in".to_string(), } } @@ -137,7 +128,7 @@ async fn login(mut db: Connection, info: Json, cookies: &CookieJa String::from("Invalid username or password (to those whining about why it doesn't tell you if the username or password is incorrect, security)") } } - None => + None => String::from("Invalid username or password (to those whining about why it doesn't tell you if the username or password is incorrect, security)"), } } @@ -149,8 +140,8 @@ async fn logout(cookies: &CookieJar<'_>) -> &'static str { Some(_) => { cookies.remove_private("token"); "Logged out." - }, - None => "Not logged in." + } + None => "Not logged in.", } } @@ -158,66 +149,69 @@ async fn logout(cookies: &CookieJar<'_>) -> &'static str { async fn adminpanel(mut db: Connection, cookies: &CookieJar<'_>) -> RawHtml { let token = cookies.get_private("token"); match token { - Some(t) => { - match User::get_by_token(&mut db, t).await { - Some(user) => match user.admin { - true => { - RawHtml( - fs::read_to_string("/srv/web/adminpanel.html").unwrap().replace("{{username}}", &user.username[..]) - ) - }, - false => RawHtml(fs::read_to_string("/srv/web/invalidperms.html").unwrap()) - } - None => RawHtml(fs::read_to_string("/srv/web/error.html").unwrap().replace("{{errorcode}}", "498")) - } + Some(t) => match User::get_by_token(&mut db, t).await { + Some(user) => match user.admin { + true => RawHtml( + fs::read_to_string("/srv/web/adminpanel.html") + .unwrap() + .replace("{{username}}", &user.username[..]), + ), + false => RawHtml(fs::read_to_string("/srv/web/invalidperms.html").unwrap()), + }, + None => RawHtml( + fs::read_to_string("/srv/web/error.html") + .unwrap() + .replace("{{errorcode}}", "498"), + ), }, - None => { - RawHtml(fs::read_to_string("/srv/web/invalidperms.html").unwrap()) - } + None => RawHtml(fs::read_to_string("/srv/web/invalidperms.html").unwrap()), } } #[derive(Deserialize, Serialize)] #[serde(crate = "rocket::serde")] struct ApiPermsResult { - perms: Result + perms: Result, } #[derive(Deserialize, Serialize)] #[serde(crate = "rocket::serde")] -struct Perms{ +struct Perms { admin: bool, make_posts: bool, - comment: bool + comment: bool, } #[get("/perms/")] -async fn api_perms(mut db: Connection, username: String, cookies: &CookieJar<'_>) -> Json { +async fn api_perms( + mut db: Connection, + username: String, + cookies: &CookieJar<'_>, +) -> Json { match cookies.get_private("token") { - Some(t) => { - match User::get_by_token(&mut db, t).await { - Some(user) => { - match user.admin { - true => { - match User::get_by_username(&mut db, &username).await { - Some(user) => { - Json(ApiPermsResult { - perms: Ok(Perms { - admin: user.admin, - make_posts: user.make_posts, - comment: user.comment - }) - }) - }, - None => { - Json(ApiPermsResult { perms: Err("User doesn't exist".to_string()) }) - } - } - }, - false => {Json(ApiPermsResult { perms: Err("You don't have the permission to do this".to_string())})} - } + Some(t) => match User::get_by_token(&mut db, t).await { + Some(user) => match user.admin { + true => match User::get_by_username(&mut db, &username).await { + Some(user) => Json(ApiPermsResult { + perms: Ok(Perms { + admin: user.admin, + make_posts: user.make_posts, + comment: user.comment, + }), + }), + None => Json(ApiPermsResult { + perms: Err("User doesn't exist".to_string()), + }), }, - None => {Json(ApiPermsResult { perms: Err("Invalid token".to_string())})}, - }}, - None => {Json(ApiPermsResult { perms: Err("Not logged in".to_string())})} + false => Json(ApiPermsResult { + perms: Err("You don't have the permission to do this".to_string()), + }), + }, + None => Json(ApiPermsResult { + perms: Err("Invalid token".to_string()), + }), + }, + None => Json(ApiPermsResult { + perms: Err("Not logged in".to_string()), + }), } } @@ -226,7 +220,7 @@ async fn api_perms(mut db: Connection, username: String, cookies: &CookieJar struct TogglePerms { perm: String, value: bool, - username: String + username: String, } #[post("/toggleperms", data = "")] @@ -243,40 +237,50 @@ async fn toggleperms( true => { match User::get_by_username(&mut db, &info.username).await { Some(toggled_user) => { - if toggled_user.username == user.username && info.perm == "admin".to_string() { + if toggled_user.username == user.username + && info.perm == "admin".to_string() + { "You can't change your own admin status".to_string() } else { - let admin_username = std::env::var("ADMIN_USERNAME").expect("set ADMIN_USERNAME env var"); + let admin_username = std::env::var("ADMIN_USERNAME") + .expect("set ADMIN_USERNAME env var"); if toggled_user.username == admin_username { "You can't change the system admin's perms.".to_string() } else { - if info.perm == "admin" && user.username != admin_username { - "You can't change other people's admin status.".to_string() + if info.perm == "admin" + && user.username != admin_username + { + "You can't change other people's admin status." + .to_string() } else { // how deep is this shit // i counted. 12. // NOW we can actually do the thing :D - let res = match toggled_user.set_role(&mut db, &info.perm, &info.value).await { + let res = match toggled_user + .set_role(&mut db, &info.perm, &info.value) + .await + { Ok(_) => "Done".to_string(), - Err(why) => format!("Couldn't update the user's role: {why}") + Err(why) => format!( + "Couldn't update the user's role: {why}" + ), }; res } } } - }, - None => "The user you're trying to toggle perms for doesn't exist.".to_string() + } + None => "The user you're trying to toggle perms for doesn't exist." + .to_string(), } - }, - false => { - "You aren't an admin.".to_string() } + false => "You aren't an admin.".to_string(), } - }, - None => "Invalid user".to_string() + } + None => "Invalid user".to_string(), } - }, - None => "Not logged in".to_string() + } + None => "Not logged in".to_string(), } } @@ -287,36 +291,35 @@ async fn get_image(image: String, mut db: Connection) -> Result {match Reader::open(format!("/srv/images/{image}.png")) { - Ok(i) => { - match i.decode() { + Ok(_) => match Reader::open(format!("/srv/images/{image}.png")) { + Ok(i) => match i.decode() { Ok(img) => { let encoder = match format { "jpg" => image::ImageFormat::Jpeg, "jpeg" => image::ImageFormat::Jpeg, "png" => image::ImageFormat::Png, - _ => {panic!("invalid format")} + _ => { + panic!("invalid format") + } }; - img.save_with_format(format!("/tmp/{image}.{format}"), encoder).unwrap(); - let file = NamedFile::open(Path::new(&format!("/tmp/{image}.{format}"))).await.unwrap(); + img.save_with_format(format!("/tmp/{image}.{format}"), encoder) + .unwrap(); + let file = NamedFile::open(Path::new(&format!("/tmp/{image}.{format}"))) + .await + .unwrap(); fs::remove_file(format!("/tmp/{image}.{format}")).unwrap_or(()); Ok(file) - }, - Err(why) => { - Err(match &why.to_string()[..] { - "Format error decoding Png: Invalid PNG signature." => { - Status::NotAcceptable - }, - _ => Status::InternalServerError - }) } - } - }, - Err(_) => Err(Status::NotFound) - }}, - Err(_) => Err(Status::ImATeapot) + Err(why) => Err(match &why.to_string()[..] { + "Format error decoding Png: Invalid PNG signature." => Status::NotAcceptable, + _ => Status::InternalServerError, + }), + }, + Err(_) => Err(Status::NotFound), + }, + Err(_) => Err(Status::ImATeapot), } } @@ -334,53 +337,52 @@ async fn upload(mut file: TempFile<'_>, cookies: &CookieJar<'_>, mut db: Connect Some(user) => { if user.make_posts == true || user.admin == true { // upload to db - match Image::create(&mut db, &uuid, user).await.status { - StatusTypes::Success => { + match Image::create(&mut db, &uuid, user).await { + Ok(_) => { // move image - match file.copy_to(format!("/srv/images/{uuid}.png")).await { + match file + .copy_to(format!("/srv/images/{uuid}.png")) + .await + { Ok(_) => { - match fs::remove_file(format!("/srv/tmpimages/{uuid}.png")) { - Ok(_) => { - uuid - }, + match fs::remove_file(format!( + "/srv/tmpimages/{uuid}.png" + )) { + Ok(_) => uuid, Err(why) => { eprintln!("{why:?}"); - "couldn't delete old file".to_string() + "couldn't delete old file" + .to_string() } - } - }, + } + } Err(why) => { eprintln!("{why:?}"); - "couldn't save file to final destination".to_string() + "couldn't save file to final destination" + .to_string() } } - }, - StatusTypes::Faliure => { - "Couldn't save to DB".to_string() } + Err(_) => "Couldn't save to DB".to_string(), } } else { "Invalid perms".to_string() } - }, - None => { - "Invalid login token".to_string() } + None => "Invalid login token".to_string(), } - }, - None => { - "Not logged in".to_string() } - } - }, + None => "Not logged in".to_string(), + } + } Err(why) => { // isn't an image, or something else went wrong eprintln!("{why:?}"); "error".to_string() } } - }, - Err(why) => why.to_string() + } + Err(why) => why.to_string(), } } @@ -388,72 +390,53 @@ async fn upload(mut file: TempFile<'_>, cookies: &CookieJar<'_>, mut db: Connect pub async fn delete_image(mut db: Connection, cookies: &CookieJar<'_>, uuid: String) -> String { let token = cookies.get_private("token"); match token { - Some(t) => { - match User::get_by_token(&mut db, t).await { - Some(user) => { - match Image::is_owned_by(&mut db, &uuid, &user.username).await { - Ok(b) => { - match b { - true => { - match Image::delete(&mut db, &uuid).await.status { - StatusTypes::Success => { - match fs::remove_file(format!("/srv/images/{uuid}.png")) { - Ok(_) => { - "deleted!".to_string() - }, - Err(why) => { - eprintln!("{why:?}"); - "Image deleted from database but not filesystem".to_string() - } - } - }, - StatusTypes::Faliure => "Couldn't delete image".to_string() - } - }, - false => "You don't own that image".to_string() + Some(t) => match User::get_by_token(&mut db, t).await { + Some(user) => match Image::is_owned_by(&mut db, &uuid, &user.username).await { + Ok(b) => match b { + true => match Image::delete(&mut db, &uuid).await { + Ok(_) => match fs::remove_file(format!("/srv/images/{uuid}.png")) { + Ok(_) => "deleted!".to_string(), + Err(why) => { + eprintln!("{why:?}"); + "Image deleted from database but not filesystem".to_string() } - }, - Err(_) => "Couldn't get image".to_string() - } + }, + Err(_) => "Couldn't delete image".to_string(), + }, + false => "You don't own that image".to_string(), }, - None => "Invalid login token".to_string() - } + Err(_) => "Couldn't get image".to_string(), + }, + None => "Invalid login token".to_string(), }, - None => { - "Not logged in".to_string() - } + None => "Not logged in".to_string(), } } #[get("/images/by-user/")] -pub async fn get_images_by_username(mut db: Connection, cookies: &CookieJar<'_>, username: String) -> Result>, String> { +pub async fn get_images_by_username( + mut db: Connection, + cookies: &CookieJar<'_>, + username: String, +) -> Result>, String> { let token = cookies.get_private("token"); match token { - Some(t) => { - match User::get_by_token(&mut db, t).await { - Some(user) => { - match user.admin || user.username == username { - true => { - match Image::get_by_username(&mut db, &username).await { - Ok(images) => { - Ok(Json::from(images.into_iter().map(|i| i.uuid).collect::>())) - }, - Err(why) => { - eprintln!("{why:?}"); - Err("Couldn't get that user's images".to_string()) - } - } - - }, - false => Err("You don't have permission to do this".to_string()) + Some(t) => match User::get_by_token(&mut db, t).await { + Some(user) => match user.admin || user.username == username { + true => match Image::get_by_username(&mut db, &username).await { + Ok(images) => Ok(Json::from( + images.into_iter().map(|i| i.uuid).collect::>(), + )), + Err(why) => { + eprintln!("{why:?}"); + Err("Couldn't get that user's images".to_string()) } }, - None => Err("Invalid login token".to_string()) - } + false => Err("You don't have permission to do this".to_string()), + }, + None => Err("Invalid login token".to_string()), }, - None => { - Err("Not logged in".to_string()) - } + None => Err("Not logged in".to_string()), } } @@ -491,33 +474,44 @@ async fn migrate(rocket: Rocket) -> Rocket { )) .await; - let _ = conn.execute(sqlx::query( - "ALTER TABLE users + let _ = conn + .execute(sqlx::query( + "ALTER TABLE users ADD COLUMN IF NOT EXISTS admin BOOLEAN NOT NULL DEFAULT FALSE, ADD COLUMN IF NOT EXISTS make_posts BOOLEAN NOT NULL DEFAULT FALSE, ADD COLUMN IF NOT EXISTS comment BOOLEAN NOT NULL DEFAULT TRUE - " - )).await; + ", + )) + .await; - match conn.fetch_one(sqlx::query("SELECT * from users WHERE username = $1").bind(std::env::var("ADMIN_USERNAME").expect("make ADMIN_USERNAME env var"))).await { + match conn + .fetch_one( + sqlx::query("SELECT * from users WHERE username = $1") + .bind(std::env::var("ADMIN_USERNAME").expect("make ADMIN_USERNAME env var")), + ) + .await + { Ok(_) => (), Err(_) => { // yes, User::create() exists. no, conn isn't the right type. no, i won't even attempt // to fix it. yes, this works just as well as that. fuck you. let username = std::env::var("ADMIN_USERNAME").expect("make ADMIN_USERNAME env var"); - let password = sha256::digest(std::env::var("ADMIN_PASSWORD").expect("make ADMIN_PASSWORD env var")); - - conn.execute(sqlx::query( + let password = sha256::digest( + std::env::var("ADMIN_PASSWORD").expect("make ADMIN_PASSWORD env var"), + ); + + conn.execute( + sqlx::query( r#" INSERT INTO users (username, password, admin) VALUES ($1, $2, true); "#, ) .bind(username) - .bind(password) + .bind(password), ) - .await - .expect("couldn't create admin user"); + .await + .expect("couldn't create admin user"); } }; @@ -532,26 +526,29 @@ async fn migrate(rocket: Rocket) -> Rocket { )) .await; - let _ = conn.execute(sqlx::query( - "ALTER TABLE images - DROP COLUMN IF EXISTS image" - )) - .await; + let _ = conn + .execute(sqlx::query( + "ALTER TABLE images + DROP COLUMN IF EXISTS image", + )) + .await; rocket } #[rocket::main] async fn main() { -use rocket::http::Method; + use rocket::http::Method; use rocket_cors::{AllowedOrigins, CorsOptions}; - let cors = CorsOptions::default().allowed_origins(AllowedOrigins::all()).allowed_methods( - vec![Method::Get, Method::Post, Method::Patch] - .into_iter() - .map(From::from) - .collect(), - ) - .allow_credentials(true); + let cors = CorsOptions::default() + .allowed_origins(AllowedOrigins::all()) + .allowed_methods( + vec![Method::Get, Method::Post, Method::Patch] + .into_iter() + .map(From::from) + .collect(), + ) + .allow_credentials(true); let _rocket = rocket::build() .attach(Db::init()) @@ -559,7 +556,21 @@ use rocket::http::Method; .attach(cors.to_cors().unwrap()) .mount( "/", - routes![login_page, login, logout, createuser, createuser_page, account, adminpanel, toggleperms, get_image, upload, uploadimage, delete_image, get_images_by_username], + routes![ + login_page, + login, + logout, + createuser, + createuser_page, + account, + adminpanel, + toggleperms, + get_image, + upload, + uploadimage, + delete_image, + get_images_by_username + ], ) .mount("/api", routes![api_perms]) .mount("/css", FileServer::from("/srv/web/css")) diff --git a/src/tables.rs b/src/tables.rs index 7ea43f3..900c866 100644 --- a/src/tables.rs +++ b/src/tables.rs @@ -10,8 +10,6 @@ use rocket_db_pools::{ }; use sqlx::FromRow; -use crate::{Status, StatusTypes}; - #[derive(Database)] #[database("fossil_postgres")] pub struct Db(PgPool); @@ -70,21 +68,17 @@ pub struct User { } impl User { - pub async fn create(db: &mut Connection, username: &String, password: &String) -> Status { + pub async fn create(db: &mut Connection, username: &String, password: &String) -> Result { match Regex::new(r"[^A-Za-z0-9_]") { Ok(r) => { match r.captures(username) { - Some(_) => Status { - status: StatusTypes::Faliure, - data:"The username contains invalid characters. Only letters, numbers, and underscores are allowed.".to_string() - }, + Some(_) => + Err("The username contains invalid characters. Only letters, numbers, and underscores are allowed.".to_string()) + , None => { if username.len().gt(&32) || username.len().lt(&3) { // i can Never // remember which symbol is which. this works better for me. - Status { - status: StatusTypes::Faliure, - data: "Please choose a username between 3 and 32 characters.".to_string() - } + Err("Please choose a username between 3 and 32 characters.".to_string()) } else { match db .execute( @@ -98,16 +92,12 @@ impl User { .bind(sha256::digest(password)), ).await { - Ok(_) => Status { - status: StatusTypes::Success, - data: "Created user.".to_string() - }, + Ok(_) => + Ok("Created user.".to_string()) + , Err(why) => { eprintln!("Couldn't create database entry: {why:?}"); - Status { - status: StatusTypes::Faliure, - data: "Failed to create user.".to_string() - } + Err("Failed to create user.".to_string()) } } } @@ -116,10 +106,7 @@ impl User { } Err(why) => { eprintln!("Couldn't compile name regex: {why:?}"); - Status { - status: StatusTypes::Faliure, - data: "Couldn't compile name regex.".to_string() - } + Err("Couldn't compile name regex.".to_string()) } } } @@ -239,7 +226,7 @@ pub struct Image { } impl Image { - pub async fn create(db: &mut Connection, uuid: &String, user: User) -> Status { + pub async fn create(db: &mut Connection, uuid: &String, user: User) -> Result<(), String> { match db .execute( sqlx::query( @@ -253,16 +240,12 @@ impl Image { ) .await { - Ok(_) => Status { - status: StatusTypes::Success, - data: "useless field !".to_string() - }, + Ok(_) => + Ok(()) + , Err(why) => { eprintln!("Couldn't create database entry: {why:?}"); - Status { - status: StatusTypes::Faliure, - data: "Couldn't create image.".to_string() - } + Err("Couldn't create image.".to_string()) } } } @@ -309,21 +292,15 @@ impl Image { Err(_) => Err("Couldn't get image.".to_string()), } } - pub async fn delete(db: &mut Connection, uuid: &String) -> Status { + pub async fn delete(db: &mut Connection, uuid: &String) -> Result<(), ()> { match db.execute(sqlx::query("DELETE FROM images WHERE uuid = $1").bind(uuid)).await { Ok(rows_gone) => { eprintln!("Deleted {rows_gone:?} rows."); - Status { - status: StatusTypes::Success, - data: "Successfully deleted the image.".to_string() - } + Ok(()) }, Err(why) => { eprintln!("Couldn't remove database entry: {why:?}"); - Status { - status: StatusTypes::Faliure, - data: "Couldn't delete the image.".to_string() - } + Err(()) } } }