use sqlx::query! macro

This commit is contained in:
Greg Shuflin 2025-02-05 02:40:23 -08:00
parent c89ee29d4d
commit 3cb122306a
4 changed files with 106 additions and 62 deletions

View File

@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "DELETE FROM users WHERE id = ?",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "73ffdf5be39aa5c4c160c2f77d6634a6970eeb4e1d3395f045ded747f0ce9d2a"
}

View File

@ -0,0 +1,20 @@
{
"db_name": "SQLite",
"query": "\n SELECT COUNT(*) as \"count!: i32\"\n FROM users\n ",
"describe": {
"columns": [
{
"name": "count!: i32",
"ordinal": 0,
"type_info": "Int"
}
],
"parameters": {
"Right": 0
},
"nullable": [
false
]
},
"hash": "b668f6787e6425e15414bb745bce55788deb3a996b8e51d651a9e4a07b1483b4"
}

View File

@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n INSERT INTO users (id, username, password_hash, email, display_name, created_at, admin) \n VALUES (?, ?, ?, ?, ?, ?, ?)\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 7
},
"nullable": []
},
"hash": "bf1cc32644e82c80cd6ab4d8fec843384da66c0cac7d6f8f929470c72284d8c8"
}

View File

@ -45,19 +45,22 @@ impl User {
where where
E: sqlx::Executor<'a, Database = sqlx::Sqlite>, E: sqlx::Executor<'a, Database = sqlx::Sqlite>,
{ {
sqlx::query( let id_str = self.id.to_string();
let created_at_str = self.created_at.to_rfc3339();
sqlx::query!(
r#" r#"
INSERT INTO users (id, username, password_hash, email, display_name, created_at, admin) INSERT INTO users (id, username, password_hash, email, display_name, created_at, admin)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7) VALUES (?, ?, ?, ?, ?, ?, ?)
"#, "#,
id_str,
self.username,
self.password_hash,
self.email,
self.display_name,
created_at_str,
self.admin
) )
.bind(self.id.to_string())
.bind(self.username.clone())
.bind(self.password_hash.clone())
.bind(self.email.clone())
.bind(self.display_name.clone())
.bind(self.created_at.to_rfc3339())
.bind(self.admin)
.execute(executor) .execute(executor)
.await?; .await?;
@ -173,12 +176,15 @@ pub async fn delete_user(mut db: Connection<Db>, user_id: &str) -> Status {
Err(_) => return Status::BadRequest, Err(_) => return Status::BadRequest,
}; };
let query = sqlx::query("DELETE FROM users WHERE id = ?") let id_str = uuid.to_string();
.bind(uuid.to_string()) let result = sqlx::query!(
.execute(&mut **db) "DELETE FROM users WHERE id = ?",
.await; id_str
)
.execute(&mut **db)
.await;
match query { match result {
Ok(result) => { Ok(result) => {
if result.rows_affected() > 0 { if result.rows_affected() > 0 {
Status::NoContent Status::NoContent
@ -201,6 +207,7 @@ pub async fn login(
sessions: &State<SessionStore>, sessions: &State<SessionStore>,
) -> Result<Json<LoginResponse>, Status> { ) -> Result<Json<LoginResponse>, Status> {
let creds = credentials.into_inner(); let creds = credentials.into_inner();
let username = creds.username.clone();
// Find user by username // Find user by username
let user = sqlx::query!( let user = sqlx::query!(
@ -212,7 +219,7 @@ pub async fn login(
FROM users FROM users
WHERE username = ? WHERE username = ?
"#, "#,
creds.username username
) )
.fetch_optional(&mut **db) .fetch_optional(&mut **db)
.await .await
@ -300,19 +307,21 @@ impl<'r> rocket::request::FromRequest<'r> for AuthenticatedUser {
#[get("/setup")] #[get("/setup")]
pub async fn setup_page(mut db: Connection<Db>) -> Result<Template, Status> { pub async fn setup_page(mut db: Connection<Db>) -> Result<Template, Status> {
// Check if any users exist // Check if any users exist
let count = sqlx::query!("SELECT COUNT(*) as count FROM users") let count = sqlx::query!(
.fetch_one(&mut **db) r#"
.await SELECT COUNT(*) as "count!: i32"
.map_err(|_| Status::InternalServerError)? FROM users
.count; "#
)
.fetch_one(&mut **db)
.await
.map_err(|_| Status::InternalServerError)?;
if count > 0 { if count.count > 0 {
// If users exist, redirect to login return Err(Status::Forbidden);
Err(Status::SeeOther)
} else {
// Show setup page
Ok(Template::render("setup", context! {}))
} }
Ok(Template::render("setup", context! {}))
} }
#[post("/setup", data = "<new_user>")] #[post("/setup", data = "<new_user>")]
@ -320,33 +329,33 @@ pub async fn setup(
mut db: Connection<Db>, mut db: Connection<Db>,
new_user: Json<NewUser>, new_user: Json<NewUser>,
) -> Result<Status, Json<SetupError>> { ) -> Result<Status, Json<SetupError>> {
let new_user = new_user.into_inner();
// Check if any users exist // Check if any users exist
let count = sqlx::query!("SELECT COUNT(*) as count FROM users") let count = sqlx::query!(
.fetch_one(&mut **db) r#"
.await SELECT COUNT(*) as "count!: i32"
.map_err(|e| { FROM users
eprintln!("Database error: {}", e); "#
Json(SetupError { )
error: "Internal server error".to_string(), .fetch_one(&mut **db)
}) .await
})? .map_err(|_| {
.count; Json(SetupError {
error: "Database error".to_string(),
})
})?;
if count > 0 { if count.count > 0 {
return Err(Json(SetupError { return Err(Json(SetupError {
error: "Setup has already been completed".to_string(), error: "Setup already completed".to_string(),
})); }));
} }
let password = new_user.password.as_bytes(); let new_user = new_user.into_inner();
// Hash the password // Hash the password - we'll use bcrypt
let password_hash = bcrypt::hash(password, bcrypt::DEFAULT_COST).map_err(|e| { let password_hash = bcrypt::hash(new_user.password.as_bytes(), bcrypt::DEFAULT_COST).map_err(|_| {
eprintln!("Password hashing error: {}", e);
Json(SetupError { Json(SetupError {
error: "Failed to process password".to_string(), error: "Failed to hash password".to_string(),
}) })
})?; })?;
@ -356,22 +365,13 @@ pub async fn setup(
new_user.email, new_user.email,
new_user.display_name, new_user.display_name,
); );
user.admin = true; // This is an admin user user.admin = true;
match user.write_to_database(&mut **db).await { user.write_to_database(&mut **db)
Ok(_) => Ok(Status::Created), .await
Err(e) => { .map_err(|_| Json(SetupError {
error!("Database error: {}", e); error: "Failed to create user".to_string(),
match e { }))?;
sqlx::Error::Database(db_err) if db_err.is_unique_violation() => {
Err(Json(SetupError { Ok(Status::Created)
error: "Username already exists".to_string(),
}))
}
_ => Err(Json(SetupError {
error: "Failed to create user".to_string(),
})),
}
}
}
} }