2025-03-20 09:22:31 +01:00
|
|
|
use uuid::Uuid;
|
|
|
|
|
use chrono::{DateTime, Utc};
|
|
|
|
|
|
2026-02-06 20:57:00 +01:00
|
|
|
// Re-exportar errores de entidad desde el módulo centralizado
|
|
|
|
|
pub use super::entity_errors::{UserError, UserResult};
|
2025-03-20 09:22:31 +01:00
|
|
|
|
2026-02-02 23:56:40 +01:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
2025-03-24 16:47:42 +01:00
|
|
|
// We'll handle conversion manually for now until the type is properly set up in the database
|
2025-03-20 09:22:31 +01:00
|
|
|
pub enum UserRole {
|
|
|
|
|
Admin,
|
|
|
|
|
User,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl std::fmt::Display for UserRole {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
|
|
|
match self {
|
|
|
|
|
UserRole::Admin => write!(f, "admin"),
|
|
|
|
|
UserRole::User => write!(f, "user"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-02 23:56:40 +01:00
|
|
|
#[derive(Debug, Clone)]
|
2025-03-20 09:22:31 +01:00
|
|
|
pub struct User {
|
|
|
|
|
id: String,
|
|
|
|
|
username: String,
|
|
|
|
|
email: String,
|
|
|
|
|
password_hash: String,
|
|
|
|
|
role: UserRole,
|
|
|
|
|
storage_quota_bytes: i64,
|
|
|
|
|
storage_used_bytes: i64,
|
|
|
|
|
created_at: DateTime<Utc>,
|
|
|
|
|
updated_at: DateTime<Utc>,
|
|
|
|
|
last_login_at: Option<DateTime<Utc>>,
|
|
|
|
|
active: bool,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl User {
|
2026-02-02 23:56:40 +01:00
|
|
|
/// Create a new user with a pre-hashed password.
|
|
|
|
|
///
|
|
|
|
|
/// The password hashing should be done externally using PasswordHasherPort
|
|
|
|
|
/// to maintain clean architecture and keep cryptographic dependencies
|
|
|
|
|
/// out of the domain layer.
|
|
|
|
|
///
|
|
|
|
|
/// # Arguments
|
|
|
|
|
/// * `username` - User's username (3-32 characters)
|
|
|
|
|
/// * `email` - User's email address
|
|
|
|
|
/// * `password_hash` - Pre-hashed password (from PasswordHasherPort)
|
|
|
|
|
/// * `role` - User's role
|
|
|
|
|
/// * `storage_quota_bytes` - Storage quota in bytes
|
2025-03-20 09:22:31 +01:00
|
|
|
pub fn new(
|
|
|
|
|
username: String,
|
|
|
|
|
email: String,
|
2026-02-02 23:56:40 +01:00
|
|
|
password_hash: String,
|
2025-03-20 09:22:31 +01:00
|
|
|
role: UserRole,
|
|
|
|
|
storage_quota_bytes: i64,
|
|
|
|
|
) -> UserResult<Self> {
|
|
|
|
|
// Validaciones
|
|
|
|
|
if username.is_empty() || username.len() < 3 || username.len() > 32 {
|
|
|
|
|
return Err(UserError::InvalidUsername(format!(
|
|
|
|
|
"Username debe tener entre 3 y 32 caracteres"
|
|
|
|
|
)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !email.contains('@') || email.len() < 5 {
|
|
|
|
|
return Err(UserError::ValidationError(format!(
|
|
|
|
|
"Email inválido"
|
|
|
|
|
)));
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-02 23:56:40 +01:00
|
|
|
if password_hash.is_empty() {
|
2025-03-20 09:22:31 +01:00
|
|
|
return Err(UserError::InvalidPassword(format!(
|
2026-02-02 23:56:40 +01:00
|
|
|
"Password hash no puede estar vacío"
|
2025-03-20 09:22:31 +01:00
|
|
|
)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let now = Utc::now();
|
|
|
|
|
|
|
|
|
|
Ok(Self {
|
|
|
|
|
id: Uuid::new_v4().to_string(),
|
|
|
|
|
username,
|
|
|
|
|
email,
|
|
|
|
|
password_hash,
|
|
|
|
|
role,
|
|
|
|
|
storage_quota_bytes,
|
|
|
|
|
storage_used_bytes: 0,
|
|
|
|
|
created_at: now,
|
|
|
|
|
updated_at: now,
|
|
|
|
|
last_login_at: None,
|
|
|
|
|
active: true,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Crear desde valores existentes (para reconstrucción desde BD)
|
|
|
|
|
pub fn from_data(
|
|
|
|
|
id: String,
|
|
|
|
|
username: String,
|
|
|
|
|
email: String,
|
|
|
|
|
password_hash: String,
|
|
|
|
|
role: UserRole,
|
|
|
|
|
storage_quota_bytes: i64,
|
|
|
|
|
storage_used_bytes: i64,
|
|
|
|
|
created_at: DateTime<Utc>,
|
|
|
|
|
updated_at: DateTime<Utc>,
|
|
|
|
|
last_login_at: Option<DateTime<Utc>>,
|
|
|
|
|
active: bool,
|
|
|
|
|
) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
id,
|
|
|
|
|
username,
|
|
|
|
|
email,
|
|
|
|
|
password_hash,
|
|
|
|
|
role,
|
|
|
|
|
storage_quota_bytes,
|
|
|
|
|
storage_used_bytes,
|
|
|
|
|
created_at,
|
|
|
|
|
updated_at,
|
|
|
|
|
last_login_at,
|
|
|
|
|
active,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Getters
|
|
|
|
|
pub fn id(&self) -> &str {
|
|
|
|
|
&self.id
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn username(&self) -> &str {
|
|
|
|
|
&self.username
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn email(&self) -> &str {
|
|
|
|
|
&self.email
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn role(&self) -> UserRole {
|
|
|
|
|
self.role
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn storage_quota_bytes(&self) -> i64 {
|
|
|
|
|
self.storage_quota_bytes
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn storage_used_bytes(&self) -> i64 {
|
|
|
|
|
self.storage_used_bytes
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn created_at(&self) -> DateTime<Utc> {
|
|
|
|
|
self.created_at
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn updated_at(&self) -> DateTime<Utc> {
|
|
|
|
|
self.updated_at
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn last_login_at(&self) -> Option<DateTime<Utc>> {
|
|
|
|
|
self.last_login_at
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn is_active(&self) -> bool {
|
|
|
|
|
self.active
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn password_hash(&self) -> &str {
|
|
|
|
|
&self.password_hash
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-02 23:56:40 +01:00
|
|
|
/// Update the password hash.
|
|
|
|
|
///
|
|
|
|
|
/// The new password should be hashed externally using PasswordHasherPort
|
|
|
|
|
/// before calling this method.
|
|
|
|
|
pub fn update_password_hash(&mut self, new_hash: String) {
|
|
|
|
|
self.password_hash = new_hash;
|
2025-03-20 09:22:31 +01:00
|
|
|
self.updated_at = Utc::now();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Actualizar uso de almacenamiento
|
|
|
|
|
pub fn update_storage_used(&mut self, storage_used_bytes: i64) {
|
|
|
|
|
self.storage_used_bytes = storage_used_bytes;
|
|
|
|
|
self.updated_at = Utc::now();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Registrar login
|
|
|
|
|
pub fn register_login(&mut self) {
|
|
|
|
|
let now = Utc::now();
|
|
|
|
|
self.last_login_at = Some(now);
|
|
|
|
|
self.updated_at = now;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Desactivar usuario
|
|
|
|
|
pub fn deactivate(&mut self) {
|
|
|
|
|
self.active = false;
|
|
|
|
|
self.updated_at = Utc::now();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Activar usuario
|
|
|
|
|
pub fn activate(&mut self) {
|
|
|
|
|
self.active = true;
|
|
|
|
|
self.updated_at = Utc::now();
|
|
|
|
|
}
|
|
|
|
|
}
|