122 lines
4.1 KiB
Rust
122 lines
4.1 KiB
Rust
use sqlx::{FromRow, Row};
|
|
use crate::redis_cache::{RedisCache, Cache}; // Import RedisCache and Cache Trait
|
|
use serde::{Serialize, Deserialize};
|
|
use std::sync::Arc;
|
|
use tokio::sync::Mutex;
|
|
|
|
#[derive(Debug, FromRow, Serialize, Deserialize)]
|
|
pub struct User {
|
|
pub id: i32,
|
|
pub username: String,
|
|
pub email: String,
|
|
pub hashed_password: String,
|
|
pub roles: Vec<String>,
|
|
pub created_at: chrono::NaiveDateTime,
|
|
pub updated_at: chrono::NaiveDateTime,
|
|
}
|
|
|
|
pub struct UserRepository {
|
|
pool: sqlx::PgPool,
|
|
cache: Arc<Mutex<RedisCache>>, // Thread-safe RedisCache
|
|
}
|
|
|
|
impl UserRepository {
|
|
pub fn new(pool: sqlx::PgPool, cache: Arc<Mutex<RedisCache>>) -> Self {
|
|
Self { pool, cache }
|
|
}
|
|
|
|
pub async fn get_user_by_id(&self, user_id: i32) -> Result<User, sqlx::Error> {
|
|
let cache_key = format!("user:{}", user_id);
|
|
|
|
if let Some(user) = self.cache.lock().await.get::<User>(&cache_key).await.map_err(|_| sqlx::Error::RowNotFound)? {
|
|
return Ok(user);
|
|
}
|
|
|
|
let user = sqlx::query_as::<_, User>(
|
|
"SELECT id, username, email, hashed_password, roles, created_at, updated_at FROM users WHERE id = $1",
|
|
)
|
|
.bind(user_id)
|
|
.fetch_one(&self.pool)
|
|
.await?;
|
|
|
|
self.cache.lock().await.set(&cache_key, &user, 300).await.map_err(|_| sqlx::Error::RowNotFound)?;
|
|
Ok(user)
|
|
}
|
|
|
|
pub async fn get_user_by_username(&self, username: &str) -> Result<User, sqlx::Error> {
|
|
let cache_key = format!("user:username:{}", username);
|
|
|
|
if let Some(user) = self.cache.lock().await.get::<User>(&cache_key).await.map_err(|_| sqlx::Error::RowNotFound)? {
|
|
return Ok(user);
|
|
}
|
|
|
|
let user = sqlx::query_as::<_, User>(
|
|
"SELECT id, username, email, hashed_password, roles, created_at, updated_at FROM users WHERE username = $1",
|
|
)
|
|
.bind(username)
|
|
.fetch_one(&self.pool)
|
|
.await?;
|
|
|
|
self.cache.lock().await.set(&cache_key, &user, 300).await.map_err(|_| sqlx::Error::RowNotFound)?;
|
|
Ok(user)
|
|
}
|
|
|
|
pub async fn get_user_by_email(&self, email: &str) -> Result<User, sqlx::Error> {
|
|
let cache_key = format!("user:email:{}", email);
|
|
|
|
if let Some(user) = self.cache.lock().await.get::<User>(&cache_key).await.map_err(|_| sqlx::Error::RowNotFound)? {
|
|
return Ok(user);
|
|
}
|
|
|
|
let user = sqlx::query_as::<_, User>(
|
|
"SELECT id, username, email, hashed_password, roles, created_at, updated_at FROM users WHERE email = $1",
|
|
)
|
|
.bind(email)
|
|
.fetch_one(&self.pool)
|
|
.await?;
|
|
|
|
self.cache.lock().await.set(&cache_key, &user, 300).await.map_err(|_| sqlx::Error::RowNotFound)?;
|
|
Ok(user)
|
|
}
|
|
|
|
pub async fn create_user(&self, username: &str, email: &str, hashed_password: &str, roles: &[String]) -> Result<i32, sqlx::Error> {
|
|
let row = sqlx::query(
|
|
"INSERT INTO users (username, email, hashed_password, roles, created_at, updated_at) \
|
|
VALUES ($1, $2, $3, $4, NOW(), NOW()) RETURNING id",
|
|
)
|
|
.bind(username)
|
|
.bind(email)
|
|
.bind(hashed_password)
|
|
.bind(roles)
|
|
.fetch_one(&self.pool)
|
|
.await?;
|
|
|
|
Ok(row.get("id"))
|
|
}
|
|
|
|
pub async fn update_user_email(&self, user_id: i32, new_email: &str) -> Result<(), sqlx::Error> {
|
|
sqlx::query(
|
|
"UPDATE users SET email = $1, updated_at = NOW() WHERE id = $2",
|
|
)
|
|
.bind(new_email)
|
|
.bind(user_id)
|
|
.execute(&self.pool)
|
|
.await?;
|
|
|
|
let cache_key = format!("user:{}", user_id);
|
|
self.cache.lock().await.delete(&cache_key).await.map_err(|_| sqlx::Error::RowNotFound)?;
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn delete_user(&self, user_id: i32) -> Result<(), sqlx::Error> {
|
|
sqlx::query("DELETE FROM users WHERE id = $1")
|
|
.bind(user_id)
|
|
.execute(&self.pool)
|
|
.await?;
|
|
|
|
let cache_key = format!("user:{}", user_id);
|
|
self.cache.lock().await.delete(&cache_key).await.map_err(|_| sqlx::Error::RowNotFound)?;
|
|
Ok(())
|
|
}
|
|
}
|