diff --git a/src/api/catchers/mod.rs b/src/api/catchers/mod.rs index 4eecef0..f5457e8 100644 --- a/src/api/catchers/mod.rs +++ b/src/api/catchers/mod.rs @@ -4,8 +4,19 @@ use rocket::Rocket; use rocket_contrib::json::Json; use serde_json::Value; +#[catch(400)] +pub fn bad_request() -> status::Custom> { + status::Custom( + Status::BadRequest, + Json(json!({ + "err": "bad request", + "msg": "Error parsing JSON body" + })), + ) +} + #[catch(404)] -fn not_found() -> status::Custom> { +pub fn not_found() -> status::Custom> { status::Custom( Status::NotFound, Json(json!({ @@ -16,7 +27,7 @@ fn not_found() -> status::Custom> { } #[catch(422)] -fn unprocessable_entity() -> status::Custom> { +pub fn unprocessable_entity() -> status::Custom> { status::Custom( Status::UnprocessableEntity, Json(json!({ @@ -28,7 +39,7 @@ fn unprocessable_entity() -> status::Custom> { } #[catch(500)] -fn internal_server_error() -> status::Custom> { +pub fn internal_server_error() -> status::Custom> { status::Custom( Status::NotFound, Json(json!({ @@ -40,6 +51,7 @@ fn internal_server_error() -> status::Custom> { pub fn fuel(rocket: Rocket) -> Rocket { rocket.register(catchers![ + bad_request, not_found, unprocessable_entity, internal_server_error diff --git a/src/api/routes/mod.rs b/src/api/routes/mod.rs index 3a58f5f..c313722 100644 --- a/src/api/routes/mod.rs +++ b/src/api/routes/mod.rs @@ -4,10 +4,12 @@ use rocket::Rocket; pub mod health; pub mod paste; pub mod responder; +pub mod user; pub fn fuel(rocket: Rocket) -> Rocket { let mut rocket = rocket; rocket = health::fuel(rocket); rocket = paste::fuel(rocket); + rocket = user::fuel(rocket); rocket.attach(CORS()) } diff --git a/src/api/routes/paste.rs b/src/api/routes/paste.rs index bc19312..154c949 100644 --- a/src/api/routes/paste.rs +++ b/src/api/routes/paste.rs @@ -3,41 +3,30 @@ use std::ops::DerefMut; use diesel::result::Error; use rocket::response::status::Custom; use rocket::{ - http::{Cookie, Cookies, Status}, + http::{Cookies, Status}, response::status, Rocket, }; use rocket_contrib::json::Json; use serde_json::Value; -use crate::api::guards::db; +use crate::api::catchers::{internal_server_error, not_found}; +use crate::api::guards::db::DbConn; use crate::core::paste::{entity::Paste, service::create_paste, service::fetch_paste}; use crate::core::users::service::create_or_fetch_user; use crate::utils::phonetic_key; +use crate::utils::users::get_session_id; #[post("/", data = "")] -fn create(mut paste: Json, conn: db::DbConn, mut ck: Cookies) -> Custom> { +fn create(mut paste: Json, conn: DbConn, mut ck: Cookies) -> Custom> { // Check if frontend sent a session cookie - let user_id = match ck.get_private("session") { - Some(c) => c.value().to_string(), - None => { - let user_id = phonetic_key::get_random_id(); - ck.add_private(Cookie::new("session", user_id.clone())); - user_id - } - }; + let user_id = get_session_id(&mut ck); // Create or fetch already existing user let user = match create_or_fetch_user(user_id, &conn) { Ok(user) => user, - Err(e) => { - return status::Custom( - Status::InternalServerError, - Json(json!({ - "err": e.to_string(), - "msg": "Failed to create or fetch user" - })), - ) + Err(_) => { + return internal_server_error(); } }; @@ -56,37 +45,19 @@ fn create(mut paste: Json, conn: db::DbConn, mut ck: Cookies) -> Custom status::Custom( - Status::InternalServerError, - Json(json!({ - "err": e.to_string(), - "msg": "Failed to create paste" - })), - ), + Err(_) => internal_server_error(), } } #[get("/")] -fn fetch(id: String, conn: db::DbConn) -> Custom> { +fn fetch(id: String, conn: DbConn) -> Custom> { let paste = match fetch_paste(id, &conn) { Ok(paste) => paste, - Err(err) => { - return match err.downcast_ref::() { - Some(Error::NotFound) => Custom( - Status::NotFound, - Json(json!({ - "err": err.to_string(), - "msg": "Unable to find a paste with that ID" - })), - ), - _ => Custom( - Status::InternalServerError, - Json(json!({ - "err": err.to_string(), - "msg": "Something went wrong, try again" - })), - ), - } + Err(e) => { + return match e.downcast_ref::() { + Some(Error::NotFound) => not_found(), + _ => internal_server_error(), + }; } }; diff --git a/src/api/routes/user.rs b/src/api/routes/user.rs new file mode 100644 index 0000000..9464c38 --- /dev/null +++ b/src/api/routes/user.rs @@ -0,0 +1,44 @@ +use rocket::response::status::Custom; +use rocket::{ + http::{Status}, + response::status, + Rocket, +}; +use rocket_contrib::json::Json; +use serde_json::Value; + +use crate::api::catchers::{internal_server_error, unprocessable_entity}; +use crate::api::guards::db::DbConn; +use crate::core::users::entity::User; +use crate::core::users::service::{activate_user, create_or_fetch_user}; + +#[post("/", data = "")] +fn activate(mut user: Json, conn: DbConn) -> Custom> { + if user.username.is_none() || user.password.is_none() { + return unprocessable_entity(); + } + + // Check if frontend sent a session cookie + let user_id = user.id.clone(); + + // Create or fetch already existing user + let mut found_user = match create_or_fetch_user(user_id, &conn) { + Ok(user) => user, + Err(_) => { + return internal_server_error(); + } + }; + + // Activate user + found_user.activated = Some(true); + + let activated_user = activate_user(&mut user, &conn); + match activated_user { + Ok(u) => status::Custom(Status::Ok, Json(json!(u))), + Err(_) => internal_server_error(), + } +} + +pub fn fuel(rocket: Rocket) -> Rocket { + rocket.mount("/api/user", routes![activate]) +} diff --git a/src/core/users/postgres.rs b/src/core/users/postgres.rs index e63bab6..2ddae42 100644 --- a/src/core/users/postgres.rs +++ b/src/core/users/postgres.rs @@ -17,3 +17,10 @@ pub fn find_user(id: String, conn: &PgConnection) -> Result { let user = users::table.find(id).get_result::(conn)?; Ok(user) } + +pub fn update_user(user: &User, conn: &PgConnection) -> Result { + let user = diesel::update(users::table.find(user.id.clone())) + .set(user) + .get_result::(conn)?; + Ok(user) +} diff --git a/src/core/users/service.rs b/src/core/users/service.rs index 5fa136c..57d8108 100644 --- a/src/core/users/service.rs +++ b/src/core/users/service.rs @@ -32,3 +32,11 @@ pub fn create_or_fetch_user(id: String, conn: &PgConnection) -> Result { }; Ok(user) } + +pub fn activate_user(user: &mut User, conn: &PgConnection) -> Result { + let hashed_pass = hash(user.password.as_ref().unwrap().as_bytes(), DEFAULT_COST)?; + user.password = Some(hashed_pass); + let mut activated_user = postgres::update_user(user, conn)?; + activated_user.password = None; + Ok(activated_user) +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 6e67a6c..d4351c6 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,3 +1,4 @@ pub mod db; pub mod errors; pub mod phonetic_key; +pub mod users; diff --git a/src/utils/users.rs b/src/utils/users.rs new file mode 100644 index 0000000..241f60d --- /dev/null +++ b/src/utils/users.rs @@ -0,0 +1,14 @@ +use rocket::http::{Cookie, Cookies}; + +use crate::utils::phonetic_key; + +pub fn get_session_id(ck: &mut Cookies) -> String { + match ck.get_private("session") { + Some(c) => c.value().to_string(), + None => { + let user_id = phonetic_key::get_random_id(); + ck.add_private(Cookie::new("session", user_id.clone())); + user_id + } + } +}