use crate::db::DbConfig;
use actix_web::HttpRequest;
use argon2::password_hash::SaltString;
use argon2::password_hash::rand_core::OsRng;
use argon2::{Argon2, PasswordHash, PasswordHasher, PasswordVerifier};
use uuid::Uuid;
pub fn hash_password(password: &str) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default();
let hash = argon2
.hash_password(password.as_bytes(), &salt)
.map_err(|e| format!("Failed to hash password: {e}"))?;
Ok(hash.to_string())
}
pub fn verify_password(password: &str, hash: &str) -> bool {
let parsed = match PasswordHash::new(hash) {
Ok(h) => h,
Err(_) => return false,
};
Argon2::default()
.verify_password(password.as_bytes(), &parsed)
.is_ok()
}
pub async fn get_current_author(req: &HttpRequest, db: &DbConfig) -> Option<(Uuid, String)> {
let cookie = req.cookie("session_token")?;
let token = Uuid::parse_str(cookie.value()).ok()?;
let client = db.connect().await.ok()?;
let row = client
.query_opt(
"SELECT s.author_id, a.display_name FROM sessions s JOIN authors a ON s.author_id = a.id WHERE s.token = $1",
&[&token],
)
.await
.ok()??;
let author_id: Uuid = row.get(0);
let display_name: String = row.get(1);
Some((author_id, display_name))
}
pub async fn create_session(
db: &DbConfig,
author_id: Uuid,
) -> Result<Uuid, Box<dyn std::error::Error + Send + Sync>> {
let client = db.connect().await?;
let token = Uuid::new_v4();
client
.execute(
"INSERT INTO sessions (token, author_id) VALUES ($1, $2)",
&[&token, &author_id],
)
.await?;
Ok(token)
}
pub async fn delete_session(
db: &DbConfig,
token: Uuid,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let client = db.connect().await?;
client
.execute("DELETE FROM sessions WHERE token = $1", &[&token])
.await?;
Ok(())
}