diff --git a/client-solid/src/Footer.tsx b/client-solid/src/Footer.tsx deleted file mode 100644 index 376e454..0000000 --- a/client-solid/src/Footer.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { JSXElement } from "solid-js"; - -export function Footer(): JSXElement { - return ( - - ); -} diff --git a/client-solid/src/Posts.tsx b/client-solid/src/Posts.tsx index db1efad..72b22aa 100644 --- a/client-solid/src/Posts.tsx +++ b/client-solid/src/Posts.tsx @@ -15,9 +15,9 @@ export function Posts(): JSXElement { return ( - {/* + {(post): JSXElement => } - */} + ); } diff --git a/client-solid/src/Root.tsx b/client-solid/src/Root.tsx index e07d657..717d224 100644 --- a/client-solid/src/Root.tsx +++ b/client-solid/src/Root.tsx @@ -4,7 +4,6 @@ import { GlobalStateProvider } from "./GlobalState"; import { LoginModal } from "./LoginModal"; import { Navbar } from "./Navbar"; import { Primary } from "./Primary"; -import { Footer } from "./Footer"; function Root(): JSXElement { return ( @@ -14,10 +13,9 @@ function Root(): JSXElement {
-
+
-
@@ -27,7 +25,7 @@ function Root(): JSXElement { function FancyBackground(): JSXElement { return (
-
+
); } diff --git a/server/Cargo.lock b/server/Cargo.lock index bfc7374..9917c48 100755 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -1851,7 +1851,6 @@ dependencies = [ "jsonwebtoken", "lipsum", "log", - "rand", "serde", "serde_json", "sled", diff --git a/server/Cargo.toml b/server/Cargo.toml index d3046e3..ed54483 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -17,7 +17,6 @@ env_logger = "0.10.0" jsonwebtoken = "8.3.0" lipsum = "0.9.0" log = "0.4.20" -rand = "0.8.5" serde = { version = "1.0.188", features = ["derive"] } serde_json = "1.0.107" sled = { version = "0.34.7" } diff --git a/server/src/db.rs b/server/src/db.rs index 3c52b93..89c643e 100644 --- a/server/src/db.rs +++ b/server/src/db.rs @@ -6,7 +6,7 @@ use argon2::{ use log::{info, warn}; use sqlx::PgPool; -/// Gets the latest posts from the database, ordered by created_at +// Gets the latest posts from the database, ordered by created_at pub async fn db_get_latest_posts(pool: &PgPool, limit: i64, offset: i64) -> Vec { sqlx::query_as!( Post, @@ -19,7 +19,7 @@ pub async fn db_get_latest_posts(pool: &PgPool, limit: i64, offset: i64) -> Vec< .unwrap() } -/// Gets the post with id from the database +// Gets the post with id from the database pub async fn db_get_post(id: i64, pool: &PgPool) -> Option { sqlx::query_as!(Post, "SELECT * FROM posts WHERE id = $1", id) .fetch_one(pool) @@ -27,7 +27,7 @@ pub async fn db_get_post(id: i64, pool: &PgPool) -> Option { .ok() } -/// Inserts a new post to the database +// Inserts a new post to the database pub async fn db_new_post(userid: i64, content: &str, pool: &PgPool) -> Option { info!("User with id {} submitted a post", userid); @@ -57,7 +57,6 @@ pub async fn db_new_post(userid: i64, content: &str, pool: &PgPool) -> Option bool { let exists = sqlx::query!("SELECT username FROM users WHERE username = $1", username) .fetch_one(pool) @@ -68,7 +67,6 @@ pub async fn db_user_exists(username: String, pool: &PgPool) -> bool { exists.is_some() } -/// Checks if the user exists and if the password is correct pub async fn db_user_login(username: String, password: String, pool: &PgPool) -> Option { let username = username.clone(); let user = sqlx::query_as!(User, "SELECT * FROM users WHERE username = $1", username) @@ -97,7 +95,6 @@ pub async fn db_user_login(username: String, password: String, pool: &PgPool) -> } } -/// Creates a new user if the username is not already taken pub async fn db_new_user(username: String, password: String, pool: &PgPool) -> Option { // First check if the user already exists match db_user_exists(username.clone(), pool).await { diff --git a/server/src/main.rs b/server/src/main.rs index beba099..3ed8abd 100755 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -8,12 +8,10 @@ mod db; mod jwt; mod routes; mod state; -mod util; use routes::{get_posts, login, new_post, post_by_id, register}; use state::CaptchaState; use state::ServerState; -use util::hex_string; #[actix_web::main] async fn main() -> std::io::Result<()> { @@ -22,15 +20,6 @@ async fn main() -> std::io::Result<()> { let data = ServerState::new().await; let capt_db = CaptchaState::new(); - #[cfg(debug_assertions)] - { - for _ in 0..10 { - let s = hex_string(10); - info!("Adding captcha key: {}", &s); - capt_db.capthca_db.lock().unwrap().insert(s); - } - } - info!("Spinning up server on http://localhost:8080"); HttpServer::new(move || { App::new() diff --git a/server/src/routes/users.rs b/server/src/routes/users.rs index c5e7f02..5b7d999 100755 --- a/server/src/routes/users.rs +++ b/server/src/routes/users.rs @@ -5,6 +5,8 @@ use crate::ServerState; use actix_web::web::Data; use actix_web::{post, web::Json, HttpResponse, Responder, Result}; +use argon2::password_hash::rand_core::RngCore; +use argon2::password_hash::*; use biosvg::BiosvgBuilder; use log::*; use serde::{Deserialize, Serialize}; @@ -16,7 +18,7 @@ pub struct LoginData { } #[derive(Debug, Serialize, Deserialize)] -pub struct AuthResponse { +pub struct LoginResponse { username: String, token: String, } @@ -32,31 +34,10 @@ pub struct RegisterData { pub async fn register( data: Json, state: Data, - captcha_state: Data, ) -> Result { - if !captcha_state - .capthca_db - .lock() - .unwrap() - .remove(&data.captcha) - { - info!("User failed to register, captcha was wrong"); - return Ok(HttpResponse::BadRequest().json("Error")); - } - - match db_new_user(data.username.clone(), data.password.clone(), &state.pool).await { - Some(user) => { - info!("User: {} registered", &user.username); - Ok(HttpResponse::Ok().json(AuthResponse { - username: user.username.clone(), - token: token_factory(&user.username).unwrap(), - })) - } - None => { - info!("User \"{}\" already exists", data.username); - return Ok(HttpResponse::BadRequest().json("Error")); - } - } + db_new_user(data.username.clone(), data.password.clone(), &state.pool).await; + info!("User: {} registered", data.username); + Ok(HttpResponse::Ok().json("User registered")) } #[post("/login")] @@ -65,7 +46,7 @@ pub async fn login(data: Json, state: Data) -> Result { - return Ok(HttpResponse::Ok().json(AuthResponse { + return Ok(HttpResponse::Ok().json(LoginResponse { username: data.username.clone(), token: token_factory(&data.username).unwrap(), })); @@ -86,38 +67,35 @@ pub struct CaptchaResponse { /// Request a captcha from the captcha service #[post("/captcha")] pub async fn captcha_request(cstate: Data) -> Result { - unimplemented!("Captcha is currently disabled"); - return Ok(HttpResponse::InternalServerError().json("Error")); - // This might block the thread a bit too long - // let (answer, svg) = get_captcha(); + let (answer, svg) = get_captcha(); - // let id = rand_core::OsRng.next_u32() as i32; + let id = rand_core::OsRng.next_u32() as i32; - // let cresponse = CaptchaResponse { - // captcha_svg: svg.clone(), - // captcha_id: id, - // }; + let cresponse = CaptchaResponse { + captcha_svg: svg.clone(), + captcha_id: id, + }; // This is bad in about every way i can think of // It might just be better to hit the database every time, and let the database // handle rng and maybe set a trigger to delete old captchas - // match cstate.capthca_db.lock() { - // Ok(mut db) => { - // if (db.len() as i32) > 100 { - // // To prevent the database from growing too large - // // Replace with a proper LRU cache or circular buffer - // db.remove(&(id % 100)); // This is terrible - // } - // db.insert(id, answer.clone()); // We do not care about collisions - // return Ok(HttpResponse::Ok().json(cresponse)); - // } - // Err(_) => { - // // This shouldnt happen - // error!("Failed to lock captcha database"); - // return Ok(HttpResponse::InternalServerError().json("Error")); - // } - // } + match cstate.capthca_db.lock() { + Ok(mut db) => { + if (db.len() as i32) > 100 { + // To prevent the database from growing too large + // Replace with a proper LRU cache or circular buffer + db.remove(&(id % 100)); // This is terrible + } + db.insert(id, answer.clone()); // We do not care about collisions + return Ok(HttpResponse::Ok().json(cresponse)); + } + Err(_) => { + // This shouldnt happen + error!("Failed to lock captcha database"); + return Ok(HttpResponse::InternalServerError().json("Error")); + } + } } /// Returns a new captcha in the form of a tuple (answer, svg) diff --git a/server/src/state.rs b/server/src/state.rs index aafca2b..8414449 100644 --- a/server/src/state.rs +++ b/server/src/state.rs @@ -1,4 +1,4 @@ -use std::collections::BTreeSet; +use std::collections::BTreeMap; use std::sync::Arc; use std::sync::Mutex; @@ -9,14 +9,13 @@ use sqlx::PgPool; #[derive(Clone)] pub struct CaptchaState { - // pub capthca_db: Arc>>, - pub capthca_db: Arc>>, + pub capthca_db: Arc>>, } impl CaptchaState { pub fn new() -> Self { Self { - capthca_db: Arc::new(Mutex::new(BTreeSet::new())), + capthca_db: Arc::new(Mutex::new(BTreeMap::new())), } } } @@ -63,7 +62,6 @@ impl ServerState { #[cfg(debug_assertions)] async fn debug_setup(pool: &PgPool) -> Result<(), sqlx::Error> { use lipsum::lipsum; - use rand::prelude::*; use sqlx::query; use crate::db::db_new_user; @@ -71,29 +69,26 @@ async fn debug_setup(pool: &PgPool) -> Result<(), sqlx::Error> { db_new_user("user".to_string(), "pass".to_string(), pool).await; // Check if the demo post already exists - let no_posts = query!("SELECT * FROM posts WHERE id = 1",) + let posted = query!("SELECT * FROM posts WHERE id = 1",) .fetch_one(pool) .await - .ok() - .is_none(); + .ok(); // If the demo user already has a post, don't insert another one - if no_posts { - let mut rng = rand::thread_rng(); - + if !posted.is_some() { // This requires that the user with id 1 exists in the user table - for _ in 0..100 { + query!("INSERT INTO posts (user_id, content) VALUES (1, 'Hello world! The demo username is user and the password is pass.')",) + .execute(pool) + .await?; + + for _ in 0..10 { query!( "INSERT INTO posts (user_id, content) VALUES (1, $1)", - lipsum(rng.gen_range(10..100)) + lipsum(50) ) .execute(pool) .await?; } - - query!("INSERT INTO posts (user_id, content) VALUES (1, 'Hello world! The demo username is user and the password is pass.')",) - .execute(pool) - .await?; } Ok(()) diff --git a/server/src/util/mod.rs b/server/src/util/mod.rs deleted file mode 100644 index c8183a1..0000000 --- a/server/src/util/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod util; - -pub use util::*; diff --git a/server/src/util/util.rs b/server/src/util/util.rs deleted file mode 100644 index b50a703..0000000 --- a/server/src/util/util.rs +++ /dev/null @@ -1,19 +0,0 @@ -use rand::{Rng, RngCore}; - -// This will do for now -pub fn hex_string(length: usize) -> String { - let mut rng = rand::thread_rng(); - let mut bytes = vec![0u8; length]; - rng.fill(&mut bytes[..]); - bytes.iter().map(|b| format!("{:X}", b)).collect::()[..length].to_string() -} - -mod tests { - use super::*; - - #[test] - fn test_random_hex_string() { - let s = hex_string(16); - assert_eq!(s.len(), 16); - } -}