use std::sync::Arc; use async_trait::async_trait; use libertas_core::{ error::{CoreError, CoreResult}, models::{Role, User}, repositories::UserRepository, schema::{CreateUserData, LoginUserData}, services::UserService, }; use uuid::Uuid; use crate::security::{PasswordHasher, TokenGenerator}; pub struct UserServiceImpl { repo: Arc, hasher: Arc, tokenizer: Arc, } impl UserServiceImpl { pub fn new( repo: Arc, hasher: Arc, tokenizer: Arc, ) -> Self { Self { repo, hasher, tokenizer, } } } #[async_trait] impl UserService for UserServiceImpl { async fn register(&self, data: CreateUserData<'_>) -> CoreResult { if data.username.is_empty() || data.email.is_empty() || data.password.is_empty() { return Err(CoreError::Validation( "Username, email, and password cannot be empty".to_string(), )); } if self.repo.find_by_email(data.email).await?.is_some() { return Err(CoreError::Duplicate("Email already exists".to_string())); } if self.repo.find_by_username(data.username).await?.is_some() { return Err(CoreError::Duplicate("Username already exists".to_string())); } let hashed_password = self.hasher.hash_password(data.password).await?; let user = User { id: Uuid::new_v4(), username: data.username.to_string(), email: data.email.to_string(), hashed_password, created_at: chrono::Utc::now(), updated_at: chrono::Utc::now(), role: Role::User, storage_quota: 10 * 1024 * 1024 * 1024, // 10 GB storage_used: 0, }; self.repo.create(user.clone()).await?; Ok(user) } async fn login(&self, data: LoginUserData<'_>) -> CoreResult { let user = self .repo .find_by_email(data.username_or_email) // Allow login with email .await? .or(self.repo.find_by_username(data.username_or_email).await?) // Or username .ok_or(CoreError::Auth("Invalid credentials".to_string()))?; self.hasher .verify_password(data.password, &user.hashed_password) .await?; // 3. Generate JWT token self.tokenizer.generate_token(user.id) } async fn get_user_details(&self, user_id: Uuid) -> CoreResult { self.repo .find_by_id(user_id) .await? .ok_or(CoreError::NotFound("User".to_string(), user_id)) } }