138 lines
3.8 KiB
Rust
138 lines
3.8 KiB
Rust
use crate::routes::{Post, User};
|
|
use argon2::{
|
|
password_hash::{rand_core::OsRng, SaltString},
|
|
Argon2, PasswordHasher, PasswordVerifier,
|
|
};
|
|
use log::{info, warn};
|
|
use sqlx::SqlitePool;
|
|
|
|
// Gets the latest posts from the database, ordered by created_at
|
|
pub async fn db_get_latest_posts(pool: &SqlitePool, limit: i64, offset: i64) -> Vec<Post> {
|
|
sqlx::query_as!(
|
|
Post,
|
|
"SELECT * FROM posts ORDER BY created_at DESC LIMIT ? OFFSET ?",
|
|
limit,
|
|
offset
|
|
)
|
|
.fetch_all(pool)
|
|
.await
|
|
.unwrap()
|
|
}
|
|
|
|
// Gets the post with id from the database
|
|
pub async fn db_get_post(id: i64, pool: &SqlitePool) -> Option<Post> {
|
|
sqlx::query_as!(Post, "SELECT * FROM posts WHERE id = ?", id)
|
|
.fetch_one(pool)
|
|
.await
|
|
.ok()
|
|
}
|
|
|
|
// Inserts a new post to the database
|
|
pub async fn db_new_post(userid: i64, content: &str, pool: &SqlitePool) -> Option<Post> {
|
|
info!("User with id {} submitted a post", userid);
|
|
|
|
let insert_query = sqlx::query!(
|
|
"INSERT INTO posts (user_id, content) VALUES (?, ?)",
|
|
userid,
|
|
content
|
|
)
|
|
.execute(pool)
|
|
.await;
|
|
|
|
if insert_query.is_err() {
|
|
let s = insert_query.err().unwrap();
|
|
warn!("Error inserting post into database: {}", s);
|
|
return None;
|
|
}
|
|
|
|
// Dips into the database to get the post we just inserted
|
|
let post = sqlx::query_as!(
|
|
Post,
|
|
"SELECT * FROM posts WHERE id = (SELECT MAX(id) FROM posts)"
|
|
)
|
|
.fetch_one(pool)
|
|
.await
|
|
.ok()?;
|
|
|
|
Some(post)
|
|
}
|
|
|
|
pub async fn db_user_exists(username: String, pool: &SqlitePool) -> bool {
|
|
let exists = sqlx::query!("SELECT username FROM users WHERE username = ?", username)
|
|
.fetch_one(pool)
|
|
.await
|
|
.ok()
|
|
.map(|row| row.username);
|
|
|
|
exists.is_some()
|
|
}
|
|
|
|
pub async fn db_user_login(username: String, password: String, pool: &SqlitePool) -> Option<User> {
|
|
let username = username.clone();
|
|
let user = sqlx::query_as!(User, "SELECT * FROM users WHERE username = ?", username)
|
|
.fetch_one(pool)
|
|
.await
|
|
.ok()?;
|
|
|
|
let phc_password = user.password.clone();
|
|
let phc_password = match argon2::PasswordHash::new(&phc_password) {
|
|
Ok(phc_password) => phc_password,
|
|
Err(_) => {
|
|
warn!(
|
|
"Invalid hash for user {} fetched from database (not a valid PHC string)",
|
|
username
|
|
);
|
|
return None;
|
|
}
|
|
};
|
|
|
|
let argon2 = Argon2::default();
|
|
let password = password.as_bytes();
|
|
|
|
match argon2.verify_password(password, &phc_password) {
|
|
Ok(_) => Some(user),
|
|
Err(_) => None,
|
|
}
|
|
}
|
|
|
|
pub async fn db_new_user(username: String, password: String, pool: &SqlitePool) -> Option<User> {
|
|
// First check if the user already exists
|
|
match db_user_exists(username.clone(), pool).await {
|
|
true => {
|
|
warn!("User \"{}\" already exists", username);
|
|
return None;
|
|
}
|
|
false => {}
|
|
}
|
|
|
|
// Unwrapping here because if this fails, we have a serious problem
|
|
let phc_hash = Argon2::default()
|
|
.hash_password(password.as_bytes(), &SaltString::generate(&mut OsRng))
|
|
.unwrap()
|
|
.to_string();
|
|
|
|
// Insert our new user into the database
|
|
let insert_query = sqlx::query!(
|
|
"INSERT INTO users (username, password) VALUES (?, ?)",
|
|
username,
|
|
phc_hash
|
|
)
|
|
.execute(pool)
|
|
.await;
|
|
|
|
match insert_query {
|
|
Ok(_) => {
|
|
info!("User: {} registered", username);
|
|
let user = sqlx::query_as!(User, "SELECT * FROM users WHERE username = ?", username)
|
|
.fetch_one(pool)
|
|
.await
|
|
.ok()?;
|
|
|
|
Some(user)
|
|
}
|
|
Err(e) => {
|
|
warn!("Error inserting user into database: {}", e);
|
|
return None;
|
|
}
|
|
}
|
|
}
|