169 lines
4.5 KiB
Rust
169 lines
4.5 KiB
Rust
use crate::redis_cache::{Cache, RedisCache};
|
|
use serde::{Deserialize, Serialize};
|
|
use sqlx::Error;
|
|
use sqlx::PgPool;
|
|
use std::sync::Arc;
|
|
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub struct User {
|
|
pub id: i32,
|
|
pub username: String,
|
|
pub email: String,
|
|
pub hashed_password: String,
|
|
pub roles: Vec<String>,
|
|
}
|
|
|
|
pub struct UsersService {
|
|
pub pool: PgPool,
|
|
pub cache: Arc<RedisCache>, // Shared Redis cache
|
|
}
|
|
|
|
|
|
impl UsersService {
|
|
pub async fn create_user(
|
|
&self,
|
|
username: &str,
|
|
email: &str,
|
|
hashed_password: &str,
|
|
) -> Result<i32, Error> {
|
|
let result = sqlx::query!(
|
|
r#"
|
|
INSERT INTO users (username, email, hashed_password)
|
|
VALUES ($1, $2, $3)
|
|
RETURNING id
|
|
"#,
|
|
username,
|
|
email,
|
|
hashed_password
|
|
)
|
|
.fetch_one(&self.pool)
|
|
.await?;
|
|
|
|
Ok(result.id)
|
|
}
|
|
|
|
pub async fn get_user_by_id(&self, user_id: i32) -> Result<User, sqlx::Error> {
|
|
// Check Redis cache first
|
|
if let Ok(Some(cached_user)) = self.cache.get::<User>(&format!("user:{}", user_id)).await {
|
|
return Ok(cached_user);
|
|
}
|
|
|
|
// Fetch from PostgreSQL
|
|
let row = sqlx::query!(
|
|
"SELECT id, username, email, hashed_password, roles FROM users WHERE id = $1",
|
|
user_id
|
|
)
|
|
.fetch_one(&self.pool)
|
|
.await?;
|
|
|
|
let user = User {
|
|
id: row.id,
|
|
username: row.username,
|
|
email: row.email,
|
|
hashed_password: row.hashed_password,
|
|
roles: row.roles.unwrap_or_default(),
|
|
};
|
|
|
|
// Store result in Redis
|
|
self.cache
|
|
.set(&format!("user:{}", user_id), &user, 3600)
|
|
.await
|
|
.unwrap_or_else(|err| eprintln!("Failed to cache user: {:?}", err));
|
|
|
|
Ok(user)
|
|
}
|
|
|
|
pub async fn get_user_by_username(&self, username: &str) -> Result<User, Error> {
|
|
// Check Redis cache first
|
|
if let Ok(Some(cached_user)) = self.cache.get::<User>(&format!("user_by_username:{}", username)).await {
|
|
return Ok(cached_user);
|
|
}
|
|
|
|
// Fetch from PostgreSQL
|
|
let row = sqlx::query!(
|
|
"SELECT id, username, email, hashed_password, roles FROM users WHERE username = $1",
|
|
username
|
|
)
|
|
.fetch_one(&self.pool)
|
|
.await?;
|
|
|
|
let user = User {
|
|
id: row.id,
|
|
username: row.username,
|
|
email: row.email,
|
|
hashed_password: row.hashed_password,
|
|
roles: row.roles.unwrap_or_default(),
|
|
};
|
|
|
|
// Store result in Redis
|
|
self.cache
|
|
.set(&format!("user_by_username:{}", username), &user, 3600)
|
|
.await
|
|
.unwrap_or_else(|err| eprintln!("Failed to cache user: {:?}", err));
|
|
|
|
Ok(user)
|
|
}
|
|
|
|
pub async fn get_user_by_email(&self, email: &str) -> Result<User, Error> {
|
|
// Check Redis cache first
|
|
if let Ok(Some(cached_user)) = self.cache.get::<User>(&format!("user_by_email:{}", email)).await {
|
|
return Ok(cached_user);
|
|
}
|
|
|
|
// Fetch from PostgreSQL
|
|
let row = sqlx::query!(
|
|
"SELECT id, username, email, hashed_password, roles FROM users WHERE email = $1",
|
|
email
|
|
)
|
|
.fetch_one(&self.pool)
|
|
.await?;
|
|
|
|
let user = User {
|
|
id: row.id,
|
|
username: row.username,
|
|
email: row.email,
|
|
hashed_password: row.hashed_password,
|
|
roles: row.roles.unwrap_or_default(),
|
|
};
|
|
|
|
// Store result in Redis
|
|
self.cache
|
|
.set(&format!("user_by_email:{}", email), &user, 3600)
|
|
.await
|
|
.unwrap_or_else(|err| eprintln!("Failed to cache user: {:?}", err));
|
|
|
|
Ok(user)
|
|
}
|
|
|
|
pub async fn update_user_email(&self, user_id: i32, new_email: &str) -> Result<(), Error> {
|
|
sqlx::query!(
|
|
r#"
|
|
UPDATE users
|
|
SET email = $1, updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = $2
|
|
"#,
|
|
new_email,
|
|
user_id
|
|
)
|
|
.execute(&self.pool)
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn delete_user(&self, user_id: i32) -> Result<(), Error> {
|
|
sqlx::query!(
|
|
r#"
|
|
DELETE FROM users
|
|
WHERE id = $1
|
|
"#,
|
|
user_id
|
|
)
|
|
.execute(&self.pool)
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|
|
}
|