- Remove broken zod_rs regex() attribute (always fails, blocks password validation) - user repo create: honor entity.id instead of silently generating new v4 (broke FK mentor.user_id) - mentor repo create: set id explicitly (was NULL not-null violation)
195 lines
6.4 KiB
Rust
195 lines
6.4 KiB
Rust
#![allow(clippy::field_reassign_with_default)]
|
|
use super::postgres_user_queries::{
|
|
build_role_dto, build_user_dto, query_user_list,
|
|
};
|
|
use crate::users::domain::{UserEntity, UserListItem, UserRepository};
|
|
use async_trait::async_trait;
|
|
use chrono::Utc;
|
|
use imphnen_entities::{
|
|
UsersDetailQueryDto,
|
|
seaorm::auth::roles::Entity as RolesEntity,
|
|
seaorm::auth::users::{
|
|
ActiveModel as UserActiveModel, Column as UserColumn, Entity as UsersEntity,
|
|
},
|
|
};
|
|
use imphnen_utils::AppError;
|
|
use paginator_rs::PaginationParams;
|
|
use paginator_utils::PaginatorResponse;
|
|
use sea_orm::ActiveValue;
|
|
use sea_orm::prelude::*;
|
|
use std::sync::Arc;
|
|
use uuid::Uuid;
|
|
|
|
fn user_detail_to_entity(dto: UsersDetailQueryDto) -> UserEntity {
|
|
UserEntity {
|
|
id: dto.id,
|
|
email: dto.email,
|
|
fullname: dto.fullname,
|
|
legal_name: dto.legal_name,
|
|
password: dto.password,
|
|
avatar: dto.avatar,
|
|
is_active: dto.is_active,
|
|
is_deleted: dto.is_deleted,
|
|
role: dto.role,
|
|
profile_extension: dto.profile_extension,
|
|
created_at: dto.created_at,
|
|
updated_at: dto.updated_at,
|
|
mentor_id: dto.mentor_id,
|
|
}
|
|
}
|
|
|
|
pub struct PostgresUserRepository {
|
|
db: Arc<DatabaseConnection>,
|
|
}
|
|
|
|
impl PostgresUserRepository {
|
|
pub fn new(db: DatabaseConnection) -> Self {
|
|
Self { db: Arc::new(db) }
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl UserRepository for PostgresUserRepository {
|
|
async fn find_all(
|
|
&self,
|
|
params: PaginationParams,
|
|
) -> Result<PaginatorResponse<UserListItem>, AppError> {
|
|
query_user_list(&self.db, params).await
|
|
}
|
|
|
|
async fn find_by_id(&self, id: &str) -> Result<UserEntity, AppError> {
|
|
let user_id = Uuid::parse_str(id)
|
|
.map_err(|_| AppError::BadRequestError("Invalid user ID".into()))?;
|
|
let (user, role) = UsersEntity::find_by_id(user_id)
|
|
.filter(UserColumn::DeletedAt.is_null())
|
|
.find_also_related(RolesEntity)
|
|
.one(self.db.as_ref())
|
|
.await
|
|
.map_err(|e| AppError::InternalServerError(e.to_string()))?
|
|
.ok_or_else(|| AppError::NotFoundError("User not found in database".into()))?;
|
|
Ok(user_detail_to_entity(
|
|
build_user_dto(user, build_role_dto(role)).from_profile_extension(),
|
|
))
|
|
}
|
|
|
|
async fn find_by_email(&self, email: String) -> Result<UserEntity, AppError> {
|
|
let (user, role) = UsersEntity::find()
|
|
.filter(UserColumn::Email.eq(&email))
|
|
.filter(UserColumn::DeletedAt.is_null())
|
|
.find_also_related(RolesEntity)
|
|
.one(self.db.as_ref())
|
|
.await
|
|
.map_err(|e| AppError::InternalServerError(e.to_string()))?
|
|
.ok_or_else(|| AppError::NotFoundError("User not found".into()))?;
|
|
Ok(user_detail_to_entity(
|
|
build_user_dto(user, build_role_dto(role)).from_profile_extension(),
|
|
))
|
|
}
|
|
|
|
async fn create(&self, entity: UserEntity) -> Result<String, AppError> {
|
|
let existing = UsersEntity::find()
|
|
.filter(UserColumn::Email.eq(entity.email.clone()))
|
|
.one(self.db.as_ref())
|
|
.await
|
|
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
|
if existing.is_some() {
|
|
return Err(AppError::ConflictError(
|
|
"User with this email already exists".into(),
|
|
));
|
|
}
|
|
let full_name = entity.fullname.clone();
|
|
let (first_name, last_name) =
|
|
full_name.split_once(' ').unwrap_or((&full_name, ""));
|
|
let role_id = entity
|
|
.role
|
|
.id
|
|
.parse::<Uuid>()
|
|
.ok()
|
|
.or_else(|| entity.role.id.is_empty().then_some(Uuid::nil()));
|
|
let active_model = UserActiveModel {
|
|
id: ActiveValue::Set(
|
|
Uuid::parse_str(&entity.id).unwrap_or_else(|_| Uuid::new_v4()),
|
|
),
|
|
email: ActiveValue::Set(entity.email.clone()),
|
|
password_hash: ActiveValue::Set(entity.password),
|
|
username: ActiveValue::Set(entity.email.clone()),
|
|
first_name: ActiveValue::Set(Some(first_name.to_string())),
|
|
last_name: ActiveValue::Set(Some(last_name.to_string())),
|
|
avatar_url: ActiveValue::Set(entity.avatar),
|
|
is_verified: ActiveValue::Set(false),
|
|
is_active: ActiveValue::Set(entity.is_active),
|
|
metadata: ActiveValue::Set(
|
|
entity
|
|
.profile_extension
|
|
.map(|p| serde_json::to_value(p).unwrap_or_default()),
|
|
),
|
|
created_at: ActiveValue::Set(Utc::now()),
|
|
updated_at: ActiveValue::Set(Utc::now()),
|
|
deleted_at: ActiveValue::Set(None),
|
|
role_id: ActiveValue::Set(role_id.filter(|id| !id.is_nil())),
|
|
};
|
|
UsersEntity::insert(active_model)
|
|
.exec(self.db.as_ref())
|
|
.await
|
|
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
|
Ok("Successfully created user".into())
|
|
}
|
|
|
|
async fn update(&self, entity: UserEntity) -> Result<String, AppError> {
|
|
let user_id = Uuid::parse_str(&entity.id)
|
|
.map_err(|_| AppError::BadRequestError("Invalid user ID".into()))?;
|
|
let mut active_model: UserActiveModel = UsersEntity::find_by_id(user_id)
|
|
.one(self.db.as_ref())
|
|
.await
|
|
.map_err(|e| AppError::InternalServerError(e.to_string()))?
|
|
.ok_or_else(|| AppError::NotFoundError("User not found".into()))?
|
|
.into();
|
|
let full_name = entity.fullname.clone();
|
|
let (first_name, last_name) =
|
|
full_name.split_once(' ').unwrap_or((&full_name, ""));
|
|
active_model.email = ActiveValue::Set(entity.email);
|
|
active_model.first_name = ActiveValue::Set(Some(first_name.to_string()));
|
|
active_model.last_name = ActiveValue::Set(Some(last_name.to_string()));
|
|
active_model.avatar_url = ActiveValue::Set(entity.avatar);
|
|
active_model.is_active = ActiveValue::Set(entity.is_active);
|
|
active_model.updated_at = ActiveValue::Set(Utc::now());
|
|
if !entity.password.is_empty() {
|
|
active_model.password_hash = ActiveValue::Set(entity.password);
|
|
}
|
|
let role_id = entity.role.id.parse::<Uuid>().ok();
|
|
if role_id.is_some() {
|
|
active_model.role_id = ActiveValue::Set(role_id);
|
|
}
|
|
if entity.profile_extension.is_some() {
|
|
active_model.metadata = ActiveValue::Set(
|
|
entity
|
|
.profile_extension
|
|
.map(|p| serde_json::to_value(p).unwrap_or_default()),
|
|
);
|
|
}
|
|
active_model
|
|
.update(self.db.as_ref())
|
|
.await
|
|
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
|
Ok("Success update user".into())
|
|
}
|
|
|
|
async fn delete(&self, id: String) -> Result<String, AppError> {
|
|
let user_id = Uuid::parse_str(&id)
|
|
.map_err(|_| AppError::BadRequestError("Invalid user ID".into()))?;
|
|
let mut active_model: UserActiveModel = UsersEntity::find_by_id(user_id)
|
|
.one(self.db.as_ref())
|
|
.await
|
|
.map_err(|e| AppError::InternalServerError(e.to_string()))?
|
|
.ok_or_else(|| AppError::NotFoundError("User not found".into()))?
|
|
.into();
|
|
active_model.deleted_at = ActiveValue::Set(Some(Utc::now()));
|
|
active_model.updated_at = ActiveValue::Set(Utc::now());
|
|
active_model
|
|
.update(self.db.as_ref())
|
|
.await
|
|
.map_err(|e| AppError::InternalServerError(e.to_string()))?;
|
|
Ok("Success delete user".into())
|
|
}
|
|
}
|