refactor: migrate to clean architecture with trait-based DI (v0.2.0)
Complete architectural overhaul across all 12 crates: - Replace validator crate with zod-rs for all DTO validation - Replace manual pagination with paginator-rs/paginator-sea-orm - Migrate all modules (iam, cms, gacha, dimentorin) to clean architecture: domain → application → infrastructure layers - Introduce trait-based DI (Arc<dyn Trait>) at every layer for repositories and services - Delete all v1/ legacy SurrealDB-era code across every crate - Replace opaque response helpers with typed IntoResponse structs (ApiSuccess, ApiCreated, ApiPaginated, ApiMessage) - Remove dual_mode_repository, migration_validation_errors, validator.rs dead code - Zero cargo clippy warnings; release build clean Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
1b3366d735
commit
e432a1a743
@@ -4,6 +4,7 @@
|
||||
//! with PostgreSQL database connections and comprehensive error handling.
|
||||
|
||||
pub mod validated_json;
|
||||
pub mod zod_validate;
|
||||
|
||||
use axum::{Router, serve};
|
||||
use std::{future::Future, net::SocketAddr};
|
||||
@@ -14,6 +15,7 @@ use sea_orm::DbErr;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub use validated_json::ValidatedJson;
|
||||
pub use zod_validate::ZodValidate;
|
||||
|
||||
/// PostgreSQL database clients for different connection types
|
||||
pub struct PostgresClients {
|
||||
|
||||
@@ -1,114 +1,58 @@
|
||||
//! Custom extractor for automatic JSON validation and sanitization
|
||||
//!
|
||||
//! This extractor automatically validates request payloads using the validator crate
|
||||
//! and returns appropriate error responses if validation fails.
|
||||
|
||||
use axum::{
|
||||
extract::{rejection::JsonRejection, FromRequest, Request},
|
||||
http::StatusCode,
|
||||
response::{IntoResponse, Response},
|
||||
Json,
|
||||
};
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde_json;
|
||||
use validator::Validate;
|
||||
|
||||
/// Custom extractor that automatically validates JSON payloads
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// use imphnen_libs::axum::ValidatedJson;
|
||||
/// use axum::response::Response;
|
||||
/// use axum::body::Body;
|
||||
/// use serde::Deserialize;
|
||||
/// use validator::Validate;
|
||||
///
|
||||
/// #[derive(Deserialize, Validate)]
|
||||
/// struct CreateUserRequest {
|
||||
/// #[validate(email)]
|
||||
/// email: String,
|
||||
/// #[validate(length(min = 8))]
|
||||
/// password: String,
|
||||
/// }
|
||||
///
|
||||
/// async fn create_user(
|
||||
/// ValidatedJson(payload): ValidatedJson<CreateUserRequest>
|
||||
/// ) -> Response {
|
||||
/// // payload is already validated
|
||||
/// // ... your logic here
|
||||
/// Response::new(Body::from("ok"))
|
||||
/// }
|
||||
/// ```
|
||||
pub struct ValidatedJson<T>(pub T);
|
||||
|
||||
impl<T, S> FromRequest<S> for ValidatedJson<T>
|
||||
where
|
||||
T: DeserializeOwned + Validate + 'static,
|
||||
S: Send + Sync,
|
||||
Json<T>: FromRequest<S, Rejection = JsonRejection>,
|
||||
{
|
||||
type Rejection = Response;
|
||||
|
||||
async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
|
||||
// First, extract JSON
|
||||
let Json(value) = match Json::<T>::from_request(req, state).await {
|
||||
Ok(value) => value,
|
||||
Err(rejection) => {
|
||||
let error_message = format!("Invalid JSON payload: {rejection}");
|
||||
return Err((
|
||||
StatusCode::BAD_REQUEST,
|
||||
Json(serde_json::json!({
|
||||
"error": error_message,
|
||||
"version": env!("CARGO_PKG_VERSION"),
|
||||
})),
|
||||
)
|
||||
.into_response());
|
||||
}
|
||||
};
|
||||
|
||||
// Then, validate it
|
||||
if let Err(errors) = value.validate() {
|
||||
let error_messages: Vec<String> = errors
|
||||
.field_errors()
|
||||
.iter()
|
||||
.flat_map(|(field, errors)| {
|
||||
errors.iter().map(move |error| {
|
||||
format!(
|
||||
"{}: {}",
|
||||
field,
|
||||
error.message.as_ref().map(|m| m.to_string()).unwrap_or_else(|| error.code.to_string())
|
||||
)
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
return Err((
|
||||
StatusCode::BAD_REQUEST,
|
||||
Json(serde_json::json!({
|
||||
"error": "Validation failed",
|
||||
"details": error_messages,
|
||||
"version": env!("CARGO_PKG_VERSION"),
|
||||
})),
|
||||
)
|
||||
.into_response());
|
||||
}
|
||||
|
||||
Ok(ValidatedJson(value))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug, Deserialize, Validate)]
|
||||
struct TestPayload {
|
||||
#[validate(email)]
|
||||
email: String,
|
||||
#[validate(length(min = 8))]
|
||||
password: String,
|
||||
}
|
||||
|
||||
// Note: Full integration tests should be done at the application level
|
||||
}
|
||||
use axum::{
|
||||
body::Bytes,
|
||||
extract::{FromRequest, Request},
|
||||
http::StatusCode,
|
||||
response::{IntoResponse, Response},
|
||||
Json,
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
use super::zod_validate::ZodValidate;
|
||||
|
||||
pub struct ValidatedJson<T>(pub T);
|
||||
|
||||
impl<T, S> FromRequest<S> for ValidatedJson<T>
|
||||
where
|
||||
T: ZodValidate + 'static,
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = Response;
|
||||
|
||||
async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
|
||||
let bytes = Bytes::from_request(req, state).await.map_err(|e| {
|
||||
(
|
||||
StatusCode::BAD_REQUEST,
|
||||
Json(json!({
|
||||
"message": format!("Failed to read body: {e}"),
|
||||
"version": env!("CARGO_PKG_VERSION"),
|
||||
})),
|
||||
)
|
||||
.into_response()
|
||||
})?;
|
||||
|
||||
let json_value: serde_json::Value =
|
||||
serde_json::from_slice(&bytes).map_err(|e| {
|
||||
(
|
||||
StatusCode::BAD_REQUEST,
|
||||
Json(json!({
|
||||
"message": format!("Invalid JSON: {e}"),
|
||||
"version": env!("CARGO_PKG_VERSION"),
|
||||
})),
|
||||
)
|
||||
.into_response()
|
||||
})?;
|
||||
|
||||
let value = T::zod_validate(&json_value).map_err(|e| {
|
||||
(
|
||||
StatusCode::BAD_REQUEST,
|
||||
Json(json!({
|
||||
"message": format!("Validation error: {e}"),
|
||||
"version": env!("CARGO_PKG_VERSION"),
|
||||
})),
|
||||
)
|
||||
.into_response()
|
||||
})?;
|
||||
|
||||
Ok(ValidatedJson(value))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
use serde_json::Value;
|
||||
|
||||
pub trait ZodValidate: Sized {
|
||||
fn zod_validate(value: &Value) -> Result<Self, String>;
|
||||
}
|
||||
@@ -1,355 +0,0 @@
|
||||
//! Dual-mode repository pattern implementation
|
||||
//! Provides both PostgreSQL and in-memory repository implementations
|
||||
//! with seamless switching between modes for testing and production
|
||||
|
||||
use std::sync::Arc;
|
||||
use async_trait::async_trait;
|
||||
use uuid::Uuid;
|
||||
use chrono::{DateTime, Utc};
|
||||
use thiserror::Error;
|
||||
use sea_orm::{DbErr, ActiveModelTrait, ModelTrait, EntityTrait, QueryFilter, ColumnTrait, PaginatorTrait, QuerySelect, ActiveValue::Set,};
|
||||
|
||||
use crate::postgres::{PostgresConnection, PostgresError};
|
||||
use imphnen_entities::seaorm::auth::users::{Entity as UsersEntity, Model as UserModel};
|
||||
use imphnen_entities::seaorm::auth::roles::{Entity as RolesEntity, Model as RoleModel};
|
||||
|
||||
/// Error types for dual-mode repository operations
|
||||
#[derive(Debug, Error)]
|
||||
pub enum PostgresRepositoryError {
|
||||
#[error("Database error: {0}")]
|
||||
DatabaseError(#[from] DbErr),
|
||||
|
||||
#[error("PostgreSQL connection error: {0}")]
|
||||
ConnectionError(#[from] PostgresError),
|
||||
|
||||
#[error("Entity not found: {0}")]
|
||||
NotFound(String),
|
||||
|
||||
#[error("Validation error: {0}")]
|
||||
ValidationError(String),
|
||||
|
||||
#[error("Conversion error: {0}")]
|
||||
ConversionError(String),
|
||||
|
||||
#[error("Repository operation failed: {0}")]
|
||||
OperationFailed(String),
|
||||
}
|
||||
|
||||
/// Repository trait for user operations
|
||||
#[async_trait]
|
||||
pub trait PostgresRepository {
|
||||
/// Find user by ID
|
||||
async fn find_user_by_id(&self, id: Uuid) -> Result<Option<UserModel>, PostgresRepositoryError>;
|
||||
|
||||
/// Find user by email
|
||||
async fn find_user_by_email(&self, email: &str) -> Result<Option<UserModel>, PostgresRepositoryError>;
|
||||
|
||||
/// Find user by username
|
||||
async fn find_user_by_username(&self, username: &str) -> Result<Option<UserModel>, PostgresRepositoryError>;
|
||||
|
||||
/// Create new user
|
||||
async fn create_user(&self, user: UserModel) -> Result<UserModel, PostgresRepositoryError>;
|
||||
|
||||
/// Update user
|
||||
async fn update_user(&self, id: Uuid, user: UserModel) -> Result<UserModel, PostgresRepositoryError>;
|
||||
|
||||
/// Delete user
|
||||
async fn delete_user(&self, id: Uuid) -> Result<(), PostgresRepositoryError>;
|
||||
|
||||
/// List all users with pagination
|
||||
async fn list_users(&self, offset: u64, limit: u64) -> Result<Vec<UserModel>, PostgresRepositoryError>;
|
||||
|
||||
/// Count total users
|
||||
async fn count_users(&self) -> Result<u64, PostgresRepositoryError>;
|
||||
|
||||
/// Find role by ID
|
||||
async fn find_role_by_id(&self, id: Uuid) -> Result<Option<RoleModel>, PostgresRepositoryError>;
|
||||
|
||||
/// Find role by name
|
||||
async fn find_role_by_name(&self, name: &str) -> Result<Option<RoleModel>, PostgresRepositoryError>;
|
||||
|
||||
/// Create new role
|
||||
async fn create_role(&self, role: RoleModel) -> Result<RoleModel, PostgresRepositoryError>;
|
||||
|
||||
/// Update role
|
||||
async fn update_role(&self, id: Uuid, role: RoleModel) -> Result<RoleModel, PostgresRepositoryError>;
|
||||
|
||||
/// Delete role
|
||||
async fn delete_role(&self, id: Uuid) -> Result<(), PostgresRepositoryError>;
|
||||
|
||||
/// List all roles
|
||||
async fn list_roles(&self) -> Result<Vec<RoleModel>, PostgresRepositoryError>;
|
||||
}
|
||||
|
||||
/// Default PostgreSQL repository implementation
|
||||
pub struct PostgresRepositoryDefaultImpl {
|
||||
connection: Arc<PostgresConnection>,
|
||||
}
|
||||
|
||||
impl PostgresRepositoryDefaultImpl {
|
||||
/// Create a new PostgreSQL repository instance
|
||||
pub fn new(connection: Arc<PostgresConnection>) -> Self {
|
||||
Self { connection }
|
||||
}
|
||||
|
||||
/// Get the underlying PostgreSQL connection
|
||||
pub fn connection(&self) -> &Arc<PostgresConnection> {
|
||||
&self.connection
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl PostgresRepository for PostgresRepositoryDefaultImpl {
|
||||
async fn find_user_by_id(&self, id: Uuid) -> Result<Option<UserModel>, PostgresRepositoryError> {
|
||||
let user = UsersEntity::find_by_id(id)
|
||||
.one(&self.connection.conn)
|
||||
.await
|
||||
.map_err(PostgresRepositoryError::DatabaseError)?;
|
||||
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
async fn find_user_by_email(&self, email: &str) -> Result<Option<UserModel>, PostgresRepositoryError> {
|
||||
let user = UsersEntity::find()
|
||||
.filter(imphnen_entities::seaorm::auth::users::Column::Email.eq(email))
|
||||
.one(&self.connection.conn)
|
||||
.await
|
||||
.map_err(PostgresRepositoryError::DatabaseError)?;
|
||||
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
async fn find_user_by_username(&self, username: &str) -> Result<Option<UserModel>, PostgresRepositoryError> {
|
||||
let user = UsersEntity::find()
|
||||
.filter(imphnen_entities::seaorm::auth::users::Column::Username.eq(username))
|
||||
.one(&self.connection.conn)
|
||||
.await
|
||||
.map_err(PostgresRepositoryError::DatabaseError)?;
|
||||
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
async fn create_user(&self, user: UserModel) -> Result<UserModel, PostgresRepositoryError> {
|
||||
let active_model = imphnen_entities::seaorm::auth::users::ActiveModel {
|
||||
id: Set(user.id),
|
||||
email: Set(user.email),
|
||||
password_hash: Set(user.password_hash),
|
||||
username: Set(user.username),
|
||||
first_name: Set(user.first_name),
|
||||
last_name: Set(user.last_name),
|
||||
avatar_url: Set(user.avatar_url),
|
||||
is_verified: Set(user.is_verified),
|
||||
is_active: Set(user.is_active),
|
||||
metadata: Set(user.metadata),
|
||||
created_at: Set(user.created_at),
|
||||
updated_at: Set(user.updated_at),
|
||||
deleted_at: Set(user.deleted_at), // Use user.deleted_at
|
||||
role_id: Set(user.role_id),
|
||||
};
|
||||
|
||||
let created_user = active_model.insert(&self.connection.conn).await.map_err(PostgresRepositoryError::DatabaseError)?;
|
||||
Ok(created_user)
|
||||
}
|
||||
|
||||
async fn update_user(&self, id: Uuid, user: UserModel) -> Result<UserModel, PostgresRepositoryError> {
|
||||
let existing_user = self.find_user_by_id(id).await?
|
||||
.ok_or_else(|| PostgresRepositoryError::NotFound(format!("User with id {id} not found")))?;
|
||||
|
||||
let mut active_model: imphnen_entities::seaorm::auth::users::ActiveModel = existing_user.into();
|
||||
|
||||
// Update fields
|
||||
active_model.email = Set(user.email);
|
||||
active_model.password_hash = Set(user.password_hash);
|
||||
active_model.username = Set(user.username);
|
||||
active_model.first_name = Set(user.first_name);
|
||||
active_model.last_name = Set(user.last_name);
|
||||
active_model.avatar_url = Set(user.avatar_url);
|
||||
active_model.is_verified = Set(user.is_verified);
|
||||
active_model.is_active = Set(user.is_active);
|
||||
active_model.metadata = Set(user.metadata);
|
||||
active_model.updated_at = Set(user.updated_at);
|
||||
active_model.deleted_at = Set(user.deleted_at);
|
||||
active_model.role_id = Set(user.role_id); // Update role_id
|
||||
|
||||
let result = active_model
|
||||
.update(&self.connection.conn)
|
||||
.await
|
||||
.map_err(PostgresRepositoryError::DatabaseError)?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
async fn delete_user(&self, id: Uuid) -> Result<(), PostgresRepositoryError> {
|
||||
let user = self.find_user_by_id(id).await?
|
||||
.ok_or_else(|| PostgresRepositoryError::NotFound(format!("User with id {id} not found")))?;
|
||||
|
||||
user.delete(&self.connection.conn)
|
||||
.await
|
||||
.map_err(PostgresRepositoryError::DatabaseError)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn list_users(&self, offset: u64, limit: u64) -> Result<Vec<UserModel>, PostgresRepositoryError> {
|
||||
let users = UsersEntity::find()
|
||||
.offset(offset)
|
||||
.limit(limit)
|
||||
.all(&self.connection.conn)
|
||||
.await
|
||||
.map_err(PostgresRepositoryError::DatabaseError)?;
|
||||
|
||||
Ok(users)
|
||||
}
|
||||
|
||||
async fn count_users(&self) -> Result<u64, PostgresRepositoryError> {
|
||||
let count = UsersEntity::find()
|
||||
.count(&self.connection.conn)
|
||||
.await
|
||||
.map_err(PostgresRepositoryError::DatabaseError)?;
|
||||
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
async fn find_role_by_id(&self, id: Uuid) -> Result<Option<RoleModel>, PostgresRepositoryError> {
|
||||
let role = RolesEntity::find_by_id(id)
|
||||
.one(&self.connection.conn)
|
||||
.await
|
||||
.map_err(PostgresRepositoryError::DatabaseError)?;
|
||||
|
||||
Ok(role)
|
||||
}
|
||||
|
||||
async fn find_role_by_name(&self, name: &str) -> Result<Option<RoleModel>, PostgresRepositoryError> {
|
||||
let role = RolesEntity::find()
|
||||
.filter(imphnen_entities::seaorm::auth::roles::Column::Name.eq(name))
|
||||
.one(&self.connection.conn)
|
||||
.await
|
||||
.map_err(PostgresRepositoryError::DatabaseError)?;
|
||||
|
||||
Ok(role)
|
||||
}
|
||||
|
||||
async fn create_role(&self, role: RoleModel) -> Result<RoleModel, PostgresRepositoryError> {
|
||||
let active_model = imphnen_entities::seaorm::auth::roles::ActiveModel {
|
||||
id: Set(role.id),
|
||||
name: Set(role.name),
|
||||
description: Set(role.description),
|
||||
permissions: Set(role.permissions),
|
||||
is_system_role: Set(role.is_system_role),
|
||||
is_default: Set(role.is_default),
|
||||
created_at: Set(role.created_at),
|
||||
updated_at: Set(role.updated_at),
|
||||
deleted_at: Set(role.deleted_at),
|
||||
};
|
||||
|
||||
let result = active_model
|
||||
.insert(&self.connection.conn)
|
||||
.await
|
||||
.map_err(PostgresRepositoryError::DatabaseError)?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
async fn update_role(&self, id: Uuid, role: RoleModel) -> Result<RoleModel, PostgresRepositoryError> {
|
||||
let existing_role = self.find_role_by_id(id).await?
|
||||
.ok_or_else(|| PostgresRepositoryError::NotFound(format!("Role with id {id} not found")))?;
|
||||
|
||||
let mut active_model: imphnen_entities::seaorm::auth::roles::ActiveModel = existing_role.into();
|
||||
|
||||
active_model.name = Set(role.name);
|
||||
active_model.description = Set(role.description);
|
||||
active_model.permissions = Set(role.permissions);
|
||||
active_model.is_system_role = Set(role.is_system_role);
|
||||
active_model.is_default = Set(role.is_default);
|
||||
active_model.updated_at = Set(role.updated_at);
|
||||
active_model.deleted_at = Set(role.deleted_at);
|
||||
|
||||
let result = active_model
|
||||
.update(&self.connection.conn)
|
||||
.await
|
||||
.map_err(PostgresRepositoryError::DatabaseError)?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
async fn delete_role(&self, id: Uuid) -> Result<(), PostgresRepositoryError> {
|
||||
let role = self.find_role_by_id(id).await?
|
||||
.ok_or_else(|| PostgresRepositoryError::NotFound(format!("Role with id {id} not found")))?;
|
||||
|
||||
role.delete(&self.connection.conn)
|
||||
.await
|
||||
.map_err(PostgresRepositoryError::DatabaseError)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn list_roles(&self) -> Result<Vec<RoleModel>, PostgresRepositoryError> {
|
||||
let roles = RolesEntity::find()
|
||||
.all(&self.connection.conn)
|
||||
.await
|
||||
.map_err(PostgresRepositoryError::DatabaseError)?;
|
||||
|
||||
Ok(roles)
|
||||
}
|
||||
}
|
||||
|
||||
/// Conversion utilities for data transformation between different formats
|
||||
pub mod conversion_utils {
|
||||
use super::*;
|
||||
use serde_json::Value;
|
||||
|
||||
/// Convert JSON value to PostgreSQL-compatible format
|
||||
pub fn json_to_pg_json(json: Value) -> Result<serde_json::Value, PostgresRepositoryError> {
|
||||
Ok(json)
|
||||
}
|
||||
|
||||
/// Convert PostgreSQL JSON to standard JSON value
|
||||
pub fn pg_json_to_json(pg_json: serde_json::Value) -> Result<Value, PostgresRepositoryError> {
|
||||
Ok(pg_json)
|
||||
}
|
||||
|
||||
/// Convert string to PostgreSQL UUID format
|
||||
pub fn string_to_uuid(uuid_str: &str) -> Result<Uuid, PostgresRepositoryError> {
|
||||
Uuid::parse_str(uuid_str)
|
||||
.map_err(|e| PostgresRepositoryError::ConversionError(format!("Invalid UUID: {e}")))
|
||||
}
|
||||
|
||||
/// Convert DateTime to PostgreSQL timestamp format
|
||||
pub fn datetime_to_pg_timestamp(dt: DateTime<Utc>) -> String {
|
||||
dt.format("%Y-%m-%d %H:%M:%S%.3f").to_string()
|
||||
}
|
||||
|
||||
/// Convert PostgreSQL timestamp string to DateTime
|
||||
pub fn pg_timestamp_to_datetime(pg_timestamp: &str) -> Result<DateTime<Utc>, PostgresRepositoryError> {
|
||||
DateTime::parse_from_rfc3339(pg_timestamp)
|
||||
.map(|dt| dt.with_timezone(&Utc))
|
||||
.map_err(|e| PostgresRepositoryError::ConversionError(format!("Invalid timestamp: {e}")))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_conversion_utils() {
|
||||
// Test UUID conversion
|
||||
let uuid_str = "550e8400-e29b-41d4-a716-446655440000";
|
||||
let uuid = conversion_utils::string_to_uuid(uuid_str).unwrap();
|
||||
assert_eq!(uuid.to_string(), uuid_str);
|
||||
|
||||
// Test timestamp conversion
|
||||
let now = Utc::now();
|
||||
let pg_timestamp = conversion_utils::datetime_to_pg_timestamp(now);
|
||||
assert!(!pg_timestamp.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_postgres_repository_error() {
|
||||
let error = PostgresRepositoryError::NotFound("Test error".to_string());
|
||||
assert_eq!(error.to_string(), "Entity not found: Test error");
|
||||
|
||||
let error = PostgresRepositoryError::ValidationError("Invalid input".to_string());
|
||||
assert_eq!(error.to_string(), "Validation error: Invalid input");
|
||||
}
|
||||
}
|
||||
+1
-28
@@ -1,21 +1,3 @@
|
||||
/*!
|
||||
# imphnen-libs
|
||||
|
||||
A collection of utility libraries and services for the imphnen project, providing integrations
|
||||
with various external services and common functionality.
|
||||
|
||||
This crate includes modules for:
|
||||
- Password hashing with Argon2 (`argon`)
|
||||
- Axum web framework utilities (`axum`)
|
||||
- Environment configuration (`environment`)
|
||||
- JWT token handling (`jsonwebtoken`)
|
||||
- Email sending with Lettre (`lettre`)
|
||||
- MinIO object storage client (`minio`)
|
||||
- Service abstractions (`services`)
|
||||
- PostgreSQL database client (`postgres`)
|
||||
- Dual-mode repository pattern (`dual_mode_repository`)
|
||||
*/
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
pub mod postgres;
|
||||
@@ -26,21 +8,14 @@ pub mod jsonwebtoken;
|
||||
pub mod lettre;
|
||||
pub mod minio;
|
||||
pub mod services;
|
||||
pub mod dual_mode_repository;
|
||||
|
||||
pub use argon::{hash_password, verify_password};
|
||||
pub use axum::{axum_init, ValidatedJson};
|
||||
pub use axum::{axum_init, ValidatedJson, ZodValidate};
|
||||
pub use environment::{ENV, Env};
|
||||
pub use imphnen_entities::{
|
||||
MessageResponseDto,
|
||||
MetaRequestDto,
|
||||
MetaResponseDto,
|
||||
ResponseSuccessDto,
|
||||
ResponseListSuccessDto,
|
||||
CountResult,
|
||||
Error,
|
||||
ExperienceDto,
|
||||
EducationDto,
|
||||
UsersDetailQueryDto,
|
||||
PermissionsEnum,
|
||||
PermissionsItemDto,
|
||||
@@ -62,8 +37,6 @@ pub use services::PostgresAuthRepository;
|
||||
pub use postgres::{
|
||||
PostgresConnection, PostgresConfig, PostgresError, AppStatePostgresExt,
|
||||
};
|
||||
pub use dual_mode_repository::{PostgresRepository, PostgresRepositoryDefaultImpl, conversion_utils, PostgresRepositoryError};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AppState {
|
||||
pub postgres_connection: Arc<PostgresConnection>,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
//! Service abstractions for the application
|
||||
//! Provides traits and implementations for user lookup and authentication services
|
||||
//! with PostgreSQL integration and comprehensive error handling
|
||||
#![allow(clippy::field_reassign_with_default)]
|
||||
|
||||
use crate::{postgres::PostgresError, AppState};
|
||||
use async_trait::async_trait;
|
||||
@@ -51,6 +50,7 @@ pub enum ServiceError {
|
||||
|
||||
/// User reference types for different identification methods
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum UserReference {
|
||||
/// User ID (UUID)
|
||||
Id(Uuid),
|
||||
|
||||
Reference in New Issue
Block a user