Move routes
This commit is contained in:
parent
899240bdbc
commit
868df22ea7
123
src/main.rs
123
src/main.rs
@ -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())
|
||||||
|
115
src/user.rs
115
src/user.rs
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user