api/src/accounts.rs

119 lines
3.7 KiB
Rust

use anyhow::{Error, Result};
use pbkdf2::{
password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
Pbkdf2,
};
use sqlx::{mysql::MySqlPool, types::chrono as sqlx_chrono};
pub struct Account {
pub id: u64,
pub username: String,
pub email: String,
pub salt: String,
pub password: String,
pub joined: sqlx_chrono::NaiveDateTime,
pub verified: bool,
}
impl Account {
/// This doesn't check if an account with that name is already existing!
pub async fn new(
pool: &MySqlPool,
username: &String,
email: &String,
password: &String,
) -> Result<Self> {
let salt = SaltString::generate(&mut OsRng);
let hash = Pbkdf2
.hash_password(password.as_bytes(), &salt)
.map_err(|_| anyhow::Error::msg("Failed to hash the password"))?
.to_string();
let joined = sqlx_chrono::Utc::now().naive_utc();
sqlx::query!(
r#"INSERT INTO Accounts (username, email, salt, password, joined, verified) VALUES (?, ?, ?, ?, ?, false);"#,
username,
email,
salt.to_string(),
hash,
joined,
)
.execute(pool)
.await?;
match Account::from_username(pool, &username).await? {
Some(a) => Ok(a),
None => Err(Error::msg(
"The just created account can't be found in the database!",
)),
}
}
pub async fn from_username(pool: &MySqlPool, username: &String) -> Result<Option<Self>> {
match sqlx::query!(r#"SELECT * FROM Accounts WHERE username = ?;"#, username)
.fetch_one(pool)
.await
{
Ok(row) => {
let account = Account {
id: row.id,
username: row.username,
email: row.email,
salt: row.salt,
password: row.password,
joined: row.joined,
verified: row.verified != 0,
};
Ok(Some(account))
}
Err(sqlx::Error::RowNotFound) => Ok(None),
Err(e) => Err(Error::new(e)),
}
}
pub async fn from_id(pool: &MySqlPool, id: u64) -> Result<Option<Self>> {
match sqlx::query!(r#"SELECT * FROM Accounts WHERE id = ?;"#, id)
.fetch_one(pool)
.await
{
Ok(row) => {
let account = Account {
id: row.id,
username: row.username,
email: row.email,
salt: row.salt,
password: row.password,
joined: row.joined,
verified: row.verified != 0,
};
Ok(Some(account))
}
Err(sqlx::Error::RowNotFound) => Ok(None),
Err(e) => Err(Error::new(e)),
}
}
pub async fn from_email(pool: &MySqlPool, email: &String) -> Result<Option<Self>> {
match sqlx::query!(r#"SELECT * FROM Accounts WHERE email = ?;"#, email)
.fetch_one(pool)
.await
{
Ok(row) => {
let account = Account {
id: row.id,
username: row.username,
email: row.email,
salt: row.salt,
password: row.password,
joined: row.joined,
verified: row.verified != 0,
};
Ok(Some(account))
}
Err(sqlx::Error::RowNotFound) => Ok(None),
Err(e) => Err(Error::new(e)),
}
}
}