Move routes

This commit is contained in:
Greg Shuflin 2025-02-01 20:11:02 -08:00
parent 899240bdbc
commit 868df22ea7
2 changed files with 123 additions and 115 deletions

View File

@ -4,13 +4,9 @@ extern crate rocket;
mod user; mod user;
use rocket::fs::FileServer; use rocket::fs::FileServer;
use rocket::http::Status;
use rocket::serde::{json::Json, Serialize}; use rocket::serde::{json::Json, Serialize};
use rocket_db_pools::{sqlx, Connection, Database}; use rocket_db_pools::{sqlx, Database};
use rocket_dyn_templates::{context, Template}; use rocket_dyn_templates::{context, Template};
use uuid::Uuid;
use bcrypt;
use user::{User, NewUser};
#[derive(Database)] #[derive(Database)]
#[database("rss_data")] #[database("rss_data")]
@ -36,114 +32,6 @@ fn index() -> Template {
Template::render("index", context! {}) Template::render("index", context! {})
} }
#[post("/users", data = "<new_user>")]
async fn create_user(
mut db: Connection<Db>,
new_user: Json<NewUser>,
) -> Result<Json<User>, Status> {
let new_user = new_user.into_inner();
// Hash the password - we'll use bcrypt
let password_hash = bcrypt::hash(new_user.password.as_bytes(), bcrypt::DEFAULT_COST)
.map_err(|_| Status::InternalServerError)?;
let user = User::new(
new_user.username,
password_hash,
new_user.email,
new_user.display_name
);
let query = sqlx::query(
"INSERT INTO users (id, username, password_hash, email, display_name, created_at) VALUES (?1, ?2, ?3, ?4, ?5, ?6)"
)
.bind(user.id.to_string())
.bind(user.username.as_str())
.bind(user.password_hash.as_str())
.bind(user.email.as_ref())
.bind(user.display_name.as_ref())
.bind(user.created_at.to_rfc3339())
.execute(&mut **db).await;
match query {
Ok(_) => Ok(Json(user)),
Err(e) => {
eprintln!("Database error: {}", e);
match e {
sqlx::Error::Database(db_err) if db_err.is_unique_violation() => {
Err(Status::Conflict)
}
_ => Err(Status::InternalServerError),
}
}
}
}
#[get("/users")]
async fn get_users(mut db: Connection<Db>) -> Result<Json<Vec<User>>, Status> {
let query = sqlx::query!(
r#"
SELECT
id as "id: String",
username,
password_hash,
email,
display_name,
created_at as "created_at: chrono::DateTime<chrono::Utc>"
FROM users
"#
)
.fetch_all(&mut **db)
.await
.map_err(|e| {
eprintln!("Database error: {}", e);
Status::InternalServerError
})?;
// Convert the strings to UUIDs
let users = query
.into_iter()
.map(|row| User {
id: Uuid::parse_str(&row.id).unwrap(),
username: row.username,
password_hash: row.password_hash,
email: row.email,
display_name: row.display_name,
created_at: row.created_at,
})
.collect::<Vec<_>>();
Ok(Json(users))
}
#[delete("/users/<user_id>")]
async fn delete_user(mut db: Connection<Db>, user_id: &str) -> Status {
// Validate UUID format
let uuid = match Uuid::parse_str(user_id) {
Ok(uuid) => uuid,
Err(_) => return Status::BadRequest,
};
let query = sqlx::query("DELETE FROM users WHERE id = ?")
.bind(uuid.to_string())
.execute(&mut **db)
.await;
match query {
Ok(result) => {
if result.rows_affected() > 0 {
Status::NoContent
} else {
Status::NotFound
}
}
Err(e) => {
eprintln!("Database error: {}", e);
Status::InternalServerError
}
}
}
#[get("/login")] #[get("/login")]
fn login() -> Template { fn login() -> Template {
Template::render("login", context! {}) Template::render("login", context! {})
@ -154,7 +42,14 @@ fn rocket() -> _ {
rocket::build() rocket::build()
.mount( .mount(
"/", "/",
routes![index, message, create_user, get_users, delete_user, login], routes![
index,
message,
login,
user::create_user,
user::get_users,
user::delete_user
],
) )
.mount("/static", FileServer::from("static")) .mount("/static", FileServer::from("static"))
.attach(Template::fairing()) .attach(Template::fairing())

View File

@ -1,6 +1,11 @@
use chrono; use chrono;
use rocket::serde::{Deserialize, Serialize}; use rocket::http::Status;
use rocket::serde::{json::Json, Deserialize, Serialize};
use rocket_db_pools::Connection;
use uuid::Uuid; use uuid::Uuid;
use bcrypt;
use crate::Db;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(crate = "rocket::serde")] #[serde(crate = "rocket::serde")]
@ -34,3 +39,111 @@ pub struct NewUser {
pub email: Option<String>, pub email: Option<String>,
pub display_name: Option<String>, pub display_name: Option<String>,
} }
#[post("/users", data = "<new_user>")]
pub async fn create_user(
mut db: Connection<Db>,
new_user: Json<NewUser>,
) -> Result<Json<User>, Status> {
let new_user = new_user.into_inner();
// Hash the password - we'll use bcrypt
let password_hash = bcrypt::hash(new_user.password.as_bytes(), bcrypt::DEFAULT_COST)
.map_err(|_| Status::InternalServerError)?;
let user = User::new(
new_user.username,
password_hash,
new_user.email,
new_user.display_name
);
let query = sqlx::query(
"INSERT INTO users (id, username, password_hash, email, display_name, created_at) VALUES (?1, ?2, ?3, ?4, ?5, ?6)"
)
.bind(user.id.to_string())
.bind(user.username.as_str())
.bind(user.password_hash.as_str())
.bind(user.email.as_ref())
.bind(user.display_name.as_ref())
.bind(user.created_at.to_rfc3339())
.execute(&mut **db).await;
match query {
Ok(_) => Ok(Json(user)),
Err(e) => {
eprintln!("Database error: {}", e);
match e {
sqlx::Error::Database(db_err) if db_err.is_unique_violation() => {
Err(Status::Conflict)
}
_ => Err(Status::InternalServerError),
}
}
}
}
#[get("/users")]
pub async fn get_users(mut db: Connection<Db>) -> Result<Json<Vec<User>>, Status> {
let query = sqlx::query!(
r#"
SELECT
id as "id: String",
username,
password_hash,
email,
display_name,
created_at as "created_at: chrono::DateTime<chrono::Utc>"
FROM users
"#
)
.fetch_all(&mut **db)
.await
.map_err(|e| {
eprintln!("Database error: {}", e);
Status::InternalServerError
})?;
// Convert the strings to UUIDs
let users = query
.into_iter()
.map(|row| User {
id: Uuid::parse_str(&row.id).unwrap(),
username: row.username,
password_hash: row.password_hash,
email: row.email,
display_name: row.display_name,
created_at: row.created_at,
})
.collect::<Vec<_>>();
Ok(Json(users))
}
#[delete("/users/<user_id>")]
pub async fn delete_user(mut db: Connection<Db>, user_id: &str) -> Status {
// Validate UUID format
let uuid = match Uuid::parse_str(user_id) {
Ok(uuid) => uuid,
Err(_) => return Status::BadRequest,
};
let query = sqlx::query("DELETE FROM users WHERE id = ?")
.bind(uuid.to_string())
.execute(&mut **db)
.await;
match query {
Ok(result) => {
if result.rows_affected() > 0 {
Status::NoContent
} else {
Status::NotFound
}
}
Err(e) => {
eprintln!("Database error: {}", e);
Status::InternalServerError
}
}
}